Implement auto-creation of ICS logon file
[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 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 #if defined(_winmajor)\r
226 #define oldDialog (_winmajor < 4)\r
227 #else\r
228 #define oldDialog 0\r
229 #endif\r
230 #endif\r
231 \r
232 #define INTERNATIONAL\r
233 \r
234 #ifdef INTERNATIONAL\r
235 #  define _(s) T_(s)\r
236 #  define N_(s) s\r
237 #else\r
238 #  define _(s) s\r
239 #  define N_(s) s\r
240 #  define T_(s) s\r
241 #  define Translate(x, y)\r
242 #  define LoadLanguageFile(s)\r
243 #endif\r
244 \r
245 #ifdef INTERNATIONAL\r
246 \r
247 Boolean barbaric; // flag indicating if translation is needed\r
248 \r
249 // list of item numbers used in each dialog (used to alter language at run time)\r
250 \r
251 #define ABOUTBOX -1  /* not sure why these are needed */\r
252 #define ABOUTBOX2 -1\r
253 \r
254 int dialogItems[][42] = {\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
257   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
259   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
261   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
264   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
267   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
268 { ABOUTBOX2, IDC_ChessBoard }, \r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
270   OPT_GameListClose, IDC_GameListDoFilter }, \r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
272 { DLG_Error, IDOK }, \r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
274   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
277   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
278   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
279 { DLG_IndexNumber, IDC_Index }, \r
280 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
281 { DLG_TypeInName, IDOK, IDCANCEL }, \r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
283   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
285   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
286   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
287   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
288   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
289   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
290   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
292   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
293   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
294   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
295   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
296   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
297   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
298   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
299   GPB_General, GPB_Alarm }, \r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
301   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
302   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
303   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
304   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
305   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
306   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
307   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
309   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
310   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
311   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
312   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
313   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
314   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
315   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
316   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
318   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
319   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
320   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
321   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
324   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
325 { DLG_MoveHistory }, \r
326 { DLG_EvalGraph }, \r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
330   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
331   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
332   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
334   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
335   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
336 { 0 }\r
337 };\r
338 \r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
340 static int lastChecked;\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
342 extern int tinyLayout;\r
343 extern char * menuBarText[][10];\r
344 \r
345 void\r
346 LoadLanguageFile(char *name)\r
347 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
348     FILE *f;\r
349     int i=0, j=0, n=0, k;\r
350     char buf[MSG_SIZ];\r
351 \r
352     if(!name || name[0] == NULLCHAR) return;\r
353       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
354     appData.language = oldLanguage;\r
355     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
356     if((f = fopen(buf, "r")) == NULL) return;\r
357     while((k = fgetc(f)) != EOF) {\r
358         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
359         languageBuf[i] = k;\r
360         if(k == '\n') {\r
361             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
362                 char *p;\r
363                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
364                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
365                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
366                         english[j] = languageBuf + n + 1; *p = 0;\r
367                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
369                     }\r
370                 }\r
371             }\r
372             n = i + 1;\r
373         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
374             switch(k) {\r
375               case 'n': k = '\n'; break;\r
376               case 'r': k = '\r'; break;\r
377               case 't': k = '\t'; break;\r
378             }\r
379             languageBuf[--i] = k;\r
380         }\r
381         i++;\r
382     }\r
383     fclose(f);\r
384     barbaric = (j != 0);\r
385     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
386 }\r
387 \r
388 char *\r
389 T_(char *s)\r
390 {   // return the translation of the given string\r
391     // efficiency can be improved a lot...\r
392     int i=0;\r
393     static char buf[MSG_SIZ];\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
395     if(!barbaric) return s;\r
396     if(!s) return ""; // sanity\r
397     while(english[i]) {\r
398         if(!strcmp(s, english[i])) return foreign[i];\r
399         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
400             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
401             return buf;\r
402         }\r
403         i++;\r
404     }\r
405     return s;\r
406 }\r
407 \r
408 void\r
409 Translate(HWND hDlg, int dialogID)\r
410 {   // translate all text items in the given dialog\r
411     int i=0, j, k;\r
412     char buf[MSG_SIZ], *s;\r
413     if(!barbaric) return;\r
414     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
415     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
416     GetWindowText( hDlg, buf, MSG_SIZ );\r
417     s = T_(buf);\r
418     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
419     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
420         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
421         if(strlen(buf) == 0) continue;\r
422         s = T_(buf);\r
423         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
424     }\r
425 }\r
426 \r
427 HMENU\r
428 TranslateOneMenu(int i, HMENU subMenu)\r
429 {\r
430     int j;\r
431     static MENUITEMINFO info;\r
432 \r
433     info.cbSize = sizeof(MENUITEMINFO);\r
434     info.fMask = MIIM_STATE | MIIM_TYPE;\r
435           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
436             char buf[MSG_SIZ];\r
437             info.dwTypeData = buf;\r
438             info.cch = sizeof(buf);\r
439             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
440             if(i < 10) {\r
441                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
442                 else menuText[i][j] = strdup(buf); // remember original on first change\r
443             }\r
444             if(buf[0] == NULLCHAR) continue;\r
445             info.dwTypeData = T_(buf);\r
446             info.cch = strlen(buf)+1;\r
447             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
448           }\r
449     return subMenu;\r
450 }\r
451 \r
452 void\r
453 TranslateMenus(int addLanguage)\r
454 {\r
455     int i;\r
456     WIN32_FIND_DATA fileData;\r
457     HANDLE hFind;\r
458 #define IDM_English 1970\r
459     if(1) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
462           HMENU subMenu = GetSubMenu(mainMenu, i);\r
463           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
464                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
465           TranslateOneMenu(i, subMenu);\r
466         }\r
467         DrawMenuBar(hwndMain);\r
468     }\r
469 \r
470     if(!addLanguage) return;\r
471     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
472         HMENU mainMenu = GetMenu(hwndMain);\r
473         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
474         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
475         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
476         i = 0; lastChecked = IDM_English;\r
477         do {\r
478             char *p, *q = fileData.cFileName;\r
479             int checkFlag = MF_UNCHECKED;\r
480             languageFile[i] = strdup(q);\r
481             if(barbaric && !strcmp(oldLanguage, q)) {\r
482                 checkFlag = MF_CHECKED;\r
483                 lastChecked = IDM_English + i + 1;\r
484                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
485             }\r
486             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
487             p = strstr(fileData.cFileName, ".lng");\r
488             if(p) *p = 0;\r
489             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
490         } while(FindNextFile(hFind, &fileData));\r
491         FindClose(hFind);\r
492     }\r
493 }\r
494 \r
495 #endif\r
496 \r
497 #define IDM_RecentEngines 3000\r
498 \r
499 void\r
500 RecentEngineMenu (char *s)\r
501 {\r
502     if(appData.icsActive) return;\r
503     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
504         HMENU mainMenu = GetMenu(hwndMain);\r
505         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
506         int i=IDM_RecentEngines;\r
507         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
508         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
509         while(*s) {\r
510           char *p = strchr(s, '\n');\r
511           if(p == NULL) return; // malformed!\r
512           *p = NULLCHAR;\r
513           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
514           *p = '\n';\r
515           s = p+1;\r
516         }\r
517     }\r
518 }\r
519 \r
520 \r
521 typedef struct {\r
522   char *name;\r
523   int squareSize;\r
524   int lineGap;\r
525   int smallLayout;\r
526   int tinyLayout;\r
527   int cliWidth, cliHeight;\r
528 } SizeInfo;\r
529 \r
530 SizeInfo sizeInfo[] = \r
531 {\r
532   { "tiny",     21, 0, 1, 1, 0, 0 },\r
533   { "teeny",    25, 1, 1, 1, 0, 0 },\r
534   { "dinky",    29, 1, 1, 1, 0, 0 },\r
535   { "petite",   33, 1, 1, 1, 0, 0 },\r
536   { "slim",     37, 2, 1, 0, 0, 0 },\r
537   { "small",    40, 2, 1, 0, 0, 0 },\r
538   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
539   { "middling", 49, 2, 0, 0, 0, 0 },\r
540   { "average",  54, 2, 0, 0, 0, 0 },\r
541   { "moderate", 58, 3, 0, 0, 0, 0 },\r
542   { "medium",   64, 3, 0, 0, 0, 0 },\r
543   { "bulky",    72, 3, 0, 0, 0, 0 },\r
544   { "large",    80, 3, 0, 0, 0, 0 },\r
545   { "big",      87, 3, 0, 0, 0, 0 },\r
546   { "huge",     95, 3, 0, 0, 0, 0 },\r
547   { "giant",    108, 3, 0, 0, 0, 0 },\r
548   { "colossal", 116, 4, 0, 0, 0, 0 },\r
549   { "titanic",  129, 4, 0, 0, 0, 0 },\r
550   { NULL, 0, 0, 0, 0, 0, 0 }\r
551 };\r
552 \r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
555 {\r
556   { 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
557   { 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
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574 };\r
575 \r
576 MyFont *font[NUM_SIZES][NUM_FONTS];\r
577 \r
578 typedef struct {\r
579   char *label;\r
580   int id;\r
581   HWND hwnd;\r
582   WNDPROC wndproc;\r
583 } MyButtonDesc;\r
584 \r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
586 #define N_BUTTONS 5\r
587 \r
588 MyButtonDesc buttonDesc[N_BUTTONS] =\r
589 {\r
590   {"<<", IDM_ToStart, NULL, NULL},\r
591   {"<", IDM_Backward, NULL, NULL},\r
592   {"P", IDM_Pause, NULL, NULL},\r
593   {">", IDM_Forward, NULL, NULL},\r
594   {">>", IDM_ToEnd, NULL, NULL},\r
595 };\r
596 \r
597 int tinyLayout = 0, smallLayout = 0;\r
598 #define MENU_BAR_ITEMS 9\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
600   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
601   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
602 };\r
603 \r
604 \r
605 MySound sounds[(int)NSoundClasses];\r
606 MyTextAttribs textAttribs[(int)NColorClasses];\r
607 \r
608 MyColorizeAttribs colorizeAttribs[] = {\r
609   { (COLORREF)0, 0, N_("Shout Text") },\r
610   { (COLORREF)0, 0, N_("SShout/CShout") },\r
611   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
612   { (COLORREF)0, 0, N_("Channel Text") },\r
613   { (COLORREF)0, 0, N_("Kibitz Text") },\r
614   { (COLORREF)0, 0, N_("Tell Text") },\r
615   { (COLORREF)0, 0, N_("Challenge Text") },\r
616   { (COLORREF)0, 0, N_("Request Text") },\r
617   { (COLORREF)0, 0, N_("Seek Text") },\r
618   { (COLORREF)0, 0, N_("Normal Text") },\r
619   { (COLORREF)0, 0, N_("None") }\r
620 };\r
621 \r
622 \r
623 \r
624 static char *commentTitle;\r
625 static char *commentText;\r
626 static int commentIndex;\r
627 static Boolean editComment = FALSE;\r
628 \r
629 \r
630 char errorTitle[MSG_SIZ];\r
631 char errorMessage[2*MSG_SIZ];\r
632 HWND errorDialog = NULL;\r
633 BOOLEAN moveErrorMessageUp = FALSE;\r
634 BOOLEAN consoleEcho = TRUE;\r
635 CHARFORMAT consoleCF;\r
636 COLORREF consoleBackgroundColor;\r
637 \r
638 char *programVersion;\r
639 \r
640 #define CPReal 1\r
641 #define CPComm 2\r
642 #define CPSock 3\r
643 #define CPRcmd 4\r
644 typedef int CPKind;\r
645 \r
646 typedef struct {\r
647   CPKind kind;\r
648   HANDLE hProcess;\r
649   DWORD pid;\r
650   HANDLE hTo;\r
651   HANDLE hFrom;\r
652   SOCKET sock;\r
653   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
654 } ChildProc;\r
655 \r
656 #define INPUT_SOURCE_BUF_SIZE 4096\r
657 \r
658 typedef struct _InputSource {\r
659   CPKind kind;\r
660   HANDLE hFile;\r
661   SOCKET sock;\r
662   int lineByLine;\r
663   HANDLE hThread;\r
664   DWORD id;\r
665   char buf[INPUT_SOURCE_BUF_SIZE];\r
666   char *next;\r
667   DWORD count;\r
668   int error;\r
669   InputCallback func;\r
670   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
671   VOIDSTAR closure;\r
672 } InputSource;\r
673 \r
674 InputSource *consoleInputSource;\r
675 \r
676 DCB dcb;\r
677 \r
678 /* forward */\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
680 VOID ConsoleCreate();\r
681 LRESULT CALLBACK\r
682   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);\r
686 LRESULT CALLBACK\r
687   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
689 void ParseIcsTextMenu(char *icsTextMenuString);\r
690 VOID PopUpNameDialog(char firstchar);\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
692 \r
693 /* [AS] */\r
694 int NewGameFRC();\r
695 int GameListOptions();\r
696 \r
697 int dummy; // [HGM] for obsolete args\r
698 \r
699 HWND hwndMain = NULL;        /* root window*/\r
700 HWND hwndConsole = NULL;\r
701 HWND commentDialog = NULL;\r
702 HWND moveHistoryDialog = NULL;\r
703 HWND evalGraphDialog = NULL;\r
704 HWND engineOutputDialog = NULL;\r
705 HWND gameListDialog = NULL;\r
706 HWND editTagsDialog = NULL;\r
707 \r
708 int commentUp = FALSE;\r
709 \r
710 WindowPlacement wpMain;\r
711 WindowPlacement wpConsole;\r
712 WindowPlacement wpComment;\r
713 WindowPlacement wpMoveHistory;\r
714 WindowPlacement wpEvalGraph;\r
715 WindowPlacement wpEngineOutput;\r
716 WindowPlacement wpGameList;\r
717 WindowPlacement wpTags;\r
718 \r
719 VOID EngineOptionsPopup(); // [HGM] settings\r
720 \r
721 VOID GothicPopUp(char *title, VariantClass variant);\r
722 /*\r
723  * Setting "frozen" should disable all user input other than deleting\r
724  * the window.  We do this while engines are initializing themselves.\r
725  */\r
726 static int frozen = 0;\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];\r
728 void FreezeUI()\r
729 {\r
730   HMENU hmenu;\r
731   int i;\r
732 \r
733   if (frozen) return;\r
734   frozen = 1;\r
735   hmenu = GetMenu(hwndMain);\r
736   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
737     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
738   }\r
739   DrawMenuBar(hwndMain);\r
740 }\r
741 \r
742 /* Undo a FreezeUI */\r
743 void ThawUI()\r
744 {\r
745   HMENU hmenu;\r
746   int i;\r
747 \r
748   if (!frozen) return;\r
749   frozen = 0;\r
750   hmenu = GetMenu(hwndMain);\r
751   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
752     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
753   }\r
754   DrawMenuBar(hwndMain);\r
755 }\r
756 \r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
758 \r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
760 #ifdef JAWS\r
761 #include "jaws.c"\r
762 #else\r
763 #define JAWS_INIT\r
764 #define JAWS_ARGS\r
765 #define JAWS_ALT_INTERCEPT\r
766 #define JAWS_KBUP_NAVIGATION\r
767 #define JAWS_KBDOWN_NAVIGATION\r
768 #define JAWS_MENU_ITEMS\r
769 #define JAWS_SILENCE\r
770 #define JAWS_REPLAY\r
771 #define JAWS_ACCEL\r
772 #define JAWS_COPYRIGHT\r
773 #define JAWS_DELETE(X) X\r
774 #define SAYMACHINEMOVE()\r
775 #define SAY(X)\r
776 #endif\r
777 \r
778 /*---------------------------------------------------------------------------*\\r
779  *\r
780  * WinMain\r
781  *\r
782 \*---------------------------------------------------------------------------*/\r
783 \r
784 int APIENTRY\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
786         LPSTR lpCmdLine, int nCmdShow)\r
787 {\r
788   MSG msg;\r
789   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
790 //  INITCOMMONCONTROLSEX ex;\r
791 \r
792   debugFP = stderr;\r
793 \r
794   LoadLibrary("RICHED32.DLL");\r
795   consoleCF.cbSize = sizeof(CHARFORMAT);\r
796 \r
797   if (!InitApplication(hInstance)) {\r
798     return (FALSE);\r
799   }\r
800   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
801     return (FALSE);\r
802   }\r
803 \r
804   JAWS_INIT\r
805   TranslateMenus(1);\r
806 \r
807 //  InitCommonControlsEx(&ex);\r
808   InitCommonControls();\r
809 \r
810   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
811   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
812   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
813 \r
814   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
815 \r
816   while (GetMessage(&msg, /* message structure */\r
817                     NULL, /* handle of window receiving the message */\r
818                     0,    /* lowest message to examine */\r
819                     0))   /* highest message to examine */\r
820     {\r
821 \r
822       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
823         // [HGM] navigate: switch between all windows with tab\r
824         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
825         int i, currentElement = 0;\r
826 \r
827         // first determine what element of the chain we come from (if any)\r
828         if(appData.icsActive) {\r
829             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
830             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
831         }\r
832         if(engineOutputDialog && EngineOutputIsUp()) {\r
833             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
834             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
835         }\r
836         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
837             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
838         }\r
839         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
840         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
841         if(msg.hwnd == e1)                 currentElement = 2; else\r
842         if(msg.hwnd == e2)                 currentElement = 3; else\r
843         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
844         if(msg.hwnd == mh)                currentElement = 4; else\r
845         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
846         if(msg.hwnd == hText)  currentElement = 5; else\r
847         if(msg.hwnd == hInput) currentElement = 6; else\r
848         for (i = 0; i < N_BUTTONS; i++) {\r
849             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
850         }\r
851 \r
852         // determine where to go to\r
853         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
854           do {\r
855             currentElement = (currentElement + direction) % 7;\r
856             switch(currentElement) {\r
857                 case 0:\r
858                   h = hwndMain; break; // passing this case always makes the loop exit\r
859                 case 1:\r
860                   h = buttonDesc[0].hwnd; break; // could be NULL\r
861                 case 2:\r
862                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
863                   h = e1; break;\r
864                 case 3:\r
865                   if(!EngineOutputIsUp()) continue;\r
866                   h = e2; break;\r
867                 case 4:\r
868                   if(!MoveHistoryIsUp()) continue;\r
869                   h = mh; break;\r
870 //              case 6: // input to eval graph does not seem to get here!\r
871 //                if(!EvalGraphIsUp()) continue;\r
872 //                h = evalGraphDialog; break;\r
873                 case 5:\r
874                   if(!appData.icsActive) continue;\r
875                   SAY("display");\r
876                   h = hText; break;\r
877                 case 6:\r
878                   if(!appData.icsActive) continue;\r
879                   SAY("input");\r
880                   h = hInput; break;\r
881             }\r
882           } while(h == 0);\r
883 \r
884           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
885           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
886           SetFocus(h);\r
887 \r
888           continue; // this message now has been processed\r
889         }\r
890       }\r
891 \r
892       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
893           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
894           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
895           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
896           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
897           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
898           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
899           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
900           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
901           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
902         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
903         for(i=0; i<MAX_CHAT; i++) \r
904             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
905                 done = 1; break;\r
906         }\r
907         if(done) continue; // [HGM] chat: end patch\r
908         TranslateMessage(&msg); /* Translates virtual key codes */\r
909         DispatchMessage(&msg);  /* Dispatches message to window */\r
910       }\r
911     }\r
912 \r
913 \r
914   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
915 }\r
916 \r
917 /*---------------------------------------------------------------------------*\\r
918  *\r
919  * Initialization functions\r
920  *\r
921 \*---------------------------------------------------------------------------*/\r
922 \r
923 void\r
924 SetUserLogo()\r
925 {   // update user logo if necessary\r
926     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
927 \r
928     if(appData.autoLogo) {\r
929           curName = UserName();\r
930           if(strcmp(curName, oldUserName)) {\r
931                 GetCurrentDirectory(MSG_SIZ, dir);\r
932                 SetCurrentDirectory(installDir);\r
933                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
934                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
935                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
936                 if(userLogo == NULL)\r
937                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
938                 SetCurrentDirectory(dir); /* return to prev directory */\r
939           }\r
940     }\r
941 }\r
942 \r
943 BOOL\r
944 InitApplication(HINSTANCE hInstance)\r
945 {\r
946   WNDCLASS wc;\r
947 \r
948   /* Fill in window class structure with parameters that describe the */\r
949   /* main window. */\r
950 \r
951   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
952   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
953   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
954   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
955   wc.hInstance     = hInstance;         /* Owner of this class */\r
956   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
957   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
958   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
959   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
960   wc.lpszClassName = szAppName;                 /* Name to register as */\r
961 \r
962   /* Register the window class and return success/failure code. */\r
963   if (!RegisterClass(&wc)) return FALSE;\r
964 \r
965   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
966   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
967   wc.cbClsExtra    = 0;\r
968   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
969   wc.hInstance     = hInstance;\r
970   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
971   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
972   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
973   wc.lpszMenuName  = NULL;\r
974   wc.lpszClassName = szConsoleName;\r
975 \r
976   if (!RegisterClass(&wc)) return FALSE;\r
977   return TRUE;\r
978 }\r
979 \r
980 \r
981 /* Set by InitInstance, used by EnsureOnScreen */\r
982 int screenHeight, screenWidth;\r
983 \r
984 void\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
986 {\r
987 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
988   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
989   if (*x > screenWidth - 32) *x = 0;\r
990   if (*y > screenHeight - 32) *y = 0;\r
991   if (*x < minX) *x = minX;\r
992   if (*y < minY) *y = minY;\r
993 }\r
994 \r
995 VOID\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
997 {\r
998   char buf[MSG_SIZ], dir[MSG_SIZ];\r
999   GetCurrentDirectory(MSG_SIZ, dir);\r
1000   SetCurrentDirectory(installDir);\r
1001   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1002       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1003 \r
1004       if (cps->programLogo == NULL && appData.debugMode) {\r
1005           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1006       }\r
1007   } else if(appData.autoLogo) {\r
1008       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1009         char *opponent = "";\r
1010         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1011         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1012         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1013         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1014             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1015             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1016         }\r
1017       } else\r
1018       if(appData.directory[n] && appData.directory[n][0]) {\r
1019         SetCurrentDirectory(appData.directory[n]);\r
1020         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1021       }\r
1022   }\r
1023   SetCurrentDirectory(dir); /* return to prev directory */\r
1024 }\r
1025 \r
1026 VOID\r
1027 InitTextures()\r
1028 {\r
1029   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1030   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1031   \r
1032   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1033       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1034       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1035       liteBackTextureMode = appData.liteBackTextureMode;\r
1036 \r
1037       if (liteBackTexture == NULL && appData.debugMode) {\r
1038           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1039       }\r
1040   }\r
1041   \r
1042   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1043       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1044       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1045       darkBackTextureMode = appData.darkBackTextureMode;\r
1046 \r
1047       if (darkBackTexture == NULL && appData.debugMode) {\r
1048           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1049       }\r
1050   }\r
1051 }\r
1052 \r
1053 BOOL\r
1054 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1055 {\r
1056   HWND hwnd; /* Main window handle. */\r
1057   int ibs;\r
1058   WINDOWPLACEMENT wp;\r
1059   char *filepart;\r
1060 \r
1061   hInst = hInstance;    /* Store instance handle in our global variable */\r
1062   programName = szAppName;\r
1063 \r
1064   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1065     *filepart = NULLCHAR;\r
1066   } else {\r
1067     GetCurrentDirectory(MSG_SIZ, installDir);\r
1068   }\r
1069   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1070   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1071   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1072   /* xboard, and older WinBoards, controlled the move sound with the\r
1073      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1074      always turn the option on (so that the backend will call us),\r
1075      then let the user turn the sound off by setting it to silence if\r
1076      desired.  To accommodate old winboard.ini files saved by old\r
1077      versions of WinBoard, we also turn off the sound if the option\r
1078      was initially set to false. [HGM] taken out of InitAppData */\r
1079   if (!appData.ringBellAfterMoves) {\r
1080     sounds[(int)SoundMove].name = strdup("");\r
1081     appData.ringBellAfterMoves = TRUE;\r
1082   }\r
1083   if (appData.debugMode) {\r
1084     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1085     setbuf(debugFP, NULL);\r
1086   }\r
1087 \r
1088   LoadLanguageFile(appData.language);\r
1089 \r
1090   InitBackEnd1();\r
1091 \r
1092 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1093 //  InitEngineUCI( installDir, &second );\r
1094 \r
1095   /* Create a main window for this application instance. */\r
1096   hwnd = CreateWindow(szAppName, szTitle,\r
1097                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1098                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1099                       NULL, NULL, hInstance, NULL);\r
1100   hwndMain = hwnd;\r
1101 \r
1102   /* If window could not be created, return "failure" */\r
1103   if (!hwnd) {\r
1104     return (FALSE);\r
1105   }\r
1106 \r
1107   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1108   LoadLogo(&first, 0, FALSE);\r
1109   LoadLogo(&second, 1, appData.icsActive);\r
1110 \r
1111   SetUserLogo();\r
1112 \r
1113   iconWhite = LoadIcon(hInstance, "icon_white");\r
1114   iconBlack = LoadIcon(hInstance, "icon_black");\r
1115   iconCurrent = iconWhite;\r
1116   InitDrawingColors();\r
1117   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1118   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1119   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1120     /* Compute window size for each board size, and use the largest\r
1121        size that fits on this screen as the default. */\r
1122     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1123     if (boardSize == (BoardSize)-1 &&\r
1124         winH <= screenHeight\r
1125            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1126         && winW <= screenWidth) {\r
1127       boardSize = (BoardSize)ibs;\r
1128     }\r
1129   }\r
1130 \r
1131   InitDrawingSizes(boardSize, 0);\r
1132   RecentEngineMenu(appData.recentEngineList);\r
1133   InitMenuChecks();\r
1134   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1135 \r
1136   /* [AS] Load textures if specified */\r
1137   InitTextures();\r
1138 \r
1139   mysrandom( (unsigned) time(NULL) );\r
1140 \r
1141   /* [AS] Restore layout */\r
1142   if( wpMoveHistory.visible ) {\r
1143       MoveHistoryPopUp();\r
1144   }\r
1145 \r
1146   if( wpEvalGraph.visible ) {\r
1147       EvalGraphPopUp();\r
1148   }\r
1149 \r
1150   if( wpEngineOutput.visible ) {\r
1151       EngineOutputPopUp();\r
1152   }\r
1153 \r
1154   /* Make the window visible; update its client area; and return "success" */\r
1155   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1156   wp.length = sizeof(WINDOWPLACEMENT);\r
1157   wp.flags = 0;\r
1158   wp.showCmd = nCmdShow;\r
1159   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1160   wp.rcNormalPosition.left = wpMain.x;\r
1161   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1162   wp.rcNormalPosition.top = wpMain.y;\r
1163   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1164   SetWindowPlacement(hwndMain, &wp);\r
1165 \r
1166   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1167 \r
1168   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1169                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1170 \r
1171   if (hwndConsole) {\r
1172 #if AOT_CONSOLE\r
1173     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1174                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1175 #endif\r
1176     ShowWindow(hwndConsole, nCmdShow);\r
1177     SetActiveWindow(hwndConsole);\r
1178   }\r
1179   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1180   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1181 \r
1182   return TRUE;\r
1183 \r
1184 }\r
1185 \r
1186 VOID\r
1187 InitMenuChecks()\r
1188 {\r
1189   HMENU hmenu = GetMenu(hwndMain);\r
1190 \r
1191   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1192                         MF_BYCOMMAND|((appData.icsActive &&\r
1193                                        *appData.icsCommPort != NULLCHAR) ?\r
1194                                       MF_ENABLED : MF_GRAYED));\r
1195   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1196                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1197                                      MF_CHECKED : MF_UNCHECKED));\r
1198 }\r
1199 \r
1200 //---------------------------------------------------------------------------------------------------------\r
1201 \r
1202 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1203 #define XBOARD FALSE\r
1204 \r
1205 #define OPTCHAR "/"\r
1206 #define SEPCHAR "="\r
1207 #define TOPLEVEL 0\r
1208 \r
1209 #include "args.h"\r
1210 \r
1211 // front-end part of option handling\r
1212 \r
1213 VOID\r
1214 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1215 {\r
1216   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1217   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1218   DeleteDC(hdc);\r
1219   lf->lfWidth = 0;\r
1220   lf->lfEscapement = 0;\r
1221   lf->lfOrientation = 0;\r
1222   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1223   lf->lfItalic = mfp->italic;\r
1224   lf->lfUnderline = mfp->underline;\r
1225   lf->lfStrikeOut = mfp->strikeout;\r
1226   lf->lfCharSet = mfp->charset;\r
1227   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1228   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1229   lf->lfQuality = DEFAULT_QUALITY;\r
1230   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1231     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1232 }\r
1233 \r
1234 void\r
1235 CreateFontInMF(MyFont *mf)\r
1236\r
1237   LFfromMFP(&mf->lf, &mf->mfp);\r
1238   if (mf->hf) DeleteObject(mf->hf);\r
1239   mf->hf = CreateFontIndirect(&mf->lf);\r
1240 }\r
1241 \r
1242 // [HGM] This platform-dependent table provides the location for storing the color info\r
1243 void *\r
1244 colorVariable[] = {\r
1245   &whitePieceColor, \r
1246   &blackPieceColor, \r
1247   &lightSquareColor,\r
1248   &darkSquareColor, \r
1249   &highlightSquareColor,\r
1250   &premoveHighlightColor,\r
1251   NULL,\r
1252   &consoleBackgroundColor,\r
1253   &appData.fontForeColorWhite,\r
1254   &appData.fontBackColorWhite,\r
1255   &appData.fontForeColorBlack,\r
1256   &appData.fontBackColorBlack,\r
1257   &appData.evalHistColorWhite,\r
1258   &appData.evalHistColorBlack,\r
1259   &appData.highlightArrowColor,\r
1260 };\r
1261 \r
1262 /* Command line font name parser.  NULL name means do nothing.\r
1263    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1264    For backward compatibility, syntax without the colon is also\r
1265    accepted, but font names with digits in them won't work in that case.\r
1266 */\r
1267 VOID\r
1268 ParseFontName(char *name, MyFontParams *mfp)\r
1269 {\r
1270   char *p, *q;\r
1271   if (name == NULL) return;\r
1272   p = name;\r
1273   q = strchr(p, ':');\r
1274   if (q) {\r
1275     if (q - p >= sizeof(mfp->faceName))\r
1276       ExitArgError(_("Font name too long:"), name, TRUE);\r
1277     memcpy(mfp->faceName, p, q - p);\r
1278     mfp->faceName[q - p] = NULLCHAR;\r
1279     p = q + 1;\r
1280   } else {\r
1281     q = mfp->faceName;\r
1282     while (*p && !isdigit(*p)) {\r
1283       *q++ = *p++;\r
1284       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1285         ExitArgError(_("Font name too long:"), name, TRUE);\r
1286     }\r
1287     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1288     *q = NULLCHAR;\r
1289   }\r
1290   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1291   mfp->pointSize = (float) atof(p);\r
1292   mfp->bold = (strchr(p, 'b') != NULL);\r
1293   mfp->italic = (strchr(p, 'i') != NULL);\r
1294   mfp->underline = (strchr(p, 'u') != NULL);\r
1295   mfp->strikeout = (strchr(p, 's') != NULL);\r
1296   mfp->charset = DEFAULT_CHARSET;\r
1297   q = strchr(p, 'c');\r
1298   if (q)\r
1299     mfp->charset = (BYTE) atoi(q+1);\r
1300 }\r
1301 \r
1302 void\r
1303 ParseFont(char *name, int number)\r
1304 { // wrapper to shield back-end from 'font'\r
1305   ParseFontName(name, &font[boardSize][number]->mfp);\r
1306 }\r
1307 \r
1308 void\r
1309 SetFontDefaults()\r
1310 { // in WB  we have a 2D array of fonts; this initializes their description\r
1311   int i, j;\r
1312   /* Point font array elements to structures and\r
1313      parse default font names */\r
1314   for (i=0; i<NUM_FONTS; i++) {\r
1315     for (j=0; j<NUM_SIZES; j++) {\r
1316       font[j][i] = &fontRec[j][i];\r
1317       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1318     }\r
1319   }\r
1320 }\r
1321 \r
1322 void\r
1323 CreateFonts()\r
1324 { // here we create the actual fonts from the selected descriptions\r
1325   int i, j;\r
1326   for (i=0; i<NUM_FONTS; i++) {\r
1327     for (j=0; j<NUM_SIZES; j++) {\r
1328       CreateFontInMF(font[j][i]);\r
1329     }\r
1330   }\r
1331 }\r
1332 /* Color name parser.\r
1333    X version accepts X color names, but this one\r
1334    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1335 COLORREF\r
1336 ParseColorName(char *name)\r
1337 {\r
1338   int red, green, blue, count;\r
1339   char buf[MSG_SIZ];\r
1340 \r
1341   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1342   if (count != 3) {\r
1343     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1344       &red, &green, &blue);\r
1345   }\r
1346   if (count != 3) {\r
1347     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1348     DisplayError(buf, 0);\r
1349     return RGB(0, 0, 0);\r
1350   }\r
1351   return PALETTERGB(red, green, blue);\r
1352 }\r
1353 \r
1354 void\r
1355 ParseColor(int n, char *name)\r
1356 { // for WinBoard the color is an int, which needs to be derived from the string\r
1357   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1358 }\r
1359 \r
1360 void\r
1361 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1362 {\r
1363   char *e = argValue;\r
1364   int eff = 0;\r
1365 \r
1366   while (*e) {\r
1367     if (*e == 'b')      eff |= CFE_BOLD;\r
1368     else if (*e == 'i') eff |= CFE_ITALIC;\r
1369     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1370     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1371     else if (*e == '#' || isdigit(*e)) break;\r
1372     e++;\r
1373   }\r
1374   *effects = eff;\r
1375   *color   = ParseColorName(e);\r
1376 }\r
1377 \r
1378 void\r
1379 ParseTextAttribs(ColorClass cc, char *s)\r
1380 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1381     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1382     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1383 }\r
1384 \r
1385 void\r
1386 ParseBoardSize(void *addr, char *name)\r
1387 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1388   BoardSize bs = SizeTiny;\r
1389   while (sizeInfo[bs].name != NULL) {\r
1390     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1391         *(BoardSize *)addr = bs;\r
1392         return;\r
1393     }\r
1394     bs++;\r
1395   }\r
1396   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1397 }\r
1398 \r
1399 void\r
1400 LoadAllSounds()\r
1401 { // [HGM] import name from appData first\r
1402   ColorClass cc;\r
1403   SoundClass sc;\r
1404   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1405     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1406     textAttribs[cc].sound.data = NULL;\r
1407     MyLoadSound(&textAttribs[cc].sound);\r
1408   }\r
1409   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1410     textAttribs[cc].sound.name = strdup("");\r
1411     textAttribs[cc].sound.data = NULL;\r
1412   }\r
1413   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1414     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1415     sounds[sc].data = NULL;\r
1416     MyLoadSound(&sounds[sc]);\r
1417   }\r
1418 }\r
1419 \r
1420 void\r
1421 SetCommPortDefaults()\r
1422 {\r
1423    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1424   dcb.DCBlength = sizeof(DCB);\r
1425   dcb.BaudRate = 9600;\r
1426   dcb.fBinary = TRUE;\r
1427   dcb.fParity = FALSE;\r
1428   dcb.fOutxCtsFlow = FALSE;\r
1429   dcb.fOutxDsrFlow = FALSE;\r
1430   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1431   dcb.fDsrSensitivity = FALSE;\r
1432   dcb.fTXContinueOnXoff = TRUE;\r
1433   dcb.fOutX = FALSE;\r
1434   dcb.fInX = FALSE;\r
1435   dcb.fNull = FALSE;\r
1436   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1437   dcb.fAbortOnError = FALSE;\r
1438   dcb.ByteSize = 7;\r
1439   dcb.Parity = SPACEPARITY;\r
1440   dcb.StopBits = ONESTOPBIT;\r
1441 }\r
1442 \r
1443 // [HGM] args: these three cases taken out to stay in front-end\r
1444 void\r
1445 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1446 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1447         // while the curent board size determines the element. This system should be ported to XBoard.\r
1448         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1449         int bs;\r
1450         for (bs=0; bs<NUM_SIZES; bs++) {\r
1451           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1452           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1453           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1454             ad->argName, mfp->faceName, mfp->pointSize,\r
1455             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1456             mfp->bold ? "b" : "",\r
1457             mfp->italic ? "i" : "",\r
1458             mfp->underline ? "u" : "",\r
1459             mfp->strikeout ? "s" : "",\r
1460             (int)mfp->charset);\r
1461         }\r
1462       }\r
1463 \r
1464 void\r
1465 ExportSounds()\r
1466 { // [HGM] copy the names from the internal WB variables to appData\r
1467   ColorClass cc;\r
1468   SoundClass sc;\r
1469   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1470     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1471   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1472     (&appData.soundMove)[sc] = sounds[sc].name;\r
1473 }\r
1474 \r
1475 void\r
1476 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1477 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1478         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1479         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1480           (ta->effects & CFE_BOLD) ? "b" : "",\r
1481           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1482           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1483           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1484           (ta->effects) ? " " : "",\r
1485           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1486       }\r
1487 \r
1488 void\r
1489 SaveColor(FILE *f, ArgDescriptor *ad)\r
1490 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1491         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1492         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1493           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1494 }\r
1495 \r
1496 void\r
1497 SaveBoardSize(FILE *f, char *name, void *addr)\r
1498 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1499   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1500 }\r
1501 \r
1502 void\r
1503 ParseCommPortSettings(char *s)\r
1504 { // wrapper to keep dcb from back-end\r
1505   ParseCommSettings(s, &dcb);\r
1506 }\r
1507 \r
1508 void\r
1509 GetWindowCoords()\r
1510 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1511   GetActualPlacement(hwndMain, &wpMain);\r
1512   GetActualPlacement(hwndConsole, &wpConsole);\r
1513   GetActualPlacement(commentDialog, &wpComment);\r
1514   GetActualPlacement(editTagsDialog, &wpTags);\r
1515   GetActualPlacement(gameListDialog, &wpGameList);\r
1516   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1517   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1518   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1519 }\r
1520 \r
1521 void\r
1522 PrintCommPortSettings(FILE *f, char *name)\r
1523 { // wrapper to shield back-end from DCB\r
1524       PrintCommSettings(f, name, &dcb);\r
1525 }\r
1526 \r
1527 int\r
1528 MySearchPath(char *installDir, char *name, char *fullname)\r
1529 {\r
1530   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1531   if(name[0]== '%') {\r
1532     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1533     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1534       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1535       *strchr(buf, '%') = 0;\r
1536       strcat(fullname, getenv(buf));\r
1537       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1538     }\r
1539     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1540     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1541     return (int) strlen(fullname);\r
1542   }\r
1543   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1544 }\r
1545 \r
1546 int\r
1547 MyGetFullPathName(char *name, char *fullname)\r
1548 {\r
1549   char *dummy;\r
1550   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1551 }\r
1552 \r
1553 int\r
1554 MainWindowUp()\r
1555 { // [HGM] args: allows testing if main window is realized from back-end\r
1556   return hwndMain != NULL;\r
1557 }\r
1558 \r
1559 void\r
1560 PopUpStartupDialog()\r
1561 {\r
1562     FARPROC lpProc;\r
1563     \r
1564     LoadLanguageFile(appData.language);\r
1565     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1566     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1567     FreeProcInstance(lpProc);\r
1568 }\r
1569 \r
1570 /*---------------------------------------------------------------------------*\\r
1571  *\r
1572  * GDI board drawing routines\r
1573  *\r
1574 \*---------------------------------------------------------------------------*/\r
1575 \r
1576 /* [AS] Draw square using background texture */\r
1577 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1578 {\r
1579     XFORM   x;\r
1580 \r
1581     if( mode == 0 ) {\r
1582         return; /* Should never happen! */\r
1583     }\r
1584 \r
1585     SetGraphicsMode( dst, GM_ADVANCED );\r
1586 \r
1587     switch( mode ) {\r
1588     case 1:\r
1589         /* Identity */\r
1590         break;\r
1591     case 2:\r
1592         /* X reflection */\r
1593         x.eM11 = -1.0;\r
1594         x.eM12 = 0;\r
1595         x.eM21 = 0;\r
1596         x.eM22 = 1.0;\r
1597         x.eDx = (FLOAT) dw + dx - 1;\r
1598         x.eDy = 0;\r
1599         dx = 0;\r
1600         SetWorldTransform( dst, &x );\r
1601         break;\r
1602     case 3:\r
1603         /* Y reflection */\r
1604         x.eM11 = 1.0;\r
1605         x.eM12 = 0;\r
1606         x.eM21 = 0;\r
1607         x.eM22 = -1.0;\r
1608         x.eDx = 0;\r
1609         x.eDy = (FLOAT) dh + dy - 1;\r
1610         dy = 0;\r
1611         SetWorldTransform( dst, &x );\r
1612         break;\r
1613     case 4:\r
1614         /* X/Y flip */\r
1615         x.eM11 = 0;\r
1616         x.eM12 = 1.0;\r
1617         x.eM21 = 1.0;\r
1618         x.eM22 = 0;\r
1619         x.eDx = (FLOAT) dx;\r
1620         x.eDy = (FLOAT) dy;\r
1621         dx = 0;\r
1622         dy = 0;\r
1623         SetWorldTransform( dst, &x );\r
1624         break;\r
1625     }\r
1626 \r
1627     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1628 \r
1629     x.eM11 = 1.0;\r
1630     x.eM12 = 0;\r
1631     x.eM21 = 0;\r
1632     x.eM22 = 1.0;\r
1633     x.eDx = 0;\r
1634     x.eDy = 0;\r
1635     SetWorldTransform( dst, &x );\r
1636 \r
1637     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1638 }\r
1639 \r
1640 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1641 enum {\r
1642     PM_WP = (int) WhitePawn, \r
1643     PM_WN = (int) WhiteKnight, \r
1644     PM_WB = (int) WhiteBishop, \r
1645     PM_WR = (int) WhiteRook, \r
1646     PM_WQ = (int) WhiteQueen, \r
1647     PM_WF = (int) WhiteFerz, \r
1648     PM_WW = (int) WhiteWazir, \r
1649     PM_WE = (int) WhiteAlfil, \r
1650     PM_WM = (int) WhiteMan, \r
1651     PM_WO = (int) WhiteCannon, \r
1652     PM_WU = (int) WhiteUnicorn, \r
1653     PM_WH = (int) WhiteNightrider, \r
1654     PM_WA = (int) WhiteAngel, \r
1655     PM_WC = (int) WhiteMarshall, \r
1656     PM_WAB = (int) WhiteCardinal, \r
1657     PM_WD = (int) WhiteDragon, \r
1658     PM_WL = (int) WhiteLance, \r
1659     PM_WS = (int) WhiteCobra, \r
1660     PM_WV = (int) WhiteFalcon, \r
1661     PM_WSG = (int) WhiteSilver, \r
1662     PM_WG = (int) WhiteGrasshopper, \r
1663     PM_WK = (int) WhiteKing,\r
1664     PM_BP = (int) BlackPawn, \r
1665     PM_BN = (int) BlackKnight, \r
1666     PM_BB = (int) BlackBishop, \r
1667     PM_BR = (int) BlackRook, \r
1668     PM_BQ = (int) BlackQueen, \r
1669     PM_BF = (int) BlackFerz, \r
1670     PM_BW = (int) BlackWazir, \r
1671     PM_BE = (int) BlackAlfil, \r
1672     PM_BM = (int) BlackMan,\r
1673     PM_BO = (int) BlackCannon, \r
1674     PM_BU = (int) BlackUnicorn, \r
1675     PM_BH = (int) BlackNightrider, \r
1676     PM_BA = (int) BlackAngel, \r
1677     PM_BC = (int) BlackMarshall, \r
1678     PM_BG = (int) BlackGrasshopper, \r
1679     PM_BAB = (int) BlackCardinal,\r
1680     PM_BD = (int) BlackDragon,\r
1681     PM_BL = (int) BlackLance,\r
1682     PM_BS = (int) BlackCobra,\r
1683     PM_BV = (int) BlackFalcon,\r
1684     PM_BSG = (int) BlackSilver,\r
1685     PM_BK = (int) BlackKing\r
1686 };\r
1687 \r
1688 static HFONT hPieceFont = NULL;\r
1689 static HBITMAP hPieceMask[(int) EmptySquare];\r
1690 static HBITMAP hPieceFace[(int) EmptySquare];\r
1691 static int fontBitmapSquareSize = 0;\r
1692 static char pieceToFontChar[(int) EmptySquare] =\r
1693                               { 'p', 'n', 'b', 'r', 'q', \r
1694                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1695                       'k', 'o', 'm', 'v', 't', 'w', \r
1696                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1697                                                               'l' };\r
1698 \r
1699 extern BOOL SetCharTable( char *table, const char * map );\r
1700 /* [HGM] moved to backend.c */\r
1701 \r
1702 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1703 {\r
1704     HBRUSH hbrush;\r
1705     BYTE r1 = GetRValue( color );\r
1706     BYTE g1 = GetGValue( color );\r
1707     BYTE b1 = GetBValue( color );\r
1708     BYTE r2 = r1 / 2;\r
1709     BYTE g2 = g1 / 2;\r
1710     BYTE b2 = b1 / 2;\r
1711     RECT rc;\r
1712 \r
1713     /* Create a uniform background first */\r
1714     hbrush = CreateSolidBrush( color );\r
1715     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1716     FillRect( hdc, &rc, hbrush );\r
1717     DeleteObject( hbrush );\r
1718     \r
1719     if( mode == 1 ) {\r
1720         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1721         int steps = squareSize / 2;\r
1722         int i;\r
1723 \r
1724         for( i=0; i<steps; i++ ) {\r
1725             BYTE r = r1 - (r1-r2) * i / steps;\r
1726             BYTE g = g1 - (g1-g2) * i / steps;\r
1727             BYTE b = b1 - (b1-b2) * i / steps;\r
1728 \r
1729             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1730             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1731             FillRect( hdc, &rc, hbrush );\r
1732             DeleteObject(hbrush);\r
1733         }\r
1734     }\r
1735     else if( mode == 2 ) {\r
1736         /* Diagonal gradient, good more or less for every piece */\r
1737         POINT triangle[3];\r
1738         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1739         HBRUSH hbrush_old;\r
1740         int steps = squareSize;\r
1741         int i;\r
1742 \r
1743         triangle[0].x = squareSize - steps;\r
1744         triangle[0].y = squareSize;\r
1745         triangle[1].x = squareSize;\r
1746         triangle[1].y = squareSize;\r
1747         triangle[2].x = squareSize;\r
1748         triangle[2].y = squareSize - steps;\r
1749 \r
1750         for( i=0; i<steps; i++ ) {\r
1751             BYTE r = r1 - (r1-r2) * i / steps;\r
1752             BYTE g = g1 - (g1-g2) * i / steps;\r
1753             BYTE b = b1 - (b1-b2) * i / steps;\r
1754 \r
1755             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1756             hbrush_old = SelectObject( hdc, hbrush );\r
1757             Polygon( hdc, triangle, 3 );\r
1758             SelectObject( hdc, hbrush_old );\r
1759             DeleteObject(hbrush);\r
1760             triangle[0].x++;\r
1761             triangle[2].y++;\r
1762         }\r
1763 \r
1764         SelectObject( hdc, hpen );\r
1765     }\r
1766 }\r
1767 \r
1768 /*\r
1769     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1770     seems to work ok. The main problem here is to find the "inside" of a chess\r
1771     piece: follow the steps as explained below.\r
1772 */\r
1773 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1774 {\r
1775     HBITMAP hbm;\r
1776     HBITMAP hbm_old;\r
1777     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1778     RECT rc;\r
1779     SIZE sz;\r
1780     POINT pt;\r
1781     int backColor = whitePieceColor; \r
1782     int foreColor = blackPieceColor;\r
1783     \r
1784     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1785         backColor = appData.fontBackColorWhite;\r
1786         foreColor = appData.fontForeColorWhite;\r
1787     }\r
1788     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1789         backColor = appData.fontBackColorBlack;\r
1790         foreColor = appData.fontForeColorBlack;\r
1791     }\r
1792 \r
1793     /* Mask */\r
1794     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1795 \r
1796     hbm_old = SelectObject( hdc, hbm );\r
1797 \r
1798     rc.left = 0;\r
1799     rc.top = 0;\r
1800     rc.right = squareSize;\r
1801     rc.bottom = squareSize;\r
1802 \r
1803     /* Step 1: background is now black */\r
1804     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1805 \r
1806     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1807 \r
1808     pt.x = (squareSize - sz.cx) / 2;\r
1809     pt.y = (squareSize - sz.cy) / 2;\r
1810 \r
1811     SetBkMode( hdc, TRANSPARENT );\r
1812     SetTextColor( hdc, chroma );\r
1813     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1814     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1815 \r
1816     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1817     /* Step 3: the area outside the piece is filled with white */\r
1818 //    FloodFill( hdc, 0, 0, chroma );\r
1819     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1820     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1821     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1822     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1823     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1824     /* \r
1825         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1826         but if the start point is not inside the piece we're lost!\r
1827         There should be a better way to do this... if we could create a region or path\r
1828         from the fill operation we would be fine for example.\r
1829     */\r
1830 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1831     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1832 \r
1833     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1834         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1835         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1836 \r
1837         SelectObject( dc2, bm2 );\r
1838         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1839         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1840         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1841         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1842         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1843 \r
1844         DeleteDC( dc2 );\r
1845         DeleteObject( bm2 );\r
1846     }\r
1847 \r
1848     SetTextColor( hdc, 0 );\r
1849     /* \r
1850         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1851         draw the piece again in black for safety.\r
1852     */\r
1853     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1854 \r
1855     SelectObject( hdc, hbm_old );\r
1856 \r
1857     if( hPieceMask[index] != NULL ) {\r
1858         DeleteObject( hPieceMask[index] );\r
1859     }\r
1860 \r
1861     hPieceMask[index] = hbm;\r
1862 \r
1863     /* Face */\r
1864     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1865 \r
1866     SelectObject( hdc, hbm );\r
1867 \r
1868     {\r
1869         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1870         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1871         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1872 \r
1873         SelectObject( dc1, hPieceMask[index] );\r
1874         SelectObject( dc2, bm2 );\r
1875         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1876         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1877         \r
1878         /* \r
1879             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1880             the piece background and deletes (makes transparent) the rest.\r
1881             Thanks to that mask, we are free to paint the background with the greates\r
1882             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1883             We use this, to make gradients and give the pieces a "roundish" look.\r
1884         */\r
1885         SetPieceBackground( hdc, backColor, 2 );\r
1886         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1887 \r
1888         DeleteDC( dc2 );\r
1889         DeleteDC( dc1 );\r
1890         DeleteObject( bm2 );\r
1891     }\r
1892 \r
1893     SetTextColor( hdc, foreColor );\r
1894     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1895 \r
1896     SelectObject( hdc, hbm_old );\r
1897 \r
1898     if( hPieceFace[index] != NULL ) {\r
1899         DeleteObject( hPieceFace[index] );\r
1900     }\r
1901 \r
1902     hPieceFace[index] = hbm;\r
1903 }\r
1904 \r
1905 static int TranslatePieceToFontPiece( int piece )\r
1906 {\r
1907     switch( piece ) {\r
1908     case BlackPawn:\r
1909         return PM_BP;\r
1910     case BlackKnight:\r
1911         return PM_BN;\r
1912     case BlackBishop:\r
1913         return PM_BB;\r
1914     case BlackRook:\r
1915         return PM_BR;\r
1916     case BlackQueen:\r
1917         return PM_BQ;\r
1918     case BlackKing:\r
1919         return PM_BK;\r
1920     case WhitePawn:\r
1921         return PM_WP;\r
1922     case WhiteKnight:\r
1923         return PM_WN;\r
1924     case WhiteBishop:\r
1925         return PM_WB;\r
1926     case WhiteRook:\r
1927         return PM_WR;\r
1928     case WhiteQueen:\r
1929         return PM_WQ;\r
1930     case WhiteKing:\r
1931         return PM_WK;\r
1932 \r
1933     case BlackAngel:\r
1934         return PM_BA;\r
1935     case BlackMarshall:\r
1936         return PM_BC;\r
1937     case BlackFerz:\r
1938         return PM_BF;\r
1939     case BlackNightrider:\r
1940         return PM_BH;\r
1941     case BlackAlfil:\r
1942         return PM_BE;\r
1943     case BlackWazir:\r
1944         return PM_BW;\r
1945     case BlackUnicorn:\r
1946         return PM_BU;\r
1947     case BlackCannon:\r
1948         return PM_BO;\r
1949     case BlackGrasshopper:\r
1950         return PM_BG;\r
1951     case BlackMan:\r
1952         return PM_BM;\r
1953     case BlackSilver:\r
1954         return PM_BSG;\r
1955     case BlackLance:\r
1956         return PM_BL;\r
1957     case BlackFalcon:\r
1958         return PM_BV;\r
1959     case BlackCobra:\r
1960         return PM_BS;\r
1961     case BlackCardinal:\r
1962         return PM_BAB;\r
1963     case BlackDragon:\r
1964         return PM_BD;\r
1965 \r
1966     case WhiteAngel:\r
1967         return PM_WA;\r
1968     case WhiteMarshall:\r
1969         return PM_WC;\r
1970     case WhiteFerz:\r
1971         return PM_WF;\r
1972     case WhiteNightrider:\r
1973         return PM_WH;\r
1974     case WhiteAlfil:\r
1975         return PM_WE;\r
1976     case WhiteWazir:\r
1977         return PM_WW;\r
1978     case WhiteUnicorn:\r
1979         return PM_WU;\r
1980     case WhiteCannon:\r
1981         return PM_WO;\r
1982     case WhiteGrasshopper:\r
1983         return PM_WG;\r
1984     case WhiteMan:\r
1985         return PM_WM;\r
1986     case WhiteSilver:\r
1987         return PM_WSG;\r
1988     case WhiteLance:\r
1989         return PM_WL;\r
1990     case WhiteFalcon:\r
1991         return PM_WV;\r
1992     case WhiteCobra:\r
1993         return PM_WS;\r
1994     case WhiteCardinal:\r
1995         return PM_WAB;\r
1996     case WhiteDragon:\r
1997         return PM_WD;\r
1998     }\r
1999 \r
2000     return 0;\r
2001 }\r
2002 \r
2003 void CreatePiecesFromFont()\r
2004 {\r
2005     LOGFONT lf;\r
2006     HDC hdc_window = NULL;\r
2007     HDC hdc = NULL;\r
2008     HFONT hfont_old;\r
2009     int fontHeight;\r
2010     int i;\r
2011 \r
2012     if( fontBitmapSquareSize < 0 ) {\r
2013         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2014         return;\r
2015     }\r
2016 \r
2017     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2018             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2019         fontBitmapSquareSize = -1;\r
2020         return;\r
2021     }\r
2022 \r
2023     if( fontBitmapSquareSize != squareSize ) {\r
2024         hdc_window = GetDC( hwndMain );\r
2025         hdc = CreateCompatibleDC( hdc_window );\r
2026 \r
2027         if( hPieceFont != NULL ) {\r
2028             DeleteObject( hPieceFont );\r
2029         }\r
2030         else {\r
2031             for( i=0; i<=(int)BlackKing; i++ ) {\r
2032                 hPieceMask[i] = NULL;\r
2033                 hPieceFace[i] = NULL;\r
2034             }\r
2035         }\r
2036 \r
2037         fontHeight = 75;\r
2038 \r
2039         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2040             fontHeight = appData.fontPieceSize;\r
2041         }\r
2042 \r
2043         fontHeight = (fontHeight * squareSize) / 100;\r
2044 \r
2045         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2046         lf.lfWidth = 0;\r
2047         lf.lfEscapement = 0;\r
2048         lf.lfOrientation = 0;\r
2049         lf.lfWeight = FW_NORMAL;\r
2050         lf.lfItalic = 0;\r
2051         lf.lfUnderline = 0;\r
2052         lf.lfStrikeOut = 0;\r
2053         lf.lfCharSet = DEFAULT_CHARSET;\r
2054         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2055         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2056         lf.lfQuality = PROOF_QUALITY;\r
2057         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2058         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2059         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2060 \r
2061         hPieceFont = CreateFontIndirect( &lf );\r
2062 \r
2063         if( hPieceFont == NULL ) {\r
2064             fontBitmapSquareSize = -2;\r
2065         }\r
2066         else {\r
2067             /* Setup font-to-piece character table */\r
2068             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2069                 /* No (or wrong) global settings, try to detect the font */\r
2070                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2071                     /* Alpha */\r
2072                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2073                 }\r
2074                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2075                     /* DiagramTT* family */\r
2076                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2077                 }\r
2078                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2079                     /* Fairy symbols */\r
2080                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2081                 }\r
2082                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2083                     /* Good Companion (Some characters get warped as literal :-( */\r
2084                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2085                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2086                     SetCharTable(pieceToFontChar, s);\r
2087                 }\r
2088                 else {\r
2089                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2090                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2091                 }\r
2092             }\r
2093 \r
2094             /* Create bitmaps */\r
2095             hfont_old = SelectObject( hdc, hPieceFont );\r
2096             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2097                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2098                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2099 \r
2100             SelectObject( hdc, hfont_old );\r
2101 \r
2102             fontBitmapSquareSize = squareSize;\r
2103         }\r
2104     }\r
2105 \r
2106     if( hdc != NULL ) {\r
2107         DeleteDC( hdc );\r
2108     }\r
2109 \r
2110     if( hdc_window != NULL ) {\r
2111         ReleaseDC( hwndMain, hdc_window );\r
2112     }\r
2113 }\r
2114 \r
2115 HBITMAP\r
2116 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2117 {\r
2118   char name[128], buf[MSG_SIZ];\r
2119 \r
2120     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2121   if(appData.pieceDirectory[0]) {\r
2122     HBITMAP res;\r
2123     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2124     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2125     if(res) return res;\r
2126   }\r
2127   if (gameInfo.event &&\r
2128       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2129       strcmp(name, "k80s") == 0) {\r
2130     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2131   }\r
2132   return LoadBitmap(hinst, name);\r
2133 }\r
2134 \r
2135 \r
2136 /* Insert a color into the program's logical palette\r
2137    structure.  This code assumes the given color is\r
2138    the result of the RGB or PALETTERGB macro, and it\r
2139    knows how those macros work (which is documented).\r
2140 */\r
2141 VOID\r
2142 InsertInPalette(COLORREF color)\r
2143 {\r
2144   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2145 \r
2146   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2147     DisplayFatalError(_("Too many colors"), 0, 1);\r
2148     pLogPal->palNumEntries--;\r
2149     return;\r
2150   }\r
2151 \r
2152   pe->peFlags = (char) 0;\r
2153   pe->peRed = (char) (0xFF & color);\r
2154   pe->peGreen = (char) (0xFF & (color >> 8));\r
2155   pe->peBlue = (char) (0xFF & (color >> 16));\r
2156   return;\r
2157 }\r
2158 \r
2159 \r
2160 VOID\r
2161 InitDrawingColors()\r
2162 {\r
2163   if (pLogPal == NULL) {\r
2164     /* Allocate enough memory for a logical palette with\r
2165      * PALETTESIZE entries and set the size and version fields\r
2166      * of the logical palette structure.\r
2167      */\r
2168     pLogPal = (NPLOGPALETTE)\r
2169       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2170                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2171     pLogPal->palVersion    = 0x300;\r
2172   }\r
2173   pLogPal->palNumEntries = 0;\r
2174 \r
2175   InsertInPalette(lightSquareColor);\r
2176   InsertInPalette(darkSquareColor);\r
2177   InsertInPalette(whitePieceColor);\r
2178   InsertInPalette(blackPieceColor);\r
2179   InsertInPalette(highlightSquareColor);\r
2180   InsertInPalette(premoveHighlightColor);\r
2181 \r
2182   /*  create a logical color palette according the information\r
2183    *  in the LOGPALETTE structure.\r
2184    */\r
2185   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2186 \r
2187   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2188   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2189   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2190   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2191   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2192   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2193   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2194   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2195   /* [AS] Force rendering of the font-based pieces */\r
2196   if( fontBitmapSquareSize > 0 ) {\r
2197     fontBitmapSquareSize = 0;\r
2198   }\r
2199 }\r
2200 \r
2201 \r
2202 int\r
2203 BoardWidth(int boardSize, int n)\r
2204 { /* [HGM] argument n added to allow different width and height */\r
2205   int lineGap = sizeInfo[boardSize].lineGap;\r
2206 \r
2207   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2208       lineGap = appData.overrideLineGap;\r
2209   }\r
2210 \r
2211   return (n + 1) * lineGap +\r
2212           n * sizeInfo[boardSize].squareSize;\r
2213 }\r
2214 \r
2215 /* Respond to board resize by dragging edge */\r
2216 VOID\r
2217 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2218 {\r
2219   BoardSize newSize = NUM_SIZES - 1;\r
2220   static int recurse = 0;\r
2221   if (IsIconic(hwndMain)) return;\r
2222   if (recurse > 0) return;\r
2223   recurse++;\r
2224   while (newSize > 0) {\r
2225         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2226         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2227            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2228     newSize--;\r
2229   } \r
2230   boardSize = newSize;\r
2231   InitDrawingSizes(boardSize, flags);\r
2232   recurse--;\r
2233 }\r
2234 \r
2235 \r
2236 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2237 \r
2238 VOID\r
2239 InitDrawingSizes(BoardSize boardSize, int flags)\r
2240 {\r
2241   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2242   ChessSquare piece;\r
2243   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2244   HDC hdc;\r
2245   SIZE clockSize, messageSize;\r
2246   HFONT oldFont;\r
2247   char buf[MSG_SIZ];\r
2248   char *str;\r
2249   HMENU hmenu = GetMenu(hwndMain);\r
2250   RECT crect, wrect, oldRect;\r
2251   int offby;\r
2252   LOGBRUSH logbrush;\r
2253   VariantClass v = gameInfo.variant;\r
2254 \r
2255   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2256   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2257 \r
2258   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2259   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2260   oldBoardSize = boardSize;\r
2261 \r
2262   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2263   { // correct board size to one where built-in pieces exist\r
2264     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2265        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2266       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2267       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2268       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2269       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2270       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2271                                    boardSize = SizeMiddling;\r
2272     }\r
2273   }\r
2274   if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2275 \r
2276   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2277   oldRect.top = wpMain.y;\r
2278   oldRect.right = wpMain.x + wpMain.width;\r
2279   oldRect.bottom = wpMain.y + wpMain.height;\r
2280 \r
2281   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2282   smallLayout = sizeInfo[boardSize].smallLayout;\r
2283   squareSize = sizeInfo[boardSize].squareSize;\r
2284   lineGap = sizeInfo[boardSize].lineGap;\r
2285   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2286   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2287 \r
2288   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2289       lineGap = appData.overrideLineGap;\r
2290   }\r
2291 \r
2292   if (tinyLayout != oldTinyLayout) {\r
2293     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2294     if (tinyLayout) {\r
2295       style &= ~WS_SYSMENU;\r
2296       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2297                  "&Minimize\tCtrl+F4");\r
2298     } else {\r
2299       style |= WS_SYSMENU;\r
2300       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2301     }\r
2302     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2303 \r
2304     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2305       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2306         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2307     }\r
2308     DrawMenuBar(hwndMain);\r
2309   }\r
2310 \r
2311   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2312   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2313 \r
2314   /* Get text area sizes */\r
2315   hdc = GetDC(hwndMain);\r
2316   if (appData.clockMode) {\r
2317     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2318   } else {\r
2319     snprintf(buf, MSG_SIZ, _("White"));\r
2320   }\r
2321   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2322   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2323   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2324   str = _("We only care about the height here");\r
2325   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2326   SelectObject(hdc, oldFont);\r
2327   ReleaseDC(hwndMain, hdc);\r
2328 \r
2329   /* Compute where everything goes */\r
2330   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2331         /* [HGM] logo: if either logo is on, reserve space for it */\r
2332         logoHeight =  2*clockSize.cy;\r
2333         leftLogoRect.left   = OUTER_MARGIN;\r
2334         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2335         leftLogoRect.top    = OUTER_MARGIN;\r
2336         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2337 \r
2338         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2339         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2340         rightLogoRect.top    = OUTER_MARGIN;\r
2341         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2342 \r
2343 \r
2344     whiteRect.left = leftLogoRect.right;\r
2345     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2346     whiteRect.top = OUTER_MARGIN;\r
2347     whiteRect.bottom = whiteRect.top + logoHeight;\r
2348 \r
2349     blackRect.right = rightLogoRect.left;\r
2350     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2351     blackRect.top = whiteRect.top;\r
2352     blackRect.bottom = whiteRect.bottom;\r
2353   } else {\r
2354     whiteRect.left = OUTER_MARGIN;\r
2355     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2356     whiteRect.top = OUTER_MARGIN;\r
2357     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2358 \r
2359     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2360     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2361     blackRect.top = whiteRect.top;\r
2362     blackRect.bottom = whiteRect.bottom;\r
2363 \r
2364     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2365   }\r
2366 \r
2367   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2368   if (appData.showButtonBar) {\r
2369     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2370       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2371   } else {\r
2372     messageRect.right = OUTER_MARGIN + boardWidth;\r
2373   }\r
2374   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2375   messageRect.bottom = messageRect.top + messageSize.cy;\r
2376 \r
2377   boardRect.left = OUTER_MARGIN;\r
2378   boardRect.right = boardRect.left + boardWidth;\r
2379   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2380   boardRect.bottom = boardRect.top + boardHeight;\r
2381 \r
2382   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2383   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2384   oldTinyLayout = tinyLayout;\r
2385   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2386   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2387     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2388   winW *= 1 + twoBoards;\r
2389   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2390   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2391   wpMain.height = winH; //       without disturbing window attachments\r
2392   GetWindowRect(hwndMain, &wrect);\r
2393   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2394                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2395 \r
2396   // [HGM] placement: let attached windows follow size change.\r
2397   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2398   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2399   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2400   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2401   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2402 \r
2403   /* compensate if menu bar wrapped */\r
2404   GetClientRect(hwndMain, &crect);\r
2405   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2406   wpMain.height += offby;\r
2407   switch (flags) {\r
2408   case WMSZ_TOPLEFT:\r
2409     SetWindowPos(hwndMain, NULL, \r
2410                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2411                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2412     break;\r
2413 \r
2414   case WMSZ_TOPRIGHT:\r
2415   case WMSZ_TOP:\r
2416     SetWindowPos(hwndMain, NULL, \r
2417                  wrect.left, wrect.bottom - wpMain.height, \r
2418                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2419     break;\r
2420 \r
2421   case WMSZ_BOTTOMLEFT:\r
2422   case WMSZ_LEFT:\r
2423     SetWindowPos(hwndMain, NULL, \r
2424                  wrect.right - wpMain.width, wrect.top, \r
2425                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2426     break;\r
2427 \r
2428   case WMSZ_BOTTOMRIGHT:\r
2429   case WMSZ_BOTTOM:\r
2430   case WMSZ_RIGHT:\r
2431   default:\r
2432     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2433                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2434     break;\r
2435   }\r
2436 \r
2437   hwndPause = NULL;\r
2438   for (i = 0; i < N_BUTTONS; i++) {\r
2439     if (buttonDesc[i].hwnd != NULL) {\r
2440       DestroyWindow(buttonDesc[i].hwnd);\r
2441       buttonDesc[i].hwnd = NULL;\r
2442     }\r
2443     if (appData.showButtonBar) {\r
2444       buttonDesc[i].hwnd =\r
2445         CreateWindow("BUTTON", buttonDesc[i].label,\r
2446                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2447                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2448                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2449                      (HMENU) buttonDesc[i].id,\r
2450                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2451       if (tinyLayout) {\r
2452         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2453                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2454                     MAKELPARAM(FALSE, 0));\r
2455       }\r
2456       if (buttonDesc[i].id == IDM_Pause)\r
2457         hwndPause = buttonDesc[i].hwnd;\r
2458       buttonDesc[i].wndproc = (WNDPROC)\r
2459         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2460     }\r
2461   }\r
2462   if (gridPen != NULL) DeleteObject(gridPen);\r
2463   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2464   if (premovePen != NULL) DeleteObject(premovePen);\r
2465   if (lineGap != 0) {\r
2466     logbrush.lbStyle = BS_SOLID;\r
2467     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2468     gridPen =\r
2469       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2470                    lineGap, &logbrush, 0, NULL);\r
2471     logbrush.lbColor = highlightSquareColor;\r
2472     highlightPen =\r
2473       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2474                    lineGap, &logbrush, 0, NULL);\r
2475 \r
2476     logbrush.lbColor = premoveHighlightColor; \r
2477     premovePen =\r
2478       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2479                    lineGap, &logbrush, 0, NULL);\r
2480 \r
2481     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2482     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2483       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2484       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2485         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2486       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2487         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2488       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2489     }\r
2490     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2491       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2492       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2493         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2494         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2495       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2496         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2497       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2498     }\r
2499   }\r
2500 \r
2501   /* [HGM] Licensing requirement */\r
2502 #ifdef GOTHIC\r
2503   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2504 #endif\r
2505 #ifdef FALCON\r
2506   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2507 #endif\r
2508   GothicPopUp( "", VariantNormal);\r
2509 \r
2510 \r
2511 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2512 \r
2513   /* Load piece bitmaps for this board size */\r
2514   for (i=0; i<=2; i++) {\r
2515     for (piece = WhitePawn;\r
2516          (int) piece < (int) BlackPawn;\r
2517          piece = (ChessSquare) ((int) piece + 1)) {\r
2518       if (pieceBitmap[i][piece] != NULL)\r
2519         DeleteObject(pieceBitmap[i][piece]);\r
2520     }\r
2521   }\r
2522 \r
2523   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2524   // Orthodox Chess pieces\r
2525   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2526   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2527   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2528   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2529   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2530   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2531   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2532   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2533   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2534   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2535   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2536   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2537   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2538   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2539   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2540   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2541     // in Shogi, Hijack the unused Queen for Lance\r
2542     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2543     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2544     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2545   } else {\r
2546     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2547     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2548     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2549   }\r
2550 \r
2551   if(squareSize <= 72 && squareSize >= 33) { \r
2552     /* A & C are available in most sizes now */\r
2553     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2554       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2555       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2556       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2557       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2558       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2559       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2560       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2561       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2562       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2563       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2564       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2565       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2566     } else { // Smirf-like\r
2567       if(gameInfo.variant == VariantSChess) {\r
2568         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2569         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2570         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2571       } else {\r
2572         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2573         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2574         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2575       }\r
2576     }\r
2577     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2578       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2579       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2580       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2581     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2582       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2583       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2584       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2585     } else { // WinBoard standard\r
2586       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2587       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2588       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2589     }\r
2590   }\r
2591 \r
2592 \r
2593   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2594     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2595     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2596     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2597     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2598     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2599     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2600     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2601     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2602     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2603     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2604     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2605     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2606     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2607     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2608     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2609     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2610     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2611     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2612     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2613     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2614     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2615     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2616     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2617     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2618     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2619     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2620     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2621     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2622     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2623     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2624 \r
2625     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2626       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2627       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2628       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2629       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2630       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2631       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2632       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2633       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2634       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2635       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2636       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2637       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2638     } else {\r
2639       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2640       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2641       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2642       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2643       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2644       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2645       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2646       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2647       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2648       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2649       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2650       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2651     }\r
2652 \r
2653   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2654     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2655     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2656     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2657     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2658     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2659     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2660     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2661     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2662     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2663     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2664     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2665     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2666     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2667     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2668   }\r
2669 \r
2670 \r
2671   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2672   /* special Shogi support in this size */\r
2673   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2674       for (piece = WhitePawn;\r
2675            (int) piece < (int) BlackPawn;\r
2676            piece = (ChessSquare) ((int) piece + 1)) {\r
2677         if (pieceBitmap[i][piece] != NULL)\r
2678           DeleteObject(pieceBitmap[i][piece]);\r
2679       }\r
2680     }\r
2681   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2682   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2683   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2684   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2685   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2686   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2687   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2688   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2689   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2690   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2691   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2692   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2693   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2694   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2695   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2696   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2697   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2698   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2699   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2700   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2701   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2702   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2703   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2704   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2705   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2706   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2707   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2708   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2709   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2710   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2711   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2712   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2713   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2714   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2715   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2716   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2717   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2718   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2719   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2720   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2721   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2722   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2723   minorSize = 0;\r
2724   }\r
2725 }\r
2726 \r
2727 HBITMAP\r
2728 PieceBitmap(ChessSquare p, int kind)\r
2729 {\r
2730   if ((int) p >= (int) BlackPawn)\r
2731     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2732 \r
2733   return pieceBitmap[kind][(int) p];\r
2734 }\r
2735 \r
2736 /***************************************************************/\r
2737 \r
2738 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2739 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2740 /*\r
2741 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2742 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2743 */\r
2744 \r
2745 VOID\r
2746 SquareToPos(int row, int column, int * x, int * y)\r
2747 {\r
2748   if (flipView) {\r
2749     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2750     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2751   } else {\r
2752     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2753     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2754   }\r
2755 }\r
2756 \r
2757 VOID\r
2758 DrawCoordsOnDC(HDC hdc)\r
2759 {\r
2760   static char files[] = "0123456789012345678901221098765432109876543210";\r
2761   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2762   char str[2] = { NULLCHAR, NULLCHAR };\r
2763   int oldMode, oldAlign, x, y, start, i;\r
2764   HFONT oldFont;\r
2765   HBRUSH oldBrush;\r
2766 \r
2767   if (!appData.showCoords)\r
2768     return;\r
2769 \r
2770   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2771 \r
2772   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2773   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2774   oldAlign = GetTextAlign(hdc);\r
2775   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2776 \r
2777   y = boardRect.top + lineGap;\r
2778   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2779 \r
2780   if(border) {\r
2781     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2782     x += border - lineGap - 4; y += squareSize - 6;\r
2783   } else\r
2784   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2785   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2786     str[0] = files[start + i];\r
2787     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2788     y += squareSize + lineGap;\r
2789   }\r
2790 \r
2791   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2792 \r
2793   if(border) {\r
2794     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2795     x += -border + 4; y += border - squareSize + 6;\r
2796   } else\r
2797   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2798   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2799     str[0] = ranks[start + i];\r
2800     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2801     x += squareSize + lineGap;\r
2802   }    \r
2803 \r
2804   SelectObject(hdc, oldBrush);\r
2805   SetBkMode(hdc, oldMode);\r
2806   SetTextAlign(hdc, oldAlign);\r
2807   SelectObject(hdc, oldFont);\r
2808 }\r
2809 \r
2810 VOID\r
2811 DrawGridOnDC(HDC hdc)\r
2812 {\r
2813   HPEN oldPen;\r
2814  \r
2815   if (lineGap != 0) {\r
2816     oldPen = SelectObject(hdc, gridPen);\r
2817     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2818     SelectObject(hdc, oldPen);\r
2819   }\r
2820 }\r
2821 \r
2822 #define HIGHLIGHT_PEN 0\r
2823 #define PREMOVE_PEN   1\r
2824 \r
2825 VOID\r
2826 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2827 {\r
2828   int x1, y1;\r
2829   HPEN oldPen, hPen;\r
2830   if (lineGap == 0) return;\r
2831   if (flipView) {\r
2832     x1 = boardRect.left +\r
2833       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2834     y1 = boardRect.top +\r
2835       lineGap/2 + y * (squareSize + lineGap) + border;\r
2836   } else {\r
2837     x1 = boardRect.left +\r
2838       lineGap/2 + x * (squareSize + lineGap) + border;\r
2839     y1 = boardRect.top +\r
2840       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2841   }\r
2842   hPen = pen ? premovePen : highlightPen;\r
2843   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2844   MoveToEx(hdc, x1, y1, NULL);\r
2845   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2846   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2847   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2848   LineTo(hdc, x1, y1);\r
2849   SelectObject(hdc, oldPen);\r
2850 }\r
2851 \r
2852 VOID\r
2853 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2854 {\r
2855   int i;\r
2856   for (i=0; i<2; i++) {\r
2857     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2858       DrawHighlightOnDC(hdc, TRUE,\r
2859                         h->sq[i].x, h->sq[i].y,\r
2860                         pen);\r
2861   }\r
2862 }\r
2863 \r
2864 /* Note: sqcolor is used only in monoMode */\r
2865 /* Note that this code is largely duplicated in woptions.c,\r
2866    function DrawSampleSquare, so that needs to be updated too */\r
2867 VOID\r
2868 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2869 {\r
2870   HBITMAP oldBitmap;\r
2871   HBRUSH oldBrush;\r
2872   int tmpSize;\r
2873 \r
2874   if (appData.blindfold) return;\r
2875 \r
2876   /* [AS] Use font-based pieces if needed */\r
2877   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2878     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2879     CreatePiecesFromFont();\r
2880 \r
2881     if( fontBitmapSquareSize == squareSize ) {\r
2882         int index = TranslatePieceToFontPiece(piece);\r
2883 \r
2884         SelectObject( tmphdc, hPieceMask[ index ] );\r
2885 \r
2886       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2887         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2888       else\r
2889         BitBlt( hdc,\r
2890             x, y,\r
2891             squareSize, squareSize,\r
2892             tmphdc,\r
2893             0, 0,\r
2894             SRCAND );\r
2895 \r
2896         SelectObject( tmphdc, hPieceFace[ index ] );\r
2897 \r
2898       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2899         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2900       else\r
2901         BitBlt( hdc,\r
2902             x, y,\r
2903             squareSize, squareSize,\r
2904             tmphdc,\r
2905             0, 0,\r
2906             SRCPAINT );\r
2907 \r
2908         return;\r
2909     }\r
2910   }\r
2911 \r
2912   if (appData.monoMode) {\r
2913     SelectObject(tmphdc, PieceBitmap(piece, \r
2914       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2915     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2916            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2917   } else {\r
2918     HBRUSH xBrush = whitePieceBrush;\r
2919     tmpSize = squareSize;\r
2920     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2921     if(minorSize &&\r
2922         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2923          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2924       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2925       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2926       x += (squareSize - minorSize)>>1;\r
2927       y += squareSize - minorSize - 2;\r
2928       tmpSize = minorSize;\r
2929     }\r
2930     if (color || appData.allWhite ) {\r
2931       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2932       if( color )\r
2933               oldBrush = SelectObject(hdc, xBrush);\r
2934       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2935       if(appData.upsideDown && color==flipView)\r
2936         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2937       else\r
2938         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2939       /* Use black for outline of white pieces */\r
2940       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2941       if(appData.upsideDown && color==flipView)\r
2942         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2943       else\r
2944         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2945     } else if(appData.pieceDirectory[0]) {\r
2946       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2947       oldBrush = SelectObject(hdc, xBrush);\r
2948       if(appData.upsideDown && color==flipView)\r
2949         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2950       else\r
2951         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2952       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2953       if(appData.upsideDown && color==flipView)\r
2954         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2955       else\r
2956         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2957     } else {\r
2958       /* Use square color for details of black pieces */\r
2959       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2960       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2961       if(appData.upsideDown && !flipView)\r
2962         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2963       else\r
2964         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2965     }\r
2966     SelectObject(hdc, oldBrush);\r
2967     SelectObject(tmphdc, oldBitmap);\r
2968   }\r
2969 }\r
2970 \r
2971 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2972 int GetBackTextureMode( int algo )\r
2973 {\r
2974     int result = BACK_TEXTURE_MODE_DISABLED;\r
2975 \r
2976     switch( algo ) \r
2977     {\r
2978         case BACK_TEXTURE_MODE_PLAIN:\r
2979             result = 1; /* Always use identity map */\r
2980             break;\r
2981         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2982             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2983             break;\r
2984     }\r
2985 \r
2986     return result;\r
2987 }\r
2988 \r
2989 /* \r
2990     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2991     to handle redraws cleanly (as random numbers would always be different).\r
2992 */\r
2993 VOID RebuildTextureSquareInfo()\r
2994 {\r
2995     BITMAP bi;\r
2996     int lite_w = 0;\r
2997     int lite_h = 0;\r
2998     int dark_w = 0;\r
2999     int dark_h = 0;\r
3000     int row;\r
3001     int col;\r
3002 \r
3003     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3004 \r
3005     if( liteBackTexture != NULL ) {\r
3006         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3007             lite_w = bi.bmWidth;\r
3008             lite_h = bi.bmHeight;\r
3009         }\r
3010     }\r
3011 \r
3012     if( darkBackTexture != NULL ) {\r
3013         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3014             dark_w = bi.bmWidth;\r
3015             dark_h = bi.bmHeight;\r
3016         }\r
3017     }\r
3018 \r
3019     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3020         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3021             if( (col + row) & 1 ) {\r
3022                 /* Lite square */\r
3023                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3024                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3025                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3026                   else\r
3027                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3028                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3029                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3030                   else\r
3031                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3032                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3033                 }\r
3034             }\r
3035             else {\r
3036                 /* Dark square */\r
3037                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3038                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3039                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3040                   else\r
3041                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3042                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3043                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3044                   else\r
3045                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3046                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3047                 }\r
3048             }\r
3049         }\r
3050     }\r
3051 }\r
3052 \r
3053 /* [AS] Arrow highlighting support */\r
3054 \r
3055 static double A_WIDTH = 5; /* Width of arrow body */\r
3056 \r
3057 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3058 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3059 \r
3060 static double Sqr( double x )\r
3061 {\r
3062     return x*x;\r
3063 }\r
3064 \r
3065 static int Round( double x )\r
3066 {\r
3067     return (int) (x + 0.5);\r
3068 }\r
3069 \r
3070 /* Draw an arrow between two points using current settings */\r
3071 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3072 {\r
3073     POINT arrow[7];\r
3074     double dx, dy, j, k, x, y;\r
3075 \r
3076     if( d_x == s_x ) {\r
3077         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3078 \r
3079         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3080         arrow[0].y = s_y;\r
3081 \r
3082         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3083         arrow[1].y = d_y - h;\r
3084 \r
3085         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3086         arrow[2].y = d_y - h;\r
3087 \r
3088         arrow[3].x = d_x;\r
3089         arrow[3].y = d_y;\r
3090 \r
3091         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3092         arrow[5].y = d_y - h;\r
3093 \r
3094         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3095         arrow[4].y = d_y - h;\r
3096 \r
3097         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3098         arrow[6].y = s_y;\r
3099     }\r
3100     else if( d_y == s_y ) {\r
3101         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3102 \r
3103         arrow[0].x = s_x;\r
3104         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3105 \r
3106         arrow[1].x = d_x - w;\r
3107         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3108 \r
3109         arrow[2].x = d_x - w;\r
3110         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3111 \r
3112         arrow[3].x = d_x;\r
3113         arrow[3].y = d_y;\r
3114 \r
3115         arrow[5].x = d_x - w;\r
3116         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3117 \r
3118         arrow[4].x = d_x - w;\r
3119         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3120 \r
3121         arrow[6].x = s_x;\r
3122         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3123     }\r
3124     else {\r
3125         /* [AS] Needed a lot of paper for this! :-) */\r
3126         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3127         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3128   \r
3129         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3130 \r
3131         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3132 \r
3133         x = s_x;\r
3134         y = s_y;\r
3135 \r
3136         arrow[0].x = Round(x - j);\r
3137         arrow[0].y = Round(y + j*dx);\r
3138 \r
3139         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3140         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3141 \r
3142         if( d_x > s_x ) {\r
3143             x = (double) d_x - k;\r
3144             y = (double) d_y - k*dy;\r
3145         }\r
3146         else {\r
3147             x = (double) d_x + k;\r
3148             y = (double) d_y + k*dy;\r
3149         }\r
3150 \r
3151         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3152 \r
3153         arrow[6].x = Round(x - j);\r
3154         arrow[6].y = Round(y + j*dx);\r
3155 \r
3156         arrow[2].x = Round(arrow[6].x + 2*j);\r
3157         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3158 \r
3159         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3160         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3161 \r
3162         arrow[4].x = d_x;\r
3163         arrow[4].y = d_y;\r
3164 \r
3165         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3166         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3167     }\r
3168 \r
3169     Polygon( hdc, arrow, 7 );\r
3170 }\r
3171 \r
3172 /* [AS] Draw an arrow between two squares */\r
3173 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3174 {\r
3175     int s_x, s_y, d_x, d_y;\r
3176     HPEN hpen;\r
3177     HPEN holdpen;\r
3178     HBRUSH hbrush;\r
3179     HBRUSH holdbrush;\r
3180     LOGBRUSH stLB;\r
3181 \r
3182     if( s_col == d_col && s_row == d_row ) {\r
3183         return;\r
3184     }\r
3185 \r
3186     /* Get source and destination points */\r
3187     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3188     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3189 \r
3190     if( d_y > s_y ) {\r
3191         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3192     }\r
3193     else if( d_y < s_y ) {\r
3194         d_y += squareSize / 2 + squareSize / 4;\r
3195     }\r
3196     else {\r
3197         d_y += squareSize / 2;\r
3198     }\r
3199 \r
3200     if( d_x > s_x ) {\r
3201         d_x += squareSize / 2 - squareSize / 4;\r
3202     }\r
3203     else if( d_x < s_x ) {\r
3204         d_x += squareSize / 2 + squareSize / 4;\r
3205     }\r
3206     else {\r
3207         d_x += squareSize / 2;\r
3208     }\r
3209 \r
3210     s_x += squareSize / 2;\r
3211     s_y += squareSize / 2;\r
3212 \r
3213     /* Adjust width */\r
3214     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3215 \r
3216     /* Draw */\r
3217     stLB.lbStyle = BS_SOLID;\r
3218     stLB.lbColor = appData.highlightArrowColor;\r
3219     stLB.lbHatch = 0;\r
3220 \r
3221     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3222     holdpen = SelectObject( hdc, hpen );\r
3223     hbrush = CreateBrushIndirect( &stLB );\r
3224     holdbrush = SelectObject( hdc, hbrush );\r
3225 \r
3226     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3227 \r
3228     SelectObject( hdc, holdpen );\r
3229     SelectObject( hdc, holdbrush );\r
3230     DeleteObject( hpen );\r
3231     DeleteObject( hbrush );\r
3232 }\r
3233 \r
3234 BOOL HasHighlightInfo()\r
3235 {\r
3236     BOOL result = FALSE;\r
3237 \r
3238     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3239         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3240     {\r
3241         result = TRUE;\r
3242     }\r
3243 \r
3244     return result;\r
3245 }\r
3246 \r
3247 BOOL IsDrawArrowEnabled()\r
3248 {\r
3249     BOOL result = FALSE;\r
3250 \r
3251     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3252         result = TRUE;\r
3253     }\r
3254 \r
3255     return result;\r
3256 }\r
3257 \r
3258 VOID DrawArrowHighlight( HDC hdc )\r
3259 {\r
3260     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3261         DrawArrowBetweenSquares( hdc,\r
3262             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3263             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3264     }\r
3265 }\r
3266 \r
3267 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3268 {\r
3269     HRGN result = NULL;\r
3270 \r
3271     if( HasHighlightInfo() ) {\r
3272         int x1, y1, x2, y2;\r
3273         int sx, sy, dx, dy;\r
3274 \r
3275         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3276         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3277 \r
3278         sx = MIN( x1, x2 );\r
3279         sy = MIN( y1, y2 );\r
3280         dx = MAX( x1, x2 ) + squareSize;\r
3281         dy = MAX( y1, y2 ) + squareSize;\r
3282 \r
3283         result = CreateRectRgn( sx, sy, dx, dy );\r
3284     }\r
3285 \r
3286     return result;\r
3287 }\r
3288 \r
3289 /*\r
3290     Warning: this function modifies the behavior of several other functions. \r
3291     \r
3292     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3293     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3294     repaint is scattered all over the place, which is not good for features such as\r
3295     "arrow highlighting" that require a full repaint of the board.\r
3296 \r
3297     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3298     user interaction, when speed is not so important) but especially to avoid errors\r
3299     in the displayed graphics.\r
3300 \r
3301     In such patched places, I always try refer to this function so there is a single\r
3302     place to maintain knowledge.\r
3303     \r
3304     To restore the original behavior, just return FALSE unconditionally.\r
3305 */\r
3306 BOOL IsFullRepaintPreferrable()\r
3307 {\r
3308     BOOL result = FALSE;\r
3309 \r
3310     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3311         /* Arrow may appear on the board */\r
3312         result = TRUE;\r
3313     }\r
3314 \r
3315     return result;\r
3316 }\r
3317 \r
3318 /* \r
3319     This function is called by DrawPosition to know whether a full repaint must\r
3320     be forced or not.\r
3321 \r
3322     Only DrawPosition may directly call this function, which makes use of \r
3323     some state information. Other function should call DrawPosition specifying \r
3324     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3325 */\r
3326 BOOL DrawPositionNeedsFullRepaint()\r
3327 {\r
3328     BOOL result = FALSE;\r
3329 \r
3330     /* \r
3331         Probably a slightly better policy would be to trigger a full repaint\r
3332         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3333         but animation is fast enough that it's difficult to notice.\r
3334     */\r
3335     if( animInfo.piece == EmptySquare ) {\r
3336         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3337             result = TRUE;\r
3338         }\r
3339     }\r
3340 \r
3341     return result;\r
3342 }\r
3343 \r
3344 static HBITMAP borderBitmap;\r
3345 \r
3346 VOID\r
3347 DrawBackgroundOnDC(HDC hdc)\r
3348 {\r
3349   \r
3350   BITMAP bi;\r
3351   HDC tmphdc;\r
3352   HBITMAP hbm;\r
3353   static char oldBorder[MSG_SIZ];\r
3354   int w = 600, h = 600;\r
3355 \r
3356   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3357     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3358     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3359   }\r
3360   if(borderBitmap == NULL) { // loading failed, use white\r
3361     FillRect( hdc, &boardRect, whitePieceBrush );\r
3362     return;\r
3363   }\r
3364   tmphdc = CreateCompatibleDC(hdc);\r
3365   hbm = SelectObject(tmphdc, borderBitmap);\r
3366   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3367             w = bi.bmWidth;\r
3368             h = bi.bmHeight;\r
3369   }\r
3370   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3371                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3372   SelectObject(tmphdc, hbm);\r
3373   DeleteDC(tmphdc);\r
3374 }\r
3375 \r
3376 VOID\r
3377 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3378 {\r
3379   int row, column, x, y, square_color, piece_color;\r
3380   ChessSquare piece;\r
3381   HBRUSH oldBrush;\r
3382   HDC texture_hdc = NULL;\r
3383 \r
3384   /* [AS] Initialize background textures if needed */\r
3385   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3386       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3387       if( backTextureSquareSize != squareSize \r
3388        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3389           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3390           backTextureSquareSize = squareSize;\r
3391           RebuildTextureSquareInfo();\r
3392       }\r
3393 \r
3394       texture_hdc = CreateCompatibleDC( hdc );\r
3395   }\r
3396 \r
3397   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3398     for (column = 0; column < BOARD_WIDTH; column++) {\r
3399   \r
3400       SquareToPos(row, column, &x, &y);\r
3401 \r
3402       piece = board[row][column];\r
3403 \r
3404       square_color = ((column + row) % 2) == 1;\r
3405       if( gameInfo.variant == VariantXiangqi ) {\r
3406           square_color = !InPalace(row, column);\r
3407           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3408           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3409       }\r
3410       piece_color = (int) piece < (int) BlackPawn;\r
3411 \r
3412 \r
3413       /* [HGM] holdings file: light square or black */\r
3414       if(column == BOARD_LEFT-2) {\r
3415             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3416                 square_color = 1;\r
3417             else {\r
3418                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3419                 continue;\r
3420             }\r
3421       } else\r
3422       if(column == BOARD_RGHT + 1 ) {\r
3423             if( row < gameInfo.holdingsSize )\r
3424                 square_color = 1;\r
3425             else {\r
3426                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3427                 continue;\r
3428             }\r
3429       }\r
3430       if(column == BOARD_LEFT-1 ) /* left align */\r
3431             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3432       else if( column == BOARD_RGHT) /* right align */\r
3433             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3434       else\r
3435       if (appData.monoMode) {\r
3436         if (piece == EmptySquare) {\r
3437           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3438                  square_color ? WHITENESS : BLACKNESS);\r
3439         } else {\r
3440           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3441         }\r
3442       } \r
3443       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3444           /* [AS] Draw the square using a texture bitmap */\r
3445           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3446           int r = row, c = column; // [HGM] do not flip board in flipView\r
3447           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3448 \r
3449           DrawTile( x, y, \r
3450               squareSize, squareSize, \r
3451               hdc, \r
3452               texture_hdc,\r
3453               backTextureSquareInfo[r][c].mode,\r
3454               backTextureSquareInfo[r][c].x,\r
3455               backTextureSquareInfo[r][c].y );\r
3456 \r
3457           SelectObject( texture_hdc, hbm );\r
3458 \r
3459           if (piece != EmptySquare) {\r
3460               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3461           }\r
3462       }\r
3463       else {\r
3464         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3465 \r
3466         oldBrush = SelectObject(hdc, brush );\r
3467         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3468         SelectObject(hdc, oldBrush);\r
3469         if (piece != EmptySquare)\r
3470           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3471       }\r
3472     }\r
3473   }\r
3474 \r
3475   if( texture_hdc != NULL ) {\r
3476     DeleteDC( texture_hdc );\r
3477   }\r
3478 }\r
3479 \r
3480 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3481 void fputDW(FILE *f, int x)\r
3482 {\r
3483         fputc(x     & 255, f);\r
3484         fputc(x>>8  & 255, f);\r
3485         fputc(x>>16 & 255, f);\r
3486         fputc(x>>24 & 255, f);\r
3487 }\r
3488 \r
3489 #define MAX_CLIPS 200   /* more than enough */\r
3490 \r
3491 VOID\r
3492 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3493 {\r
3494 //  HBITMAP bufferBitmap;\r
3495   BITMAP bi;\r
3496 //  RECT Rect;\r
3497   HDC tmphdc;\r
3498   HBITMAP hbm;\r
3499   int w = 100, h = 50;\r
3500 \r
3501   if(logo == NULL) {\r
3502     if(!logoHeight) return;\r
3503     FillRect( hdc, &logoRect, whitePieceBrush );\r
3504   }\r
3505 //  GetClientRect(hwndMain, &Rect);\r
3506 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3507 //                                      Rect.bottom-Rect.top+1);\r
3508   tmphdc = CreateCompatibleDC(hdc);\r
3509   hbm = SelectObject(tmphdc, logo);\r
3510   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3511             w = bi.bmWidth;\r
3512             h = bi.bmHeight;\r
3513   }\r
3514   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3515                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3516   SelectObject(tmphdc, hbm);\r
3517   DeleteDC(tmphdc);\r
3518 }\r
3519 \r
3520 VOID\r
3521 DisplayLogos()\r
3522 {\r
3523   if(logoHeight) {\r
3524         HDC hdc = GetDC(hwndMain);\r
3525         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3526         if(appData.autoLogo) {\r
3527           \r
3528           switch(gameMode) { // pick logos based on game mode\r
3529             case IcsObserving:\r
3530                 whiteLogo = second.programLogo; // ICS logo\r
3531                 blackLogo = second.programLogo;\r
3532             default:\r
3533                 break;\r
3534             case IcsPlayingWhite:\r
3535                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3536                 blackLogo = second.programLogo; // ICS logo\r
3537                 break;\r
3538             case IcsPlayingBlack:\r
3539                 whiteLogo = second.programLogo; // ICS logo\r
3540                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3541                 break;\r
3542             case TwoMachinesPlay:\r
3543                 if(first.twoMachinesColor[0] == 'b') {\r
3544                     whiteLogo = second.programLogo;\r
3545                     blackLogo = first.programLogo;\r
3546                 }\r
3547                 break;\r
3548             case MachinePlaysWhite:\r
3549                 blackLogo = userLogo;\r
3550                 break;\r
3551             case MachinePlaysBlack:\r
3552                 whiteLogo = userLogo;\r
3553                 blackLogo = first.programLogo;\r
3554           }\r
3555         }\r
3556         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3557         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3558         ReleaseDC(hwndMain, hdc);\r
3559   }\r
3560 }\r
3561 \r
3562 void\r
3563 UpdateLogos(int display)\r
3564 { // called after loading new engine(s), in tourney or from menu\r
3565   LoadLogo(&first, 0, FALSE);\r
3566   LoadLogo(&second, 1, appData.icsActive);\r
3567   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3568   if(display) DisplayLogos();\r
3569 }\r
3570 \r
3571 static HDC hdcSeek;\r
3572 \r
3573 // [HGM] seekgraph\r
3574 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3575 {\r
3576     POINT stPt;\r
3577     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3578     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3579     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3580     SelectObject( hdcSeek, hp );\r
3581 }\r
3582 \r
3583 // front-end wrapper for drawing functions to do rectangles\r
3584 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3585 {\r
3586     HPEN hp;\r
3587     RECT rc;\r
3588 \r
3589     if (hdcSeek == NULL) {\r
3590     hdcSeek = GetDC(hwndMain);\r
3591       if (!appData.monoMode) {\r
3592         SelectPalette(hdcSeek, hPal, FALSE);\r
3593         RealizePalette(hdcSeek);\r
3594       }\r
3595     }\r
3596     hp = SelectObject( hdcSeek, gridPen );\r
3597     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3598     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3599     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3600     SelectObject( hdcSeek, hp );\r
3601 }\r
3602 \r
3603 // front-end wrapper for putting text in graph\r
3604 void DrawSeekText(char *buf, int x, int y)\r
3605 {\r
3606         SIZE stSize;\r
3607         SetBkMode( hdcSeek, TRANSPARENT );\r
3608         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3609         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3610 }\r
3611 \r
3612 void DrawSeekDot(int x, int y, int color)\r
3613 {\r
3614         int square = color & 0x80;\r
3615         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3616                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3617         color &= 0x7F;\r
3618         if(square)\r
3619             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3620                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3621         else\r
3622             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3623                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3624             SelectObject(hdcSeek, oldBrush);\r
3625 }\r
3626 \r
3627 void DrawSeekOpen()\r
3628 {\r
3629 }\r
3630 \r
3631 void DrawSeekClose()\r
3632 {\r
3633 }\r
3634 \r
3635 VOID\r
3636 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3637 {\r
3638   static Board lastReq[2], lastDrawn[2];\r
3639   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3640   static int lastDrawnFlipView = 0;\r
3641   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3642   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3643   HDC tmphdc;\r
3644   HDC hdcmem;\r
3645   HBITMAP bufferBitmap;\r
3646   HBITMAP oldBitmap;\r
3647   RECT Rect;\r
3648   HRGN clips[MAX_CLIPS];\r
3649   ChessSquare dragged_piece = EmptySquare;\r
3650   int nr = twoBoards*partnerUp;\r
3651 \r
3652   /* I'm undecided on this - this function figures out whether a full\r
3653    * repaint is necessary on its own, so there's no real reason to have the\r
3654    * caller tell it that.  I think this can safely be set to FALSE - but\r
3655    * if we trust the callers not to request full repaints unnessesarily, then\r
3656    * we could skip some clipping work.  In other words, only request a full\r
3657    * redraw when the majority of pieces have changed positions (ie. flip, \r
3658    * gamestart and similar)  --Hawk\r
3659    */\r
3660   Boolean fullrepaint = repaint;\r
3661 \r
3662   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3663 \r
3664   if( DrawPositionNeedsFullRepaint() ) {\r
3665       fullrepaint = TRUE;\r
3666   }\r
3667 \r
3668   if (board == NULL) {\r
3669     if (!lastReqValid[nr]) {\r
3670       return;\r
3671     }\r
3672     board = lastReq[nr];\r
3673   } else {\r
3674     CopyBoard(lastReq[nr], board);\r
3675     lastReqValid[nr] = 1;\r
3676   }\r
3677 \r
3678   if (doingSizing) {\r
3679     return;\r
3680   }\r
3681 \r
3682   if (IsIconic(hwndMain)) {\r
3683     return;\r
3684   }\r
3685 \r
3686   if (hdc == NULL) {\r
3687     hdc = GetDC(hwndMain);\r
3688     if (!appData.monoMode) {\r
3689       SelectPalette(hdc, hPal, FALSE);\r
3690       RealizePalette(hdc);\r
3691     }\r
3692     releaseDC = TRUE;\r
3693   } else {\r
3694     releaseDC = FALSE;\r
3695   }\r
3696 \r
3697   /* Create some work-DCs */\r
3698   hdcmem = CreateCompatibleDC(hdc);\r
3699   tmphdc = CreateCompatibleDC(hdc);\r
3700 \r
3701   /* If dragging is in progress, we temporarely remove the piece */\r
3702   /* [HGM] or temporarily decrease count if stacked              */\r
3703   /*       !! Moved to before board compare !!                   */\r
3704   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3705     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3706     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3707             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3708         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3709     } else \r
3710     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3711             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3712         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3713     } else \r
3714         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3715   }\r
3716 \r
3717   /* Figure out which squares need updating by comparing the \r
3718    * newest board with the last drawn board and checking if\r
3719    * flipping has changed.\r
3720    */\r
3721   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3722     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3723       for (column = 0; column < BOARD_WIDTH; column++) {\r
3724         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3725           SquareToPos(row, column, &x, &y);\r
3726           clips[num_clips++] =\r
3727             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3728         }\r
3729       }\r
3730     }\r
3731    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3732     for (i=0; i<2; i++) {\r
3733       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3734           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3735         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3736             lastDrawnHighlight.sq[i].y >= 0) {\r
3737           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3738                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3739           clips[num_clips++] =\r
3740             CreateRectRgn(x - lineGap, y - lineGap, \r
3741                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3742         }\r
3743         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3744           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3745           clips[num_clips++] =\r
3746             CreateRectRgn(x - lineGap, y - lineGap, \r
3747                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3748         }\r
3749       }\r
3750     }\r
3751     for (i=0; i<2; i++) {\r
3752       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3753           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3754         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3755             lastDrawnPremove.sq[i].y >= 0) {\r
3756           SquareToPos(lastDrawnPremove.sq[i].y,\r
3757                       lastDrawnPremove.sq[i].x, &x, &y);\r
3758           clips[num_clips++] =\r
3759             CreateRectRgn(x - lineGap, y - lineGap, \r
3760                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3761         }\r
3762         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3763             premoveHighlightInfo.sq[i].y >= 0) {\r
3764           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3765                       premoveHighlightInfo.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       }\r
3771     }\r
3772    } else { // nr == 1\r
3773         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3774         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3775         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3776         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3777       for (i=0; i<2; i++) {\r
3778         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3779             partnerHighlightInfo.sq[i].y >= 0) {\r
3780           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3781                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3782           clips[num_clips++] =\r
3783             CreateRectRgn(x - lineGap, y - lineGap, \r
3784                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3785         }\r
3786         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3787             oldPartnerHighlight.sq[i].y >= 0) {\r
3788           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3789                       oldPartnerHighlight.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       }\r
3795    }\r
3796   } else {\r
3797     fullrepaint = TRUE;\r
3798   }\r
3799 \r
3800   /* Create a buffer bitmap - this is the actual bitmap\r
3801    * being written to.  When all the work is done, we can\r
3802    * copy it to the real DC (the screen).  This avoids\r
3803    * the problems with flickering.\r
3804    */\r
3805   GetClientRect(hwndMain, &Rect);\r
3806   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3807                                         Rect.bottom-Rect.top+1);\r
3808   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3809   if (!appData.monoMode) {\r
3810     SelectPalette(hdcmem, hPal, FALSE);\r
3811   }\r
3812 \r
3813   /* Create clips for dragging */\r
3814   if (!fullrepaint) {\r
3815     if (dragInfo.from.x >= 0) {\r
3816       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3817       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3818     }\r
3819     if (dragInfo.start.x >= 0) {\r
3820       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3821       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3822     }\r
3823     if (dragInfo.pos.x >= 0) {\r
3824       x = dragInfo.pos.x - squareSize / 2;\r
3825       y = dragInfo.pos.y - squareSize / 2;\r
3826       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3827     }\r
3828     if (dragInfo.lastpos.x >= 0) {\r
3829       x = dragInfo.lastpos.x - squareSize / 2;\r
3830       y = dragInfo.lastpos.y - squareSize / 2;\r
3831       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3832     }\r
3833   }\r
3834 \r
3835   /* Are we animating a move?  \r
3836    * If so, \r
3837    *   - remove the piece from the board (temporarely)\r
3838    *   - calculate the clipping region\r
3839    */\r
3840   if (!fullrepaint) {\r
3841     if (animInfo.piece != EmptySquare) {\r
3842       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3843       x = boardRect.left + animInfo.lastpos.x;\r
3844       y = boardRect.top + animInfo.lastpos.y;\r
3845       x2 = boardRect.left + animInfo.pos.x;\r
3846       y2 = boardRect.top + animInfo.pos.y;\r
3847       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3848       /* Slight kludge.  The real problem is that after AnimateMove is\r
3849          done, the position on the screen does not match lastDrawn.\r
3850          This currently causes trouble only on e.p. captures in\r
3851          atomic, where the piece moves to an empty square and then\r
3852          explodes.  The old and new positions both had an empty square\r
3853          at the destination, but animation has drawn a piece there and\r
3854          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3855       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3856     }\r
3857   }\r
3858 \r
3859   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3860   if (num_clips == 0)\r
3861     fullrepaint = TRUE;\r
3862 \r
3863   /* Set clipping on the memory DC */\r
3864   if (!fullrepaint) {\r
3865     SelectClipRgn(hdcmem, clips[0]);\r
3866     for (x = 1; x < num_clips; x++) {\r
3867       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3868         abort();  // this should never ever happen!\r
3869     }\r
3870   }\r
3871 \r
3872   /* Do all the drawing to the memory DC */\r
3873   if(explodeInfo.radius) { // [HGM] atomic\r
3874         HBRUSH oldBrush;\r
3875         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3876         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3877         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3878         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3879         x += squareSize/2;\r
3880         y += squareSize/2;\r
3881         if(!fullrepaint) {\r
3882           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3883           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3884         }\r
3885         DrawGridOnDC(hdcmem);\r
3886         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3887         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3888         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3889         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3890         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3891         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3892         SelectObject(hdcmem, oldBrush);\r
3893   } else {\r
3894     if(border) DrawBackgroundOnDC(hdcmem);\r
3895     DrawGridOnDC(hdcmem);\r
3896     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3897         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3898         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3899     } else {\r
3900         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3901         oldPartnerHighlight = partnerHighlightInfo;\r
3902     }\r
3903     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3904   }\r
3905   if(nr == 0) // [HGM] dual: markers only on left board\r
3906   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3907     for (column = 0; column < BOARD_WIDTH; column++) {\r
3908         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3909             HBRUSH oldBrush = SelectObject(hdcmem, \r
3910                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3911             SquareToPos(row, column, &x, &y);\r
3912             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3913                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3914             SelectObject(hdcmem, oldBrush);\r
3915         }\r
3916     }\r
3917   }\r
3918 \r
3919   if( appData.highlightMoveWithArrow ) {\r
3920     DrawArrowHighlight(hdcmem);\r
3921   }\r
3922 \r
3923   DrawCoordsOnDC(hdcmem);\r
3924 \r
3925   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3926                  /* to make sure lastDrawn contains what is actually drawn */\r
3927 \r
3928   /* Put the dragged piece back into place and draw it (out of place!) */\r
3929     if (dragged_piece != EmptySquare) {\r
3930     /* [HGM] or restack */\r
3931     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3932                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3933     else\r
3934     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3935                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3936     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3937     x = dragInfo.pos.x - squareSize / 2;\r
3938     y = dragInfo.pos.y - squareSize / 2;\r
3939     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3940                   ((int) dragInfo.piece < (int) BlackPawn), \r
3941                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3942   }   \r
3943   \r
3944   /* Put the animated piece back into place and draw it */\r
3945   if (animInfo.piece != EmptySquare) {\r
3946     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3947     x = boardRect.left + animInfo.pos.x;\r
3948     y = boardRect.top + animInfo.pos.y;\r
3949     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3950                   ((int) animInfo.piece < (int) BlackPawn),\r
3951                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3952   }\r
3953 \r
3954   /* Release the bufferBitmap by selecting in the old bitmap \r
3955    * and delete the memory DC\r
3956    */\r
3957   SelectObject(hdcmem, oldBitmap);\r
3958   DeleteDC(hdcmem);\r
3959 \r
3960   /* Set clipping on the target DC */\r
3961   if (!fullrepaint) {\r
3962     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3963         RECT rect;\r
3964         GetRgnBox(clips[x], &rect);\r
3965         DeleteObject(clips[x]);\r
3966         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3967                           rect.right + wpMain.width/2, rect.bottom);\r
3968     }\r
3969     SelectClipRgn(hdc, clips[0]);\r
3970     for (x = 1; x < num_clips; x++) {\r
3971       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3972         abort();   // this should never ever happen!\r
3973     } \r
3974   }\r
3975 \r
3976   /* Copy the new bitmap onto the screen in one go.\r
3977    * This way we avoid any flickering\r
3978    */\r
3979   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3980   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3981          boardRect.right - boardRect.left,\r
3982          boardRect.bottom - boardRect.top,\r
3983          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3984   if(saveDiagFlag) { \r
3985     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3986     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3987 \r
3988     GetObject(bufferBitmap, sizeof(b), &b);\r
3989     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3990         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3991         bih.biWidth = b.bmWidth;\r
3992         bih.biHeight = b.bmHeight;\r
3993         bih.biPlanes = 1;\r
3994         bih.biBitCount = b.bmBitsPixel;\r
3995         bih.biCompression = 0;\r
3996         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3997         bih.biXPelsPerMeter = 0;\r
3998         bih.biYPelsPerMeter = 0;\r
3999         bih.biClrUsed = 0;\r
4000         bih.biClrImportant = 0;\r
4001 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4002 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4003         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4004 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4005 \r
4006         wb = b.bmWidthBytes;\r
4007         // count colors\r
4008         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4009                 int k = ((int*) pData)[i];\r
4010                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4011                 if(j >= 16) break;\r
4012                 color[j] = k;\r
4013                 if(j >= nrColors) nrColors = j+1;\r
4014         }\r
4015         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4016                 INT p = 0;\r
4017                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4018                     for(w=0; w<(wb>>2); w+=2) {\r
4019                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4020                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4021                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4022                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4023                         pData[p++] = m | j<<4;\r
4024                     }\r
4025                     while(p&3) pData[p++] = 0;\r
4026                 }\r
4027                 fac = 3;\r
4028                 wb = ((wb+31)>>5)<<2;\r
4029         }\r
4030         // write BITMAPFILEHEADER\r
4031         fprintf(diagFile, "BM");\r
4032         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4033         fputDW(diagFile, 0);\r
4034         fputDW(diagFile, 0x36 + (fac?64:0));\r
4035         // write BITMAPINFOHEADER\r
4036         fputDW(diagFile, 40);\r
4037         fputDW(diagFile, b.bmWidth);\r
4038         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4039         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4040         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4041         fputDW(diagFile, 0);\r
4042         fputDW(diagFile, 0);\r
4043         fputDW(diagFile, 0);\r
4044         fputDW(diagFile, 0);\r
4045         fputDW(diagFile, 0);\r
4046         fputDW(diagFile, 0);\r
4047         // write color table\r
4048         if(fac)\r
4049         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4050         // write bitmap data\r
4051         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4052                 fputc(pData[i], diagFile);\r
4053         free(pData);\r
4054      }\r
4055   }\r
4056 \r
4057   SelectObject(tmphdc, oldBitmap);\r
4058 \r
4059   /* Massive cleanup */\r
4060   for (x = 0; x < num_clips; x++)\r
4061     DeleteObject(clips[x]);\r
4062 \r
4063   DeleteDC(tmphdc);\r
4064   DeleteObject(bufferBitmap);\r
4065 \r
4066   if (releaseDC) \r
4067     ReleaseDC(hwndMain, hdc);\r
4068   \r
4069   if (lastDrawnFlipView != flipView && nr == 0) {\r
4070     if (flipView)\r
4071       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4072     else\r
4073       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4074   }\r
4075 \r
4076 /*  CopyBoard(lastDrawn, board);*/\r
4077   lastDrawnHighlight = highlightInfo;\r
4078   lastDrawnPremove   = premoveHighlightInfo;\r
4079   lastDrawnFlipView = flipView;\r
4080   lastDrawnValid[nr] = 1;\r
4081 }\r
4082 \r
4083 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4084 int\r
4085 SaveDiagram(f)\r
4086      FILE *f;\r
4087 {\r
4088     saveDiagFlag = 1; diagFile = f;\r
4089     HDCDrawPosition(NULL, TRUE, NULL);\r
4090     saveDiagFlag = 0;\r
4091 \r
4092     fclose(f);\r
4093     return TRUE;\r
4094 }\r
4095 \r
4096 \r
4097 /*---------------------------------------------------------------------------*\\r
4098 | CLIENT PAINT PROCEDURE\r
4099 |   This is the main event-handler for the WM_PAINT message.\r
4100 |\r
4101 \*---------------------------------------------------------------------------*/\r
4102 VOID\r
4103 PaintProc(HWND hwnd)\r
4104 {\r
4105   HDC         hdc;\r
4106   PAINTSTRUCT ps;\r
4107   HFONT       oldFont;\r
4108 \r
4109   if((hdc = BeginPaint(hwnd, &ps))) {\r
4110     if (IsIconic(hwnd)) {\r
4111       DrawIcon(hdc, 2, 2, iconCurrent);\r
4112     } else {\r
4113       if (!appData.monoMode) {\r
4114         SelectPalette(hdc, hPal, FALSE);\r
4115         RealizePalette(hdc);\r
4116       }\r
4117       HDCDrawPosition(hdc, 1, NULL);\r
4118       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4119         flipView = !flipView; partnerUp = !partnerUp;\r
4120         HDCDrawPosition(hdc, 1, NULL);\r
4121         flipView = !flipView; partnerUp = !partnerUp;\r
4122       }\r
4123       oldFont =\r
4124         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4125       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4126                  ETO_CLIPPED|ETO_OPAQUE,\r
4127                  &messageRect, messageText, strlen(messageText), NULL);\r
4128       SelectObject(hdc, oldFont);\r
4129       DisplayBothClocks();\r
4130       DisplayLogos();\r
4131     }\r
4132     EndPaint(hwnd,&ps);\r
4133   }\r
4134 \r
4135   return;\r
4136 }\r
4137 \r
4138 \r
4139 /*\r
4140  * If the user selects on a border boundary, return -1; if off the board,\r
4141  *   return -2.  Otherwise map the event coordinate to the square.\r
4142  * The offset boardRect.left or boardRect.top must already have been\r
4143  *   subtracted from x.\r
4144  */\r
4145 int EventToSquare(x, limit)\r
4146      int x, limit;\r
4147 {\r
4148   if (x <= border)\r
4149     return -2;\r
4150   if (x < lineGap + border)\r
4151     return -1;\r
4152   x -= lineGap + border;\r
4153   if ((x % (squareSize + lineGap)) >= squareSize)\r
4154     return -1;\r
4155   x /= (squareSize + lineGap);\r
4156     if (x >= limit)\r
4157     return -2;\r
4158   return x;\r
4159 }\r
4160 \r
4161 typedef struct {\r
4162   char piece;\r
4163   int command;\r
4164   char* name;\r
4165 } DropEnable;\r
4166 \r
4167 DropEnable dropEnables[] = {\r
4168   { 'P', DP_Pawn, N_("Pawn") },\r
4169   { 'N', DP_Knight, N_("Knight") },\r
4170   { 'B', DP_Bishop, N_("Bishop") },\r
4171   { 'R', DP_Rook, N_("Rook") },\r
4172   { 'Q', DP_Queen, N_("Queen") },\r
4173 };\r
4174 \r
4175 VOID\r
4176 SetupDropMenu(HMENU hmenu)\r
4177 {\r
4178   int i, count, enable;\r
4179   char *p;\r
4180   extern char white_holding[], black_holding[];\r
4181   char item[MSG_SIZ];\r
4182 \r
4183   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4184     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4185                dropEnables[i].piece);\r
4186     count = 0;\r
4187     while (p && *p++ == dropEnables[i].piece) count++;\r
4188       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4189     enable = count > 0 || !appData.testLegality\r
4190       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4191                       && !appData.icsActive);\r
4192     ModifyMenu(hmenu, dropEnables[i].command,\r
4193                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4194                dropEnables[i].command, item);\r
4195   }\r
4196 }\r
4197 \r
4198 void DragPieceBegin(int x, int y, Boolean instantly)\r
4199 {\r
4200       dragInfo.lastpos.x = boardRect.left + x;\r
4201       dragInfo.lastpos.y = boardRect.top + y;\r
4202       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4203       dragInfo.from.x = fromX;\r
4204       dragInfo.from.y = fromY;\r
4205       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4206       dragInfo.start = dragInfo.from;\r
4207       SetCapture(hwndMain);\r
4208 }\r
4209 \r
4210 void DragPieceEnd(int x, int y)\r
4211 {\r
4212     ReleaseCapture();\r
4213     dragInfo.start.x = dragInfo.start.y = -1;\r
4214     dragInfo.from = dragInfo.start;\r
4215     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4216 }\r
4217 \r
4218 void ChangeDragPiece(ChessSquare piece)\r
4219 {\r
4220     dragInfo.piece = piece;\r
4221 }\r
4222 \r
4223 /* Event handler for mouse messages */\r
4224 VOID\r
4225 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4226 {\r
4227   int x, y, menuNr;\r
4228   POINT pt;\r
4229   static int recursive = 0;\r
4230   HMENU hmenu;\r
4231   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4232 \r
4233   if (recursive) {\r
4234     if (message == WM_MBUTTONUP) {\r
4235       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4236          to the middle button: we simulate pressing the left button too!\r
4237          */\r
4238       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4239       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4240     }\r
4241     return;\r
4242   }\r
4243   recursive++;\r
4244   \r
4245   pt.x = LOWORD(lParam);\r
4246   pt.y = HIWORD(lParam);\r
4247   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4248   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4249   if (!flipView && y >= 0) {\r
4250     y = BOARD_HEIGHT - 1 - y;\r
4251   }\r
4252   if (flipView && x >= 0) {\r
4253     x = BOARD_WIDTH - 1 - x;\r
4254   }\r
4255 \r
4256   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4257 \r
4258   switch (message) {\r
4259   case WM_LBUTTONDOWN:\r
4260       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4261         ClockClick(flipClock); break;\r
4262       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4263         ClockClick(!flipClock); break;\r
4264       }\r
4265       dragInfo.start.x = dragInfo.start.y = -1;\r
4266       dragInfo.from = dragInfo.start;\r
4267     if(fromX == -1 && frozen) { // not sure where this is for\r
4268                 fromX = fromY = -1; \r
4269       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4270       break;\r
4271     }\r
4272       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4273       DrawPosition(TRUE, NULL);\r
4274     break;\r
4275 \r
4276   case WM_LBUTTONUP:\r
4277       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4278       DrawPosition(TRUE, NULL);\r
4279     break;\r
4280 \r
4281   case WM_MOUSEMOVE:\r
4282     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4283     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4284     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4285     if ((appData.animateDragging || appData.highlightDragging)\r
4286         && (wParam & MK_LBUTTON)\r
4287         && dragInfo.from.x >= 0) \r
4288     {\r
4289       BOOL full_repaint = FALSE;\r
4290 \r
4291       if (appData.animateDragging) {\r
4292         dragInfo.pos = pt;\r
4293       }\r
4294       if (appData.highlightDragging) {\r
4295         SetHighlights(fromX, fromY, x, y);\r
4296         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4297             full_repaint = TRUE;\r
4298         }\r
4299       }\r
4300       \r
4301       DrawPosition( full_repaint, NULL);\r
4302       \r
4303       dragInfo.lastpos = dragInfo.pos;\r
4304     }\r
4305     break;\r
4306 \r
4307   case WM_MOUSEWHEEL: // [DM]\r
4308     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4309        /* Mouse Wheel is being rolled forward\r
4310         * Play moves forward\r
4311         */\r
4312        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4313                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4314        /* Mouse Wheel is being rolled backward\r
4315         * Play moves backward\r
4316         */\r
4317        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4318                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4319     }\r
4320     break;\r
4321 \r
4322   case WM_MBUTTONUP:\r
4323   case WM_RBUTTONUP:\r
4324     ReleaseCapture();\r
4325     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4326     break;\r
4327  \r
4328   case WM_MBUTTONDOWN:\r
4329   case WM_RBUTTONDOWN:\r
4330     ErrorPopDown();\r
4331     ReleaseCapture();\r
4332     fromX = fromY = -1;\r
4333     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4334     dragInfo.start.x = dragInfo.start.y = -1;\r
4335     dragInfo.from = dragInfo.start;\r
4336     dragInfo.lastpos = dragInfo.pos;\r
4337     if (appData.highlightDragging) {\r
4338       ClearHighlights();\r
4339     }\r
4340     if(y == -2) {\r
4341       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4342       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4343           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4344       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4345           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4346       }\r
4347       break;\r
4348     }\r
4349     DrawPosition(TRUE, NULL);\r
4350 \r
4351     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4352     switch (menuNr) {\r
4353     case 0:\r
4354       if (message == WM_MBUTTONDOWN) {\r
4355         buttonCount = 3;  /* even if system didn't think so */\r
4356         if (wParam & MK_SHIFT) \r
4357           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4358         else\r
4359           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4360       } else { /* message == WM_RBUTTONDOWN */\r
4361         /* Just have one menu, on the right button.  Windows users don't\r
4362            think to try the middle one, and sometimes other software steals\r
4363            it, or it doesn't really exist. */\r
4364         if(gameInfo.variant != VariantShogi)\r
4365             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4366         else\r
4367             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4368       }\r
4369       break;\r
4370     case 2:\r
4371       SetCapture(hwndMain);\r
4372       break;\r
4373     case 1:\r
4374       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4375       SetupDropMenu(hmenu);\r
4376       MenuPopup(hwnd, pt, hmenu, -1);\r
4377     default:\r
4378       break;\r
4379     }\r
4380     break;\r
4381   }\r
4382 \r
4383   recursive--;\r
4384 }\r
4385 \r
4386 /* Preprocess messages for buttons in main window */\r
4387 LRESULT CALLBACK\r
4388 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4389 {\r
4390   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4391   int i, dir;\r
4392 \r
4393   for (i=0; i<N_BUTTONS; i++) {\r
4394     if (buttonDesc[i].id == id) break;\r
4395   }\r
4396   if (i == N_BUTTONS) return 0;\r
4397   switch (message) {\r
4398   case WM_KEYDOWN:\r
4399     switch (wParam) {\r
4400     case VK_LEFT:\r
4401     case VK_RIGHT:\r
4402       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4403       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4404       return TRUE;\r
4405     }\r
4406     break;\r
4407   case WM_CHAR:\r
4408     switch (wParam) {\r
4409     case '\r':\r
4410       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4411       return TRUE;\r
4412     default:\r
4413       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4414         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4415         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4416         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4417         SetFocus(h);\r
4418         SendMessage(h, WM_CHAR, wParam, lParam);\r
4419         return TRUE;\r
4420       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4421         TypeInEvent((char)wParam);\r
4422       }\r
4423       break;\r
4424     }\r
4425     break;\r
4426   }\r
4427   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4428 }\r
4429 \r
4430 /* Process messages for Promotion dialog box */\r
4431 LRESULT CALLBACK\r
4432 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4433 {\r
4434   char promoChar;\r
4435 \r
4436   switch (message) {\r
4437   case WM_INITDIALOG: /* message: initialize dialog box */\r
4438     /* Center the dialog over the application window */\r
4439     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4440     Translate(hDlg, DLG_PromotionKing);\r
4441     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4442       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4443        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4444        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4445                SW_SHOW : SW_HIDE);\r
4446     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4447     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4448        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4449          PieceToChar(WhiteAngel) != '~') ||\r
4450         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4451          PieceToChar(BlackAngel) != '~')   ) ?\r
4452                SW_SHOW : SW_HIDE);\r
4453     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4454        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4455          PieceToChar(WhiteMarshall) != '~') ||\r
4456         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4457          PieceToChar(BlackMarshall) != '~')   ) ?\r
4458                SW_SHOW : SW_HIDE);\r
4459     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4460     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4461        gameInfo.variant != VariantShogi ?\r
4462                SW_SHOW : SW_HIDE);\r
4463     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4464        gameInfo.variant != VariantShogi ?\r
4465                SW_SHOW : SW_HIDE);\r
4466     if(gameInfo.variant == VariantShogi) {\r
4467         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4468         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4469         SetWindowText(hDlg, "Promote?");\r
4470     }\r
4471     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4472        gameInfo.variant == VariantSuper ?\r
4473                SW_SHOW : SW_HIDE);\r
4474     return TRUE;\r
4475 \r
4476   case WM_COMMAND: /* message: received a command */\r
4477     switch (LOWORD(wParam)) {\r
4478     case IDCANCEL:\r
4479       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4480       ClearHighlights();\r
4481       DrawPosition(FALSE, NULL);\r
4482       return TRUE;\r
4483     case PB_King:\r
4484       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4485       break;\r
4486     case PB_Queen:\r
4487       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4488       break;\r
4489     case PB_Rook:\r
4490       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4491       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4492       break;\r
4493     case PB_Bishop:\r
4494       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4495       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4496       break;\r
4497     case PB_Chancellor:\r
4498       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4499       break;\r
4500     case PB_Archbishop:\r
4501       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4502       break;\r
4503     case PB_Knight:\r
4504       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4505       break;\r
4506     default:\r
4507       return FALSE;\r
4508     }\r
4509     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4510     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4511     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4512     fromX = fromY = -1;\r
4513     if (!appData.highlightLastMove) {\r
4514       ClearHighlights();\r
4515       DrawPosition(FALSE, NULL);\r
4516     }\r
4517     return TRUE;\r
4518   }\r
4519   return FALSE;\r
4520 }\r
4521 \r
4522 /* Pop up promotion dialog */\r
4523 VOID\r
4524 PromotionPopup(HWND hwnd)\r
4525 {\r
4526   FARPROC lpProc;\r
4527 \r
4528   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4529   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4530     hwnd, (DLGPROC)lpProc);\r
4531   FreeProcInstance(lpProc);\r
4532 }\r
4533 \r
4534 void\r
4535 PromotionPopUp()\r
4536 {\r
4537   DrawPosition(TRUE, NULL);\r
4538   PromotionPopup(hwndMain);\r
4539 }\r
4540 \r
4541 VOID\r
4542 LoadGameDialog(HWND hwnd, char* title)\r
4543 {\r
4544   UINT number = 0;\r
4545   FILE *f;\r
4546   char fileTitle[MSG_SIZ];\r
4547   f = OpenFileDialog(hwnd, "rb", "",\r
4548                      appData.oldSaveStyle ? "gam" : "pgn",\r
4549                      GAME_FILT,\r
4550                      title, &number, fileTitle, NULL);\r
4551   if (f != NULL) {\r
4552     cmailMsgLoaded = FALSE;\r
4553     if (number == 0) {\r
4554       int error = GameListBuild(f);\r
4555       if (error) {\r
4556         DisplayError(_("Cannot build game list"), error);\r
4557       } else if (!ListEmpty(&gameList) &&\r
4558                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4559         GameListPopUp(f, fileTitle);\r
4560         return;\r
4561       }\r
4562       GameListDestroy();\r
4563       number = 1;\r
4564     }\r
4565     LoadGame(f, number, fileTitle, FALSE);\r
4566   }\r
4567 }\r
4568 \r
4569 int get_term_width()\r
4570 {\r
4571     HDC hdc;\r
4572     TEXTMETRIC tm;\r
4573     RECT rc;\r
4574     HFONT hfont, hold_font;\r
4575     LOGFONT lf;\r
4576     HWND hText;\r
4577 \r
4578     if (hwndConsole)\r
4579         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4580     else\r
4581         return 79;\r
4582 \r
4583     // get the text metrics\r
4584     hdc = GetDC(hText);\r
4585     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4586     if (consoleCF.dwEffects & CFE_BOLD)\r
4587         lf.lfWeight = FW_BOLD;\r
4588     if (consoleCF.dwEffects & CFE_ITALIC)\r
4589         lf.lfItalic = TRUE;\r
4590     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4591         lf.lfStrikeOut = TRUE;\r
4592     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4593         lf.lfUnderline = TRUE;\r
4594     hfont = CreateFontIndirect(&lf);\r
4595     hold_font = SelectObject(hdc, hfont);\r
4596     GetTextMetrics(hdc, &tm);\r
4597     SelectObject(hdc, hold_font);\r
4598     DeleteObject(hfont);\r
4599     ReleaseDC(hText, hdc);\r
4600 \r
4601     // get the rectangle\r
4602     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4603 \r
4604     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4605 }\r
4606 \r
4607 void UpdateICSWidth(HWND hText)\r
4608 {\r
4609     LONG old_width, new_width;\r
4610 \r
4611     new_width = get_term_width(hText, FALSE);\r
4612     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4613     if (new_width != old_width)\r
4614     {\r
4615         ics_update_width(new_width);\r
4616         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4617     }\r
4618 }\r
4619 \r
4620 VOID\r
4621 ChangedConsoleFont()\r
4622 {\r
4623   CHARFORMAT cfmt;\r
4624   CHARRANGE tmpsel, sel;\r
4625   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4626   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4627   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4628   PARAFORMAT paraf;\r
4629 \r
4630   cfmt.cbSize = sizeof(CHARFORMAT);\r
4631   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4632     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4633                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4634   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4635    * size.  This was undocumented in the version of MSVC++ that I had\r
4636    * when I wrote the code, but is apparently documented now.\r
4637    */\r
4638   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4639   cfmt.bCharSet = f->lf.lfCharSet;\r
4640   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4641   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4642   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4643   /* Why are the following seemingly needed too? */\r
4644   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4645   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4646   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4647   tmpsel.cpMin = 0;\r
4648   tmpsel.cpMax = -1; /*999999?*/\r
4649   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4650   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4651   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4652    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4653    */\r
4654   paraf.cbSize = sizeof(paraf);\r
4655   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4656   paraf.dxStartIndent = 0;\r
4657   paraf.dxOffset = WRAP_INDENT;\r
4658   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4659   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4660   UpdateICSWidth(hText);\r
4661 }\r
4662 \r
4663 /*---------------------------------------------------------------------------*\\r
4664  *\r
4665  * Window Proc for main window\r
4666  *\r
4667 \*---------------------------------------------------------------------------*/\r
4668 \r
4669 /* Process messages for main window, etc. */\r
4670 LRESULT CALLBACK\r
4671 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4672 {\r
4673   FARPROC lpProc;\r
4674   int wmId, wmEvent;\r
4675   char *defName;\r
4676   FILE *f;\r
4677   UINT number;\r
4678   char fileTitle[MSG_SIZ];\r
4679   char buf[MSG_SIZ];\r
4680   static SnapData sd;\r
4681   static int peek=0;\r
4682 \r
4683   switch (message) {\r
4684 \r
4685   case WM_PAINT: /* message: repaint portion of window */\r
4686     PaintProc(hwnd);\r
4687     break;\r
4688 \r
4689   case WM_ERASEBKGND:\r
4690     if (IsIconic(hwnd)) {\r
4691       /* Cheat; change the message */\r
4692       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4693     } else {\r
4694       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4695     }\r
4696     break;\r
4697 \r
4698   case WM_LBUTTONDOWN:\r
4699   case WM_MBUTTONDOWN:\r
4700   case WM_RBUTTONDOWN:\r
4701   case WM_LBUTTONUP:\r
4702   case WM_MBUTTONUP:\r
4703   case WM_RBUTTONUP:\r
4704   case WM_MOUSEMOVE:\r
4705   case WM_MOUSEWHEEL:\r
4706     MouseEvent(hwnd, message, wParam, lParam);\r
4707     break;\r
4708 \r
4709   case WM_KEYUP:\r
4710     if((char)wParam == '\b') {\r
4711       ForwardEvent(); peek = 0;\r
4712     }\r
4713 \r
4714     JAWS_KBUP_NAVIGATION\r
4715 \r
4716     break;\r
4717 \r
4718   case WM_KEYDOWN:\r
4719     if((char)wParam == '\b') {\r
4720       if(!peek) BackwardEvent(), peek = 1;\r
4721     }\r
4722 \r
4723     JAWS_KBDOWN_NAVIGATION\r
4724 \r
4725     break;\r
4726 \r
4727   case WM_CHAR:\r
4728     \r
4729     JAWS_ALT_INTERCEPT\r
4730 \r
4731     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4732         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4733         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4734         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4735         SetFocus(h);\r
4736         SendMessage(h, message, wParam, lParam);\r
4737     } else if(lParam != KF_REPEAT) {\r
4738         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4739                 TypeInEvent((char)wParam);\r
4740         } else if((char)wParam == 003) CopyGameToClipboard();\r
4741          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4742     }\r
4743 \r
4744     break;\r
4745 \r
4746   case WM_PALETTECHANGED:\r
4747     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4748       int nnew;\r
4749       HDC hdc = GetDC(hwndMain);\r
4750       SelectPalette(hdc, hPal, TRUE);\r
4751       nnew = RealizePalette(hdc);\r
4752       if (nnew > 0) {\r
4753         paletteChanged = TRUE;\r
4754         InvalidateRect(hwnd, &boardRect, FALSE);\r
4755       }\r
4756       ReleaseDC(hwnd, hdc);\r
4757     }\r
4758     break;\r
4759 \r
4760   case WM_QUERYNEWPALETTE:\r
4761     if (!appData.monoMode /*&& paletteChanged*/) {\r
4762       int nnew;\r
4763       HDC hdc = GetDC(hwndMain);\r
4764       paletteChanged = FALSE;\r
4765       SelectPalette(hdc, hPal, FALSE);\r
4766       nnew = RealizePalette(hdc);\r
4767       if (nnew > 0) {\r
4768         InvalidateRect(hwnd, &boardRect, FALSE);\r
4769       }\r
4770       ReleaseDC(hwnd, hdc);\r
4771       return TRUE;\r
4772     }\r
4773     return FALSE;\r
4774 \r
4775   case WM_COMMAND: /* message: command from application menu */\r
4776     wmId    = LOWORD(wParam);\r
4777     wmEvent = HIWORD(wParam);\r
4778 \r
4779     switch (wmId) {\r
4780     case IDM_NewGame:\r
4781       ResetGameEvent();\r
4782       SAY("new game enter a move to play against the computer with white");\r
4783       break;\r
4784 \r
4785     case IDM_NewGameFRC:\r
4786       if( NewGameFRC() == 0 ) {\r
4787         ResetGameEvent();\r
4788       }\r
4789       break;\r
4790 \r
4791     case IDM_NewVariant:\r
4792       NewVariantPopup(hwnd);\r
4793       break;\r
4794 \r
4795     case IDM_LoadGame:\r
4796       LoadGameDialog(hwnd, _("Load Game from File"));\r
4797       break;\r
4798 \r
4799     case IDM_LoadNextGame:\r
4800       ReloadGame(1);\r
4801       break;\r
4802 \r
4803     case IDM_LoadPrevGame:\r
4804       ReloadGame(-1);\r
4805       break;\r
4806 \r
4807     case IDM_ReloadGame:\r
4808       ReloadGame(0);\r
4809       break;\r
4810 \r
4811     case IDM_LoadPosition:\r
4812       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4813         Reset(FALSE, TRUE);\r
4814       }\r
4815       number = 1;\r
4816       f = OpenFileDialog(hwnd, "rb", "",\r
4817                          appData.oldSaveStyle ? "pos" : "fen",\r
4818                          POSITION_FILT,\r
4819                          _("Load Position from File"), &number, fileTitle, NULL);\r
4820       if (f != NULL) {\r
4821         LoadPosition(f, number, fileTitle);\r
4822       }\r
4823       break;\r
4824 \r
4825     case IDM_LoadNextPosition:\r
4826       ReloadPosition(1);\r
4827       break;\r
4828 \r
4829     case IDM_LoadPrevPosition:\r
4830       ReloadPosition(-1);\r
4831       break;\r
4832 \r
4833     case IDM_ReloadPosition:\r
4834       ReloadPosition(0);\r
4835       break;\r
4836 \r
4837     case IDM_SaveGame:\r
4838       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4839       f = OpenFileDialog(hwnd, "a", defName,\r
4840                          appData.oldSaveStyle ? "gam" : "pgn",\r
4841                          GAME_FILT,\r
4842                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4843       if (f != NULL) {\r
4844         SaveGame(f, 0, "");\r
4845       }\r
4846       break;\r
4847 \r
4848     case IDM_SavePosition:\r
4849       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4850       f = OpenFileDialog(hwnd, "a", defName,\r
4851                          appData.oldSaveStyle ? "pos" : "fen",\r
4852                          POSITION_FILT,\r
4853                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4854       if (f != NULL) {\r
4855         SavePosition(f, 0, "");\r
4856       }\r
4857       break;\r
4858 \r
4859     case IDM_SaveDiagram:\r
4860       defName = "diagram";\r
4861       f = OpenFileDialog(hwnd, "wb", defName,\r
4862                          "bmp",\r
4863                          DIAGRAM_FILT,\r
4864                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4865       if (f != NULL) {\r
4866         SaveDiagram(f);\r
4867       }\r
4868       break;\r
4869 \r
4870     case IDM_CopyGame:\r
4871       CopyGameToClipboard();\r
4872       break;\r
4873 \r
4874     case IDM_PasteGame:\r
4875       PasteGameFromClipboard();\r
4876       break;\r
4877 \r
4878     case IDM_CopyGameListToClipboard:\r
4879       CopyGameListToClipboard();\r
4880       break;\r
4881 \r
4882     /* [AS] Autodetect FEN or PGN data */\r
4883     case IDM_PasteAny:\r
4884       PasteGameOrFENFromClipboard();\r
4885       break;\r
4886 \r
4887     /* [AS] Move history */\r
4888     case IDM_ShowMoveHistory:\r
4889         if( MoveHistoryIsUp() ) {\r
4890             MoveHistoryPopDown();\r
4891         }\r
4892         else {\r
4893             MoveHistoryPopUp();\r
4894         }\r
4895         break;\r
4896 \r
4897     /* [AS] Eval graph */\r
4898     case IDM_ShowEvalGraph:\r
4899         if( EvalGraphIsUp() ) {\r
4900             EvalGraphPopDown();\r
4901         }\r
4902         else {\r
4903             EvalGraphPopUp();\r
4904             SetFocus(hwndMain);\r
4905         }\r
4906         break;\r
4907 \r
4908     /* [AS] Engine output */\r
4909     case IDM_ShowEngineOutput:\r
4910         if( EngineOutputIsUp() ) {\r
4911             EngineOutputPopDown();\r
4912         }\r
4913         else {\r
4914             EngineOutputPopUp();\r
4915         }\r
4916         break;\r
4917 \r
4918     /* [AS] User adjudication */\r
4919     case IDM_UserAdjudication_White:\r
4920         UserAdjudicationEvent( +1 );\r
4921         break;\r
4922 \r
4923     case IDM_UserAdjudication_Black:\r
4924         UserAdjudicationEvent( -1 );\r
4925         break;\r
4926 \r
4927     case IDM_UserAdjudication_Draw:\r
4928         UserAdjudicationEvent( 0 );\r
4929         break;\r
4930 \r
4931     /* [AS] Game list options dialog */\r
4932     case IDM_GameListOptions:\r
4933       GameListOptions();\r
4934       break;\r
4935 \r
4936     case IDM_NewChat:\r
4937       ChatPopUp(NULL);\r
4938       break;\r
4939 \r
4940     case IDM_CopyPosition:\r
4941       CopyFENToClipboard();\r
4942       break;\r
4943 \r
4944     case IDM_PastePosition:\r
4945       PasteFENFromClipboard();\r
4946       break;\r
4947 \r
4948     case IDM_MailMove:\r
4949       MailMoveEvent();\r
4950       break;\r
4951 \r
4952     case IDM_ReloadCMailMsg:\r
4953       Reset(TRUE, TRUE);\r
4954       ReloadCmailMsgEvent(FALSE);\r
4955       break;\r
4956 \r
4957     case IDM_Minimize:\r
4958       ShowWindow(hwnd, SW_MINIMIZE);\r
4959       break;\r
4960 \r
4961     case IDM_Exit:\r
4962       ExitEvent(0);\r
4963       break;\r
4964 \r
4965     case IDM_MachineWhite:\r
4966       MachineWhiteEvent();\r
4967       /*\r
4968        * refresh the tags dialog only if it's visible\r
4969        */\r
4970       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4971           char *tags;\r
4972           tags = PGNTags(&gameInfo);\r
4973           TagsPopUp(tags, CmailMsg());\r
4974           free(tags);\r
4975       }\r
4976       SAY("computer starts playing white");\r
4977       break;\r
4978 \r
4979     case IDM_MachineBlack:\r
4980       MachineBlackEvent();\r
4981       /*\r
4982        * refresh the tags dialog only if it's visible\r
4983        */\r
4984       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4985           char *tags;\r
4986           tags = PGNTags(&gameInfo);\r
4987           TagsPopUp(tags, CmailMsg());\r
4988           free(tags);\r
4989       }\r
4990       SAY("computer starts playing black");\r
4991       break;\r
4992 \r
4993     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4994       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4995       break;\r
4996 \r
4997     case IDM_TwoMachines:\r
4998       TwoMachinesEvent();\r
4999       /*\r
5000        * refresh the tags dialog only if it's visible\r
5001        */\r
5002       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5003           char *tags;\r
5004           tags = PGNTags(&gameInfo);\r
5005           TagsPopUp(tags, CmailMsg());\r
5006           free(tags);\r
5007       }\r
5008       SAY("computer starts playing both sides");\r
5009       break;\r
5010 \r
5011     case IDM_AnalysisMode:\r
5012       if(AnalyzeModeEvent()) {\r
5013         SAY("analyzing current position");\r
5014       }\r
5015       break;\r
5016 \r
5017     case IDM_AnalyzeFile:\r
5018       AnalyzeFileEvent();\r
5019       break;\r
5020 \r
5021     case IDM_IcsClient:\r
5022       IcsClientEvent();\r
5023       break;\r
5024 \r
5025     case IDM_EditGame:\r
5026     case IDM_EditGame2:\r
5027       EditGameEvent();\r
5028       SAY("edit game");\r
5029       break;\r
5030 \r
5031     case IDM_EditPosition:\r
5032     case IDM_EditPosition2:\r
5033       EditPositionEvent();\r
5034       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5035       break;\r
5036 \r
5037     case IDM_Training:\r
5038       TrainingEvent();\r
5039       break;\r
5040 \r
5041     case IDM_ShowGameList:\r
5042       ShowGameListProc();\r
5043       break;\r
5044 \r
5045     case IDM_EditProgs1:\r
5046       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5047       break;\r
5048 \r
5049     case IDM_LoadProg1:\r
5050      LoadEnginePopUp(hwndMain, 0);\r
5051       break;\r
5052 \r
5053     case IDM_LoadProg2:\r
5054      LoadEnginePopUp(hwndMain, 1);\r
5055       break;\r
5056 \r
5057     case IDM_EditServers:\r
5058       EditTagsPopUp(icsNames, &icsNames);\r
5059       break;\r
5060 \r
5061     case IDM_EditTags:\r
5062     case IDM_Tags:\r
5063       EditTagsProc();\r
5064       break;\r
5065 \r
5066     case IDM_EditBook:\r
5067       EditBookEvent();\r
5068       break;\r
5069 \r
5070     case IDM_EditComment:\r
5071     case IDM_Comment:\r
5072       if (commentUp && editComment) {\r
5073         CommentPopDown();\r
5074       } else {\r
5075         EditCommentEvent();\r
5076       }\r
5077       break;\r
5078 \r
5079     case IDM_Pause:\r
5080       PauseEvent();\r
5081       break;\r
5082 \r
5083     case IDM_Accept:\r
5084       AcceptEvent();\r
5085       break;\r
5086 \r
5087     case IDM_Decline:\r
5088       DeclineEvent();\r
5089       break;\r
5090 \r
5091     case IDM_Rematch:\r
5092       RematchEvent();\r
5093       break;\r
5094 \r
5095     case IDM_CallFlag:\r
5096       CallFlagEvent();\r
5097       break;\r
5098 \r
5099     case IDM_Draw:\r
5100       DrawEvent();\r
5101       break;\r
5102 \r
5103     case IDM_Adjourn:\r
5104       AdjournEvent();\r
5105       break;\r
5106 \r
5107     case IDM_Abort:\r
5108       AbortEvent();\r
5109       break;\r
5110 \r
5111     case IDM_Resign:\r
5112       ResignEvent();\r
5113       break;\r
5114 \r
5115     case IDM_StopObserving:\r
5116       StopObservingEvent();\r
5117       break;\r
5118 \r
5119     case IDM_StopExamining:\r
5120       StopExaminingEvent();\r
5121       break;\r
5122 \r
5123     case IDM_Upload:\r
5124       UploadGameEvent();\r
5125       break;\r
5126 \r
5127     case IDM_TypeInMove:\r
5128       TypeInEvent('\000');\r
5129       break;\r
5130 \r
5131     case IDM_TypeInName:\r
5132       PopUpNameDialog('\000');\r
5133       break;\r
5134 \r
5135     case IDM_Backward:\r
5136       BackwardEvent();\r
5137       SetFocus(hwndMain);\r
5138       break;\r
5139 \r
5140     JAWS_MENU_ITEMS\r
5141 \r
5142     case IDM_Forward:\r
5143       ForwardEvent();\r
5144       SetFocus(hwndMain);\r
5145       break;\r
5146 \r
5147     case IDM_ToStart:\r
5148       ToStartEvent();\r
5149       SetFocus(hwndMain);\r
5150       break;\r
5151 \r
5152     case IDM_ToEnd:\r
5153       ToEndEvent();\r
5154       SetFocus(hwndMain);\r
5155       break;\r
5156 \r
5157     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5158     case OPT_GameListPrev:\r
5159       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5160       break;\r
5161 \r
5162     case IDM_Revert:\r
5163       RevertEvent(FALSE);\r
5164       break;\r
5165 \r
5166     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5167       RevertEvent(TRUE);\r
5168       break;\r
5169 \r
5170     case IDM_TruncateGame:\r
5171       TruncateGameEvent();\r
5172       break;\r
5173 \r
5174     case IDM_MoveNow:\r
5175       MoveNowEvent();\r
5176       break;\r
5177 \r
5178     case IDM_RetractMove:\r
5179       RetractMoveEvent();\r
5180       break;\r
5181 \r
5182     case IDM_FlipView:\r
5183       flipView = !flipView;\r
5184       DrawPosition(FALSE, NULL);\r
5185       break;\r
5186 \r
5187     case IDM_FlipClock:\r
5188       flipClock = !flipClock;\r
5189       DisplayBothClocks();\r
5190       DisplayLogos();\r
5191       break;\r
5192 \r
5193     case IDM_MuteSounds:\r
5194       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5195       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5196                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5197       break;\r
5198 \r
5199     case IDM_GeneralOptions:\r
5200       GeneralOptionsPopup(hwnd);\r
5201       DrawPosition(TRUE, NULL);\r
5202       break;\r
5203 \r
5204     case IDM_BoardOptions:\r
5205       BoardOptionsPopup(hwnd);\r
5206       break;\r
5207 \r
5208     case IDM_ThemeOptions:\r
5209       ThemeOptionsPopup(hwnd);\r
5210       break;\r
5211 \r
5212     case IDM_EnginePlayOptions:\r
5213       EnginePlayOptionsPopup(hwnd);\r
5214       break;\r
5215 \r
5216     case IDM_Engine1Options:\r
5217       EngineOptionsPopup(hwnd, &first);\r
5218       break;\r
5219 \r
5220     case IDM_Engine2Options:\r
5221       savedHwnd = hwnd;\r
5222       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5223       EngineOptionsPopup(hwnd, &second);\r
5224       break;\r
5225 \r
5226     case IDM_OptionsUCI:\r
5227       UciOptionsPopup(hwnd);\r
5228       break;\r
5229 \r
5230     case IDM_Tourney:\r
5231       TourneyPopup(hwnd);\r
5232       break;\r
5233 \r
5234     case IDM_IcsOptions:\r
5235       IcsOptionsPopup(hwnd);\r
5236       break;\r
5237 \r
5238     case IDM_Fonts:\r
5239       FontsOptionsPopup(hwnd);\r
5240       break;\r
5241 \r
5242     case IDM_Sounds:\r
5243       SoundOptionsPopup(hwnd);\r
5244       break;\r
5245 \r
5246     case IDM_CommPort:\r
5247       CommPortOptionsPopup(hwnd);\r
5248       break;\r
5249 \r
5250     case IDM_LoadOptions:\r
5251       LoadOptionsPopup(hwnd);\r
5252       break;\r
5253 \r
5254     case IDM_SaveOptions:\r
5255       SaveOptionsPopup(hwnd);\r
5256       break;\r
5257 \r
5258     case IDM_TimeControl:\r
5259       TimeControlOptionsPopup(hwnd);\r
5260       break;\r
5261 \r
5262     case IDM_SaveSettings:\r
5263       SaveSettings(settingsFileName);\r
5264       break;\r
5265 \r
5266     case IDM_SaveSettingsOnExit:\r
5267       saveSettingsOnExit = !saveSettingsOnExit;\r
5268       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5269                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5270                                          MF_CHECKED : MF_UNCHECKED));\r
5271       break;\r
5272 \r
5273     case IDM_Hint:\r
5274       HintEvent();\r
5275       break;\r
5276 \r
5277     case IDM_Book:\r
5278       BookEvent();\r
5279       break;\r
5280 \r
5281     case IDM_AboutGame:\r
5282       AboutGameEvent();\r
5283       break;\r
5284 \r
5285     case IDM_Debug:\r
5286       appData.debugMode = !appData.debugMode;\r
5287       if (appData.debugMode) {\r
5288         char dir[MSG_SIZ];\r
5289         GetCurrentDirectory(MSG_SIZ, dir);\r
5290         SetCurrentDirectory(installDir);\r
5291         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5292         SetCurrentDirectory(dir);\r
5293         setbuf(debugFP, NULL);\r
5294       } else {\r
5295         fclose(debugFP);\r
5296         debugFP = NULL;\r
5297       }\r
5298       break;\r
5299 \r
5300     case IDM_HELPCONTENTS:\r
5301       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5302           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5303           MessageBox (GetFocus(),\r
5304                     _("Unable to activate help"),\r
5305                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5306       }\r
5307       break;\r
5308 \r
5309     case IDM_HELPSEARCH:\r
5310         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5311             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5312         MessageBox (GetFocus(),\r
5313                     _("Unable to activate help"),\r
5314                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5315       }\r
5316       break;\r
5317 \r
5318     case IDM_HELPHELP:\r
5319       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5320         MessageBox (GetFocus(),\r
5321                     _("Unable to activate help"),\r
5322                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5323       }\r
5324       break;\r
5325 \r
5326     case IDM_ABOUT:\r
5327       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5328       DialogBox(hInst, \r
5329         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5330         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5331       FreeProcInstance(lpProc);\r
5332       break;\r
5333 \r
5334     case IDM_DirectCommand1:\r
5335       AskQuestionEvent(_("Direct Command"),\r
5336                        _("Send to chess program:"), "", "1");\r
5337       break;\r
5338     case IDM_DirectCommand2:\r
5339       AskQuestionEvent(_("Direct Command"),\r
5340                        _("Send to second chess program:"), "", "2");\r
5341       break;\r
5342 \r
5343     case EP_WhitePawn:\r
5344       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5345       fromX = fromY = -1;\r
5346       break;\r
5347 \r
5348     case EP_WhiteKnight:\r
5349       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5350       fromX = fromY = -1;\r
5351       break;\r
5352 \r
5353     case EP_WhiteBishop:\r
5354       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5355       fromX = fromY = -1;\r
5356       break;\r
5357 \r
5358     case EP_WhiteRook:\r
5359       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5360       fromX = fromY = -1;\r
5361       break;\r
5362 \r
5363     case EP_WhiteQueen:\r
5364       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5365       fromX = fromY = -1;\r
5366       break;\r
5367 \r
5368     case EP_WhiteFerz:\r
5369       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5370       fromX = fromY = -1;\r
5371       break;\r
5372 \r
5373     case EP_WhiteWazir:\r
5374       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5375       fromX = fromY = -1;\r
5376       break;\r
5377 \r
5378     case EP_WhiteAlfil:\r
5379       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5380       fromX = fromY = -1;\r
5381       break;\r
5382 \r
5383     case EP_WhiteCannon:\r
5384       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5385       fromX = fromY = -1;\r
5386       break;\r
5387 \r
5388     case EP_WhiteCardinal:\r
5389       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5390       fromX = fromY = -1;\r
5391       break;\r
5392 \r
5393     case EP_WhiteMarshall:\r
5394       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5395       fromX = fromY = -1;\r
5396       break;\r
5397 \r
5398     case EP_WhiteKing:\r
5399       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5400       fromX = fromY = -1;\r
5401       break;\r
5402 \r
5403     case EP_BlackPawn:\r
5404       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5405       fromX = fromY = -1;\r
5406       break;\r
5407 \r
5408     case EP_BlackKnight:\r
5409       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5410       fromX = fromY = -1;\r
5411       break;\r
5412 \r
5413     case EP_BlackBishop:\r
5414       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5415       fromX = fromY = -1;\r
5416       break;\r
5417 \r
5418     case EP_BlackRook:\r
5419       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5420       fromX = fromY = -1;\r
5421       break;\r
5422 \r
5423     case EP_BlackQueen:\r
5424       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5425       fromX = fromY = -1;\r
5426       break;\r
5427 \r
5428     case EP_BlackFerz:\r
5429       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5430       fromX = fromY = -1;\r
5431       break;\r
5432 \r
5433     case EP_BlackWazir:\r
5434       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5435       fromX = fromY = -1;\r
5436       break;\r
5437 \r
5438     case EP_BlackAlfil:\r
5439       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5440       fromX = fromY = -1;\r
5441       break;\r
5442 \r
5443     case EP_BlackCannon:\r
5444       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5445       fromX = fromY = -1;\r
5446       break;\r
5447 \r
5448     case EP_BlackCardinal:\r
5449       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5450       fromX = fromY = -1;\r
5451       break;\r
5452 \r
5453     case EP_BlackMarshall:\r
5454       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5455       fromX = fromY = -1;\r
5456       break;\r
5457 \r
5458     case EP_BlackKing:\r
5459       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5460       fromX = fromY = -1;\r
5461       break;\r
5462 \r
5463     case EP_EmptySquare:\r
5464       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5465       fromX = fromY = -1;\r
5466       break;\r
5467 \r
5468     case EP_ClearBoard:\r
5469       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5470       fromX = fromY = -1;\r
5471       break;\r
5472 \r
5473     case EP_White:\r
5474       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5475       fromX = fromY = -1;\r
5476       break;\r
5477 \r
5478     case EP_Black:\r
5479       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5480       fromX = fromY = -1;\r
5481       break;\r
5482 \r
5483     case EP_Promote:\r
5484       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5485       fromX = fromY = -1;\r
5486       break;\r
5487 \r
5488     case EP_Demote:\r
5489       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5490       fromX = fromY = -1;\r
5491       break;\r
5492 \r
5493     case DP_Pawn:\r
5494       DropMenuEvent(WhitePawn, fromX, fromY);\r
5495       fromX = fromY = -1;\r
5496       break;\r
5497 \r
5498     case DP_Knight:\r
5499       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5500       fromX = fromY = -1;\r
5501       break;\r
5502 \r
5503     case DP_Bishop:\r
5504       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5505       fromX = fromY = -1;\r
5506       break;\r
5507 \r
5508     case DP_Rook:\r
5509       DropMenuEvent(WhiteRook, fromX, fromY);\r
5510       fromX = fromY = -1;\r
5511       break;\r
5512 \r
5513     case DP_Queen:\r
5514       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5515       fromX = fromY = -1;\r
5516       break;\r
5517 \r
5518     case IDM_English:\r
5519       barbaric = 0; appData.language = "";\r
5520       TranslateMenus(0);\r
5521       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5522       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5523       lastChecked = wmId;\r
5524       break;\r
5525 \r
5526     default:\r
5527       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5528           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5529       else\r
5530       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5531           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5532           TranslateMenus(0);\r
5533           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5534           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5535           lastChecked = wmId;\r
5536           break;\r
5537       }\r
5538       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5539     }\r
5540     break;\r
5541 \r
5542   case WM_TIMER:\r
5543     switch (wParam) {\r
5544     case CLOCK_TIMER_ID:\r
5545       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5546       clockTimerEvent = 0;\r
5547       DecrementClocks(); /* call into back end */\r
5548       break;\r
5549     case LOAD_GAME_TIMER_ID:\r
5550       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5551       loadGameTimerEvent = 0;\r
5552       AutoPlayGameLoop(); /* call into back end */\r
5553       break;\r
5554     case ANALYSIS_TIMER_ID:\r
5555       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5556                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5557         AnalysisPeriodicEvent(0);\r
5558       } else {\r
5559         KillTimer(hwnd, analysisTimerEvent);\r
5560         analysisTimerEvent = 0;\r
5561       }\r
5562       break;\r
5563     case DELAYED_TIMER_ID:\r
5564       KillTimer(hwnd, delayedTimerEvent);\r
5565       delayedTimerEvent = 0;\r
5566       delayedTimerCallback();\r
5567       break;\r
5568     }\r
5569     break;\r
5570 \r
5571   case WM_USER_Input:\r
5572     InputEvent(hwnd, message, wParam, lParam);\r
5573     break;\r
5574 \r
5575   /* [AS] Also move "attached" child windows */\r
5576   case WM_WINDOWPOSCHANGING:\r
5577 \r
5578     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5579         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5580 \r
5581         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5582             /* Window is moving */\r
5583             RECT rcMain;\r
5584 \r
5585 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5586             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5587             rcMain.right  = wpMain.x + wpMain.width;\r
5588             rcMain.top    = wpMain.y;\r
5589             rcMain.bottom = wpMain.y + wpMain.height;\r
5590             \r
5591             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5592             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5593             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5594             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5595             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5596             wpMain.x = lpwp->x;\r
5597             wpMain.y = lpwp->y;\r
5598         }\r
5599     }\r
5600     break;\r
5601 \r
5602   /* [AS] Snapping */\r
5603   case WM_ENTERSIZEMOVE:\r
5604     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5605     if (hwnd == hwndMain) {\r
5606       doingSizing = TRUE;\r
5607       lastSizing = 0;\r
5608     }\r
5609     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5610     break;\r
5611 \r
5612   case WM_SIZING:\r
5613     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5614     if (hwnd == hwndMain) {\r
5615       lastSizing = wParam;\r
5616     }\r
5617     break;\r
5618 \r
5619   case WM_MOVING:\r
5620     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5621       return OnMoving( &sd, hwnd, wParam, lParam );\r
5622 \r
5623   case WM_EXITSIZEMOVE:\r
5624     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5625     if (hwnd == hwndMain) {\r
5626       RECT client;\r
5627       doingSizing = FALSE;\r
5628       InvalidateRect(hwnd, &boardRect, FALSE);\r
5629       GetClientRect(hwnd, &client);\r
5630       ResizeBoard(client.right, client.bottom, lastSizing);\r
5631       lastSizing = 0;\r
5632       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5633     }\r
5634     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5635     break;\r
5636 \r
5637   case WM_DESTROY: /* message: window being destroyed */\r
5638     PostQuitMessage(0);\r
5639     break;\r
5640 \r
5641   case WM_CLOSE:\r
5642     if (hwnd == hwndMain) {\r
5643       ExitEvent(0);\r
5644     }\r
5645     break;\r
5646 \r
5647   default:      /* Passes it on if unprocessed */\r
5648     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5649   }\r
5650   return 0;\r
5651 }\r
5652 \r
5653 /*---------------------------------------------------------------------------*\\r
5654  *\r
5655  * Misc utility routines\r
5656  *\r
5657 \*---------------------------------------------------------------------------*/\r
5658 \r
5659 /*\r
5660  * Decent random number generator, at least not as bad as Windows\r
5661  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5662  */\r
5663 unsigned int randstate;\r
5664 \r
5665 int\r
5666 myrandom(void)\r
5667 {\r
5668   randstate = randstate * 1664525 + 1013904223;\r
5669   return (int) randstate & 0x7fffffff;\r
5670 }\r
5671 \r
5672 void\r
5673 mysrandom(unsigned int seed)\r
5674 {\r
5675   randstate = seed;\r
5676 }\r
5677 \r
5678 \r
5679 /* \r
5680  * returns TRUE if user selects a different color, FALSE otherwise \r
5681  */\r
5682 \r
5683 BOOL\r
5684 ChangeColor(HWND hwnd, COLORREF *which)\r
5685 {\r
5686   static BOOL firstTime = TRUE;\r
5687   static DWORD customColors[16];\r
5688   CHOOSECOLOR cc;\r
5689   COLORREF newcolor;\r
5690   int i;\r
5691   ColorClass ccl;\r
5692 \r
5693   if (firstTime) {\r
5694     /* Make initial colors in use available as custom colors */\r
5695     /* Should we put the compiled-in defaults here instead? */\r
5696     i = 0;\r
5697     customColors[i++] = lightSquareColor & 0xffffff;\r
5698     customColors[i++] = darkSquareColor & 0xffffff;\r
5699     customColors[i++] = whitePieceColor & 0xffffff;\r
5700     customColors[i++] = blackPieceColor & 0xffffff;\r
5701     customColors[i++] = highlightSquareColor & 0xffffff;\r
5702     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5703 \r
5704     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5705       customColors[i++] = textAttribs[ccl].color;\r
5706     }\r
5707     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5708     firstTime = FALSE;\r
5709   }\r
5710 \r
5711   cc.lStructSize = sizeof(cc);\r
5712   cc.hwndOwner = hwnd;\r
5713   cc.hInstance = NULL;\r
5714   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5715   cc.lpCustColors = (LPDWORD) customColors;\r
5716   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5717 \r
5718   if (!ChooseColor(&cc)) return FALSE;\r
5719 \r
5720   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5721   if (newcolor == *which) return FALSE;\r
5722   *which = newcolor;\r
5723   return TRUE;\r
5724 \r
5725   /*\r
5726   InitDrawingColors();\r
5727   InvalidateRect(hwnd, &boardRect, FALSE);\r
5728   */\r
5729 }\r
5730 \r
5731 BOOLEAN\r
5732 MyLoadSound(MySound *ms)\r
5733 {\r
5734   BOOL ok = FALSE;\r
5735   struct stat st;\r
5736   FILE *f;\r
5737 \r
5738   if (ms->data && ms->flag) free(ms->data);\r
5739   ms->data = NULL;\r
5740 \r
5741   switch (ms->name[0]) {\r
5742   case NULLCHAR:\r
5743     /* Silence */\r
5744     ok = TRUE;\r
5745     break;\r
5746   case '$':\r
5747     /* System sound from Control Panel.  Don't preload here. */\r
5748     ok = TRUE;\r
5749     break;\r
5750   case '!':\r
5751     if (ms->name[1] == NULLCHAR) {\r
5752       /* "!" alone = silence */\r
5753       ok = TRUE;\r
5754     } else {\r
5755       /* Builtin wave resource.  Error if not found. */\r
5756       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5757       if (h == NULL) break;\r
5758       ms->data = (void *)LoadResource(hInst, h);\r
5759       ms->flag = 0; // not maloced, so cannot be freed!\r
5760       if (h == NULL) break;\r
5761       ok = TRUE;\r
5762     }\r
5763     break;\r
5764   default:\r
5765     /* .wav file.  Error if not found. */\r
5766     f = fopen(ms->name, "rb");\r
5767     if (f == NULL) break;\r
5768     if (fstat(fileno(f), &st) < 0) break;\r
5769     ms->data = malloc(st.st_size);\r
5770     ms->flag = 1;\r
5771     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5772     fclose(f);\r
5773     ok = TRUE;\r
5774     break;\r
5775   }\r
5776   if (!ok) {\r
5777     char buf[MSG_SIZ];\r
5778       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5779     DisplayError(buf, GetLastError());\r
5780   }\r
5781   return ok;\r
5782 }\r
5783 \r
5784 BOOLEAN\r
5785 MyPlaySound(MySound *ms)\r
5786 {\r
5787   BOOLEAN ok = FALSE;\r
5788 \r
5789   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5790   switch (ms->name[0]) {\r
5791   case NULLCHAR:\r
5792         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5793     /* Silence */\r
5794     ok = TRUE;\r
5795     break;\r
5796   case '$':\r
5797     /* System sound from Control Panel (deprecated feature).\r
5798        "$" alone or an unset sound name gets default beep (still in use). */\r
5799     if (ms->name[1]) {\r
5800       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5801     }\r
5802     if (!ok) ok = MessageBeep(MB_OK);\r
5803     break; \r
5804   case '!':\r
5805     /* Builtin wave resource, or "!" alone for silence */\r
5806     if (ms->name[1]) {\r
5807       if (ms->data == NULL) return FALSE;\r
5808       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5809     } else {\r
5810       ok = TRUE;\r
5811     }\r
5812     break;\r
5813   default:\r
5814     /* .wav file.  Error if not found. */\r
5815     if (ms->data == NULL) return FALSE;\r
5816     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5817     break;\r
5818   }\r
5819   /* Don't print an error: this can happen innocently if the sound driver\r
5820      is busy; for instance, if another instance of WinBoard is playing\r
5821      a sound at about the same time. */\r
5822   return ok;\r
5823 }\r
5824 \r
5825 \r
5826 LRESULT CALLBACK\r
5827 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5828 {\r
5829   BOOL ok;\r
5830   OPENFILENAME *ofn;\r
5831   static UINT *number; /* gross that this is static */\r
5832 \r
5833   switch (message) {\r
5834   case WM_INITDIALOG: /* message: initialize dialog box */\r
5835     /* Center the dialog over the application window */\r
5836     ofn = (OPENFILENAME *) lParam;\r
5837     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5838       number = (UINT *) ofn->lCustData;\r
5839       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5840     } else {\r
5841       number = NULL;\r
5842     }\r
5843     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5844     Translate(hDlg, 1536);\r
5845     return FALSE;  /* Allow for further processing */\r
5846 \r
5847   case WM_COMMAND:\r
5848     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5849       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5850     }\r
5851     return FALSE;  /* Allow for further processing */\r
5852   }\r
5853   return FALSE;\r
5854 }\r
5855 \r
5856 UINT APIENTRY\r
5857 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5858 {\r
5859   static UINT *number;\r
5860   OPENFILENAME *ofname;\r
5861   OFNOTIFY *ofnot;\r
5862   switch (uiMsg) {\r
5863   case WM_INITDIALOG:\r
5864     Translate(hdlg, DLG_IndexNumber);\r
5865     ofname = (OPENFILENAME *)lParam;\r
5866     number = (UINT *)(ofname->lCustData);\r
5867     break;\r
5868   case WM_NOTIFY:\r
5869     ofnot = (OFNOTIFY *)lParam;\r
5870     if (ofnot->hdr.code == CDN_FILEOK) {\r
5871       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5872     }\r
5873     break;\r
5874   }\r
5875   return 0;\r
5876 }\r
5877 \r
5878 \r
5879 FILE *\r
5880 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5881                char *nameFilt, char *dlgTitle, UINT *number,\r
5882                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5883 {\r
5884   OPENFILENAME openFileName;\r
5885   char buf1[MSG_SIZ];\r
5886   FILE *f;\r
5887 \r
5888   if (fileName == NULL) fileName = buf1;\r
5889   if (defName == NULL) {\r
5890     safeStrCpy(fileName, "*.", 3 );\r
5891     strcat(fileName, defExt);\r
5892   } else {\r
5893     safeStrCpy(fileName, defName, MSG_SIZ );\r
5894   }\r
5895     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5896   if (number) *number = 0;\r
5897 \r
5898   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5899   openFileName.hwndOwner         = hwnd;\r
5900   openFileName.hInstance         = (HANDLE) hInst;\r
5901   openFileName.lpstrFilter       = nameFilt;\r
5902   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5903   openFileName.nMaxCustFilter    = 0L;\r
5904   openFileName.nFilterIndex      = 1L;\r
5905   openFileName.lpstrFile         = fileName;\r
5906   openFileName.nMaxFile          = MSG_SIZ;\r
5907   openFileName.lpstrFileTitle    = fileTitle;\r
5908   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5909   openFileName.lpstrInitialDir   = NULL;\r
5910   openFileName.lpstrTitle        = dlgTitle;\r
5911   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5912     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5913     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5914     | (oldDialog ? 0 : OFN_EXPLORER);\r
5915   openFileName.nFileOffset       = 0;\r
5916   openFileName.nFileExtension    = 0;\r
5917   openFileName.lpstrDefExt       = defExt;\r
5918   openFileName.lCustData         = (LONG) number;\r
5919   openFileName.lpfnHook          = oldDialog ?\r
5920     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5921   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5922 \r
5923   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5924                         GetOpenFileName(&openFileName)) {\r
5925     /* open the file */\r
5926     f = fopen(openFileName.lpstrFile, write);\r
5927     if (f == NULL) {\r
5928       MessageBox(hwnd, _("File open failed"), NULL,\r
5929                  MB_OK|MB_ICONEXCLAMATION);\r
5930       return NULL;\r
5931     }\r
5932   } else {\r
5933     int err = CommDlgExtendedError();\r
5934     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5935     return FALSE;\r
5936   }\r
5937   return f;\r
5938 }\r
5939 \r
5940 \r
5941 \r
5942 VOID APIENTRY\r
5943 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5944 {\r
5945   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5946 \r
5947   /*\r
5948    * Get the first pop-up menu in the menu template. This is the\r
5949    * menu that TrackPopupMenu displays.\r
5950    */\r
5951   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5952   TranslateOneMenu(10, hmenuTrackPopup);\r
5953 \r
5954   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5955 \r
5956   /*\r
5957    * TrackPopup uses screen coordinates, so convert the\r
5958    * coordinates of the mouse click to screen coordinates.\r
5959    */\r
5960   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5961 \r
5962   /* Draw and track the floating pop-up menu. */\r
5963   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5964                  pt.x, pt.y, 0, hwnd, NULL);\r
5965 \r
5966   /* Destroy the menu.*/\r
5967   DestroyMenu(hmenu);\r
5968 }\r
5969    \r
5970 typedef struct {\r
5971   HWND hDlg, hText;\r
5972   int sizeX, sizeY, newSizeX, newSizeY;\r
5973   HDWP hdwp;\r
5974 } ResizeEditPlusButtonsClosure;\r
5975 \r
5976 BOOL CALLBACK\r
5977 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5978 {\r
5979   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5980   RECT rect;\r
5981   POINT pt;\r
5982 \r
5983   if (hChild == cl->hText) return TRUE;\r
5984   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5985   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5986   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5987   ScreenToClient(cl->hDlg, &pt);\r
5988   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5989     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5990   return TRUE;\r
5991 }\r
5992 \r
5993 /* Resize a dialog that has a (rich) edit field filling most of\r
5994    the top, with a row of buttons below */\r
5995 VOID\r
5996 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5997 {\r
5998   RECT rectText;\r
5999   int newTextHeight, newTextWidth;\r
6000   ResizeEditPlusButtonsClosure cl;\r
6001   \r
6002   /*if (IsIconic(hDlg)) return;*/\r
6003   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6004   \r
6005   cl.hdwp = BeginDeferWindowPos(8);\r
6006 \r
6007   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6008   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6009   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6010   if (newTextHeight < 0) {\r
6011     newSizeY += -newTextHeight;\r
6012     newTextHeight = 0;\r
6013   }\r
6014   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6015     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6016 \r
6017   cl.hDlg = hDlg;\r
6018   cl.hText = hText;\r
6019   cl.sizeX = sizeX;\r
6020   cl.sizeY = sizeY;\r
6021   cl.newSizeX = newSizeX;\r
6022   cl.newSizeY = newSizeY;\r
6023   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6024 \r
6025   EndDeferWindowPos(cl.hdwp);\r
6026 }\r
6027 \r
6028 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6029 {\r
6030     RECT    rChild, rParent;\r
6031     int     wChild, hChild, wParent, hParent;\r
6032     int     wScreen, hScreen, xNew, yNew;\r
6033     HDC     hdc;\r
6034 \r
6035     /* Get the Height and Width of the child window */\r
6036     GetWindowRect (hwndChild, &rChild);\r
6037     wChild = rChild.right - rChild.left;\r
6038     hChild = rChild.bottom - rChild.top;\r
6039 \r
6040     /* Get the Height and Width of the parent window */\r
6041     GetWindowRect (hwndParent, &rParent);\r
6042     wParent = rParent.right - rParent.left;\r
6043     hParent = rParent.bottom - rParent.top;\r
6044 \r
6045     /* Get the display limits */\r
6046     hdc = GetDC (hwndChild);\r
6047     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6048     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6049     ReleaseDC(hwndChild, hdc);\r
6050 \r
6051     /* Calculate new X position, then adjust for screen */\r
6052     xNew = rParent.left + ((wParent - wChild) /2);\r
6053     if (xNew < 0) {\r
6054         xNew = 0;\r
6055     } else if ((xNew+wChild) > wScreen) {\r
6056         xNew = wScreen - wChild;\r
6057     }\r
6058 \r
6059     /* Calculate new Y position, then adjust for screen */\r
6060     if( mode == 0 ) {\r
6061         yNew = rParent.top  + ((hParent - hChild) /2);\r
6062     }\r
6063     else {\r
6064         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6065     }\r
6066 \r
6067     if (yNew < 0) {\r
6068         yNew = 0;\r
6069     } else if ((yNew+hChild) > hScreen) {\r
6070         yNew = hScreen - hChild;\r
6071     }\r
6072 \r
6073     /* Set it, and return */\r
6074     return SetWindowPos (hwndChild, NULL,\r
6075                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6076 }\r
6077 \r
6078 /* Center one window over another */\r
6079 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6080 {\r
6081     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6082 }\r
6083 \r
6084 /*---------------------------------------------------------------------------*\\r
6085  *\r
6086  * Startup Dialog functions\r
6087  *\r
6088 \*---------------------------------------------------------------------------*/\r
6089 void\r
6090 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6091 {\r
6092   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6093 \r
6094   while (*cd != NULL) {\r
6095     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6096     cd++;\r
6097   }\r
6098 }\r
6099 \r
6100 void\r
6101 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6102 {\r
6103   char buf1[MAX_ARG_LEN];\r
6104   int len;\r
6105 \r
6106   if (str[0] == '@') {\r
6107     FILE* f = fopen(str + 1, "r");\r
6108     if (f == NULL) {\r
6109       DisplayFatalError(str + 1, errno, 2);\r
6110       return;\r
6111     }\r
6112     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6113     fclose(f);\r
6114     buf1[len] = NULLCHAR;\r
6115     str = buf1;\r
6116   }\r
6117 \r
6118   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6119 \r
6120   for (;;) {\r
6121     char buf[MSG_SIZ];\r
6122     char *end = strchr(str, '\n');\r
6123     if (end == NULL) return;\r
6124     memcpy(buf, str, end - str);\r
6125     buf[end - str] = NULLCHAR;\r
6126     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6127     str = end + 1;\r
6128   }\r
6129 }\r
6130 \r
6131 void\r
6132 SetStartupDialogEnables(HWND hDlg)\r
6133 {\r
6134   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6135     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6136     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6137   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6138     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6139   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6140     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6141   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6142     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6143   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6144     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6145     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6146     IsDlgButtonChecked(hDlg, OPT_View));\r
6147 }\r
6148 \r
6149 char *\r
6150 QuoteForFilename(char *filename)\r
6151 {\r
6152   int dquote, space;\r
6153   dquote = strchr(filename, '"') != NULL;\r
6154   space = strchr(filename, ' ') != NULL;\r
6155   if (dquote || space) {\r
6156     if (dquote) {\r
6157       return "'";\r
6158     } else {\r
6159       return "\"";\r
6160     }\r
6161   } else {\r
6162     return "";\r
6163   }\r
6164 }\r
6165 \r
6166 VOID\r
6167 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6168 {\r
6169   char buf[MSG_SIZ];\r
6170   char *q;\r
6171 \r
6172   InitComboStringsFromOption(hwndCombo, nthnames);\r
6173   q = QuoteForFilename(nthcp);\r
6174     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6175   if (*nthdir != NULLCHAR) {\r
6176     q = QuoteForFilename(nthdir);\r
6177       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6178   }\r
6179   if (*nthcp == NULLCHAR) {\r
6180     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6181   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6182     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6183     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6184   }\r
6185 }\r
6186 \r
6187 LRESULT CALLBACK\r
6188 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6189 {\r
6190   char buf[MSG_SIZ];\r
6191   HANDLE hwndCombo;\r
6192   char *p;\r
6193 \r
6194   switch (message) {\r
6195   case WM_INITDIALOG:\r
6196     /* Center the dialog */\r
6197     CenterWindow (hDlg, GetDesktopWindow());\r
6198     Translate(hDlg, DLG_Startup);\r
6199     /* Initialize the dialog items */\r
6200     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6201                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6202                   firstChessProgramNames);\r
6203     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6204                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6205                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6206     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6207     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6208       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6209     if (*appData.icsHelper != NULLCHAR) {\r
6210       char *q = QuoteForFilename(appData.icsHelper);\r
6211       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6212     }\r
6213     if (*appData.icsHost == NULLCHAR) {\r
6214       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6215       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6216     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6217       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6218       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6219     }\r
6220 \r
6221     if (appData.icsActive) {\r
6222       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6223     }\r
6224     else if (appData.noChessProgram) {\r
6225       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6226     }\r
6227     else {\r
6228       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6229     }\r
6230 \r
6231     SetStartupDialogEnables(hDlg);\r
6232     return TRUE;\r
6233 \r
6234   case WM_COMMAND:\r
6235     switch (LOWORD(wParam)) {\r
6236     case IDOK:\r
6237       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6238         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6239         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6240         p = buf;\r
6241         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6242         ParseArgs(StringGet, &p);\r
6243         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6244         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6245         p = buf;\r
6246         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6247         ParseArgs(StringGet, &p);\r
6248         SwapEngines(singleList); // ... and then make it 'second'\r
6249         appData.noChessProgram = FALSE;\r
6250         appData.icsActive = FALSE;\r
6251       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6252         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6253         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6254         p = buf;\r
6255         ParseArgs(StringGet, &p);\r
6256         if (appData.zippyPlay) {\r
6257           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6258           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6259           p = buf;\r
6260           ParseArgs(StringGet, &p);\r
6261         }\r
6262       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6263         appData.noChessProgram = TRUE;\r
6264         appData.icsActive = FALSE;\r
6265       } else {\r
6266         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6267                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6268         return TRUE;\r
6269       }\r
6270       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6271         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6272         p = buf;\r
6273         ParseArgs(StringGet, &p);\r
6274       }\r
6275       EndDialog(hDlg, TRUE);\r
6276       return TRUE;\r
6277 \r
6278     case IDCANCEL:\r
6279       ExitEvent(0);\r
6280       return TRUE;\r
6281 \r
6282     case IDM_HELPCONTENTS:\r
6283       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6284         MessageBox (GetFocus(),\r
6285                     _("Unable to activate help"),\r
6286                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6287       }\r
6288       break;\r
6289 \r
6290     default:\r
6291       SetStartupDialogEnables(hDlg);\r
6292       break;\r
6293     }\r
6294     break;\r
6295   }\r
6296   return FALSE;\r
6297 }\r
6298 \r
6299 /*---------------------------------------------------------------------------*\\r
6300  *\r
6301  * About box dialog functions\r
6302  *\r
6303 \*---------------------------------------------------------------------------*/\r
6304 \r
6305 /* Process messages for "About" dialog box */\r
6306 LRESULT CALLBACK\r
6307 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6308 {\r
6309   switch (message) {\r
6310   case WM_INITDIALOG: /* message: initialize dialog box */\r
6311     /* Center the dialog over the application window */\r
6312     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6313     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6314     Translate(hDlg, ABOUTBOX);\r
6315     JAWS_COPYRIGHT\r
6316     return (TRUE);\r
6317 \r
6318   case WM_COMMAND: /* message: received a command */\r
6319     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6320         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6321       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6322       return (TRUE);\r
6323     }\r
6324     break;\r
6325   }\r
6326   return (FALSE);\r
6327 }\r
6328 \r
6329 /*---------------------------------------------------------------------------*\\r
6330  *\r
6331  * Comment Dialog functions\r
6332  *\r
6333 \*---------------------------------------------------------------------------*/\r
6334 \r
6335 LRESULT CALLBACK\r
6336 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6337 {\r
6338   static HANDLE hwndText = NULL;\r
6339   int len, newSizeX, newSizeY, flags;\r
6340   static int sizeX, sizeY;\r
6341   char *str;\r
6342   RECT rect;\r
6343   MINMAXINFO *mmi;\r
6344 \r
6345   switch (message) {\r
6346   case WM_INITDIALOG: /* message: initialize dialog box */\r
6347     /* Initialize the dialog items */\r
6348     Translate(hDlg, DLG_EditComment);\r
6349     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6350     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6351     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6352     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6353     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6354     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6355     SetWindowText(hDlg, commentTitle);\r
6356     if (editComment) {\r
6357       SetFocus(hwndText);\r
6358     } else {\r
6359       SetFocus(GetDlgItem(hDlg, IDOK));\r
6360     }\r
6361     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6362                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6363                 MAKELPARAM(FALSE, 0));\r
6364     /* Size and position the dialog */\r
6365     if (!commentDialog) {\r
6366       commentDialog = hDlg;\r
6367       flags = SWP_NOZORDER;\r
6368       GetClientRect(hDlg, &rect);\r
6369       sizeX = rect.right;\r
6370       sizeY = rect.bottom;\r
6371       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6372           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6373         WINDOWPLACEMENT wp;\r
6374         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6375         wp.length = sizeof(WINDOWPLACEMENT);\r
6376         wp.flags = 0;\r
6377         wp.showCmd = SW_SHOW;\r
6378         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6379         wp.rcNormalPosition.left = wpComment.x;\r
6380         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6381         wp.rcNormalPosition.top = wpComment.y;\r
6382         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6383         SetWindowPlacement(hDlg, &wp);\r
6384 \r
6385         GetClientRect(hDlg, &rect);\r
6386         newSizeX = rect.right;\r
6387         newSizeY = rect.bottom;\r
6388         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6389                               newSizeX, newSizeY);\r
6390         sizeX = newSizeX;\r
6391         sizeY = newSizeY;\r
6392       }\r
6393     }\r
6394     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6395     return FALSE;\r
6396 \r
6397   case WM_COMMAND: /* message: received a command */\r
6398     switch (LOWORD(wParam)) {\r
6399     case IDOK:\r
6400       if (editComment) {\r
6401         char *p, *q;\r
6402         /* Read changed options from the dialog box */\r
6403         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6404         len = GetWindowTextLength(hwndText);\r
6405         str = (char *) malloc(len + 1);\r
6406         GetWindowText(hwndText, str, len + 1);\r
6407         p = q = str;\r
6408         while (*q) {\r
6409           if (*q == '\r')\r
6410             q++;\r
6411           else\r
6412             *p++ = *q++;\r
6413         }\r
6414         *p = NULLCHAR;\r
6415         ReplaceComment(commentIndex, str);\r
6416         free(str);\r
6417       }\r
6418       CommentPopDown();\r
6419       return TRUE;\r
6420 \r
6421     case IDCANCEL:\r
6422     case OPT_CancelComment:\r
6423       CommentPopDown();\r
6424       return TRUE;\r
6425 \r
6426     case OPT_ClearComment:\r
6427       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6428       break;\r
6429 \r
6430     case OPT_EditComment:\r
6431       EditCommentEvent();\r
6432       return TRUE;\r
6433 \r
6434     default:\r
6435       break;\r
6436     }\r
6437     break;\r
6438 \r
6439   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6440         if( wParam == OPT_CommentText ) {\r
6441             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6442 \r
6443             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6444                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6445                 POINTL pt;\r
6446                 LRESULT index;\r
6447 \r
6448                 pt.x = LOWORD( lpMF->lParam );\r
6449                 pt.y = HIWORD( lpMF->lParam );\r
6450 \r
6451                 if(lpMF->msg == WM_CHAR) {\r
6452                         CHARRANGE sel;\r
6453                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6454                         index = sel.cpMin;\r
6455                 } else\r
6456                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6457 \r
6458                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6459                 len = GetWindowTextLength(hwndText);\r
6460                 str = (char *) malloc(len + 1);\r
6461                 GetWindowText(hwndText, str, len + 1);\r
6462                 ReplaceComment(commentIndex, str);\r
6463                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6464                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6465                 free(str);\r
6466 \r
6467                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6468                 lpMF->msg = WM_USER;\r
6469 \r
6470                 return TRUE;\r
6471             }\r
6472         }\r
6473         break;\r
6474 \r
6475   case WM_SIZE:\r
6476     newSizeX = LOWORD(lParam);\r
6477     newSizeY = HIWORD(lParam);\r
6478     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6479     sizeX = newSizeX;\r
6480     sizeY = newSizeY;\r
6481     break;\r
6482 \r
6483   case WM_GETMINMAXINFO:\r
6484     /* Prevent resizing window too small */\r
6485     mmi = (MINMAXINFO *) lParam;\r
6486     mmi->ptMinTrackSize.x = 100;\r
6487     mmi->ptMinTrackSize.y = 100;\r
6488     break;\r
6489   }\r
6490   return FALSE;\r
6491 }\r
6492 \r
6493 VOID\r
6494 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6495 {\r
6496   FARPROC lpProc;\r
6497   char *p, *q;\r
6498 \r
6499   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6500 \r
6501   if (str == NULL) str = "";\r
6502   p = (char *) malloc(2 * strlen(str) + 2);\r
6503   q = p;\r
6504   while (*str) {\r
6505     if (*str == '\n') *q++ = '\r';\r
6506     *q++ = *str++;\r
6507   }\r
6508   *q = NULLCHAR;\r
6509   if (commentText != NULL) free(commentText);\r
6510 \r
6511   commentIndex = index;\r
6512   commentTitle = title;\r
6513   commentText = p;\r
6514   editComment = edit;\r
6515 \r
6516   if (commentDialog) {\r
6517     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6518     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6519   } else {\r
6520     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6521     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6522                  hwndMain, (DLGPROC)lpProc);\r
6523     FreeProcInstance(lpProc);\r
6524   }\r
6525   commentUp = TRUE;\r
6526 }\r
6527 \r
6528 \r
6529 /*---------------------------------------------------------------------------*\\r
6530  *\r
6531  * Type-in move dialog functions\r
6532  * \r
6533 \*---------------------------------------------------------------------------*/\r
6534 \r
6535 LRESULT CALLBACK\r
6536 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6537 {\r
6538   char move[MSG_SIZ];\r
6539   HWND hInput;\r
6540 \r
6541   switch (message) {\r
6542   case WM_INITDIALOG:\r
6543     move[0] = (char) lParam;\r
6544     move[1] = NULLCHAR;\r
6545     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6546     Translate(hDlg, DLG_TypeInMove);\r
6547     hInput = GetDlgItem(hDlg, OPT_Move);\r
6548     SetWindowText(hInput, move);\r
6549     SetFocus(hInput);\r
6550     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6551     return FALSE;\r
6552 \r
6553   case WM_COMMAND:\r
6554     switch (LOWORD(wParam)) {\r
6555     case IDOK:\r
6556 \r
6557       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6558       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6559       TypeInDoneEvent(move);\r
6560       EndDialog(hDlg, TRUE);\r
6561       return TRUE;\r
6562     case IDCANCEL:\r
6563       EndDialog(hDlg, FALSE);\r
6564       return TRUE;\r
6565     default:\r
6566       break;\r
6567     }\r
6568     break;\r
6569   }\r
6570   return FALSE;\r
6571 }\r
6572 \r
6573 VOID\r
6574 PopUpMoveDialog(char firstchar)\r
6575 {\r
6576     FARPROC lpProc;\r
6577 \r
6578       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6579       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6580         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6581       FreeProcInstance(lpProc);\r
6582 }\r
6583 \r
6584 /*---------------------------------------------------------------------------*\\r
6585  *\r
6586  * Type-in name dialog functions\r
6587  * \r
6588 \*---------------------------------------------------------------------------*/\r
6589 \r
6590 LRESULT CALLBACK\r
6591 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6592 {\r
6593   char move[MSG_SIZ];\r
6594   HWND hInput;\r
6595 \r
6596   switch (message) {\r
6597   case WM_INITDIALOG:\r
6598     move[0] = (char) lParam;\r
6599     move[1] = NULLCHAR;\r
6600     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6601     Translate(hDlg, DLG_TypeInName);\r
6602     hInput = GetDlgItem(hDlg, OPT_Name);\r
6603     SetWindowText(hInput, move);\r
6604     SetFocus(hInput);\r
6605     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6606     return FALSE;\r
6607 \r
6608   case WM_COMMAND:\r
6609     switch (LOWORD(wParam)) {\r
6610     case IDOK:\r
6611       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6612       appData.userName = strdup(move);\r
6613       SetUserLogo();\r
6614       SetGameInfo();\r
6615       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6616         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6617         DisplayTitle(move);\r
6618       }\r
6619 \r
6620 \r
6621       EndDialog(hDlg, TRUE);\r
6622       return TRUE;\r
6623     case IDCANCEL:\r
6624       EndDialog(hDlg, FALSE);\r
6625       return TRUE;\r
6626     default:\r
6627       break;\r
6628     }\r
6629     break;\r
6630   }\r
6631   return FALSE;\r
6632 }\r
6633 \r
6634 VOID\r
6635 PopUpNameDialog(char firstchar)\r
6636 {\r
6637     FARPROC lpProc;\r
6638     \r
6639       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6640       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6641         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6642       FreeProcInstance(lpProc);\r
6643 }\r
6644 \r
6645 /*---------------------------------------------------------------------------*\\r
6646  *\r
6647  *  Error dialogs\r
6648  * \r
6649 \*---------------------------------------------------------------------------*/\r
6650 \r
6651 /* Nonmodal error box */\r
6652 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6653                              WPARAM wParam, LPARAM lParam);\r
6654 \r
6655 VOID\r
6656 ErrorPopUp(char *title, char *content)\r
6657 {\r
6658   FARPROC lpProc;\r
6659   char *p, *q;\r
6660   BOOLEAN modal = hwndMain == NULL;\r
6661 \r
6662   p = content;\r
6663   q = errorMessage;\r
6664   while (*p) {\r
6665     if (*p == '\n') {\r
6666       if (modal) {\r
6667         *q++ = ' ';\r
6668         p++;\r
6669       } else {\r
6670         *q++ = '\r';\r
6671         *q++ = *p++;\r
6672       }\r
6673     } else {\r
6674       *q++ = *p++;\r
6675     }\r
6676   }\r
6677   *q = NULLCHAR;\r
6678   strncpy(errorTitle, title, sizeof(errorTitle));\r
6679   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6680   \r
6681   if (modal) {\r
6682     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6683   } else {\r
6684     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6685     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6686                  hwndMain, (DLGPROC)lpProc);\r
6687     FreeProcInstance(lpProc);\r
6688   }\r
6689 }\r
6690 \r
6691 VOID\r
6692 ErrorPopDown()\r
6693 {\r
6694   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6695   if (errorDialog == NULL) return;\r
6696   DestroyWindow(errorDialog);\r
6697   errorDialog = NULL;\r
6698   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6699 }\r
6700 \r
6701 LRESULT CALLBACK\r
6702 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6703 {\r
6704   HANDLE hwndText;\r
6705   RECT rChild;\r
6706 \r
6707   switch (message) {\r
6708   case WM_INITDIALOG:\r
6709     GetWindowRect(hDlg, &rChild);\r
6710 \r
6711     /*\r
6712     SetWindowPos(hDlg, NULL, rChild.left,\r
6713       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6714       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6715     */\r
6716 \r
6717     /* \r
6718         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6719         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6720         and it doesn't work when you resize the dialog.\r
6721         For now, just give it a default position.\r
6722     */\r
6723     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6724     Translate(hDlg, DLG_Error);\r
6725 \r
6726     errorDialog = hDlg;\r
6727     SetWindowText(hDlg, errorTitle);\r
6728     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6729     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6730     return FALSE;\r
6731 \r
6732   case WM_COMMAND:\r
6733     switch (LOWORD(wParam)) {\r
6734     case IDOK:\r
6735     case IDCANCEL:\r
6736       if (errorDialog == hDlg) errorDialog = NULL;\r
6737       DestroyWindow(hDlg);\r
6738       return TRUE;\r
6739 \r
6740     default:\r
6741       break;\r
6742     }\r
6743     break;\r
6744   }\r
6745   return FALSE;\r
6746 }\r
6747 \r
6748 #ifdef GOTHIC\r
6749 HWND gothicDialog = NULL;\r
6750 \r
6751 LRESULT CALLBACK\r
6752 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6753 {\r
6754   HANDLE hwndText;\r
6755   RECT rChild;\r
6756   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6757 \r
6758   switch (message) {\r
6759   case WM_INITDIALOG:\r
6760     GetWindowRect(hDlg, &rChild);\r
6761 \r
6762     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6763                                                              SWP_NOZORDER);\r
6764 \r
6765     /* \r
6766         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6767         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6768         and it doesn't work when you resize the dialog.\r
6769         For now, just give it a default position.\r
6770     */\r
6771     gothicDialog = hDlg;\r
6772     SetWindowText(hDlg, errorTitle);\r
6773     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6774     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6775     return FALSE;\r
6776 \r
6777   case WM_COMMAND:\r
6778     switch (LOWORD(wParam)) {\r
6779     case IDOK:\r
6780     case IDCANCEL:\r
6781       if (errorDialog == hDlg) errorDialog = NULL;\r
6782       DestroyWindow(hDlg);\r
6783       return TRUE;\r
6784 \r
6785     default:\r
6786       break;\r
6787     }\r
6788     break;\r
6789   }\r
6790   return FALSE;\r
6791 }\r
6792 \r
6793 VOID\r
6794 GothicPopUp(char *title, VariantClass variant)\r
6795 {\r
6796   FARPROC lpProc;\r
6797   static char *lastTitle;\r
6798 \r
6799   strncpy(errorTitle, title, sizeof(errorTitle));\r
6800   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6801 \r
6802   if(lastTitle != title && gothicDialog != NULL) {\r
6803     DestroyWindow(gothicDialog);\r
6804     gothicDialog = NULL;\r
6805   }\r
6806   if(variant != VariantNormal && gothicDialog == NULL) {\r
6807     title = lastTitle;\r
6808     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6809     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6810                  hwndMain, (DLGPROC)lpProc);\r
6811     FreeProcInstance(lpProc);\r
6812   }\r
6813 }\r
6814 #endif\r
6815 \r
6816 /*---------------------------------------------------------------------------*\\r
6817  *\r
6818  *  Ics Interaction console functions\r
6819  *\r
6820 \*---------------------------------------------------------------------------*/\r
6821 \r
6822 #define HISTORY_SIZE 64\r
6823 static char *history[HISTORY_SIZE];\r
6824 int histIn = 0, histP = 0;\r
6825 \r
6826 VOID\r
6827 SaveInHistory(char *cmd)\r
6828 {\r
6829   if (history[histIn] != NULL) {\r
6830     free(history[histIn]);\r
6831     history[histIn] = NULL;\r
6832   }\r
6833   if (*cmd == NULLCHAR) return;\r
6834   history[histIn] = StrSave(cmd);\r
6835   histIn = (histIn + 1) % HISTORY_SIZE;\r
6836   if (history[histIn] != NULL) {\r
6837     free(history[histIn]);\r
6838     history[histIn] = NULL;\r
6839   }\r
6840   histP = histIn;\r
6841 }\r
6842 \r
6843 char *\r
6844 PrevInHistory(char *cmd)\r
6845 {\r
6846   int newhp;\r
6847   if (histP == histIn) {\r
6848     if (history[histIn] != NULL) free(history[histIn]);\r
6849     history[histIn] = StrSave(cmd);\r
6850   }\r
6851   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6852   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6853   histP = newhp;\r
6854   return history[histP];\r
6855 }\r
6856 \r
6857 char *\r
6858 NextInHistory()\r
6859 {\r
6860   if (histP == histIn) return NULL;\r
6861   histP = (histP + 1) % HISTORY_SIZE;\r
6862   return history[histP];   \r
6863 }\r
6864 \r
6865 HMENU\r
6866 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6867 {\r
6868   HMENU hmenu, h;\r
6869   int i = 0;\r
6870   hmenu = LoadMenu(hInst, "TextMenu");\r
6871   h = GetSubMenu(hmenu, 0);\r
6872   while (e->item) {\r
6873     if (strcmp(e->item, "-") == 0) {\r
6874       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6875     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6876       int flags = MF_STRING, j = 0;\r
6877       if (e->item[0] == '|') {\r
6878         flags |= MF_MENUBARBREAK;\r
6879         j++;\r
6880       }\r
6881       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6882       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6883     }\r
6884     e++;\r
6885     i++;\r
6886   } \r
6887   return hmenu;\r
6888 }\r
6889 \r
6890 WNDPROC consoleTextWindowProc;\r
6891 \r
6892 void\r
6893 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6894 {\r
6895   char buf[MSG_SIZ], name[MSG_SIZ];\r
6896   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6897   CHARRANGE sel;\r
6898 \r
6899   if (!getname) {\r
6900     SetWindowText(hInput, command);\r
6901     if (immediate) {\r
6902       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6903     } else {\r
6904       sel.cpMin = 999999;\r
6905       sel.cpMax = 999999;\r
6906       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6907       SetFocus(hInput);\r
6908     }\r
6909     return;\r
6910   }    \r
6911   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6912   if (sel.cpMin == sel.cpMax) {\r
6913     /* Expand to surrounding word */\r
6914     TEXTRANGE tr;\r
6915     do {\r
6916       tr.chrg.cpMax = sel.cpMin;\r
6917       tr.chrg.cpMin = --sel.cpMin;\r
6918       if (sel.cpMin < 0) break;\r
6919       tr.lpstrText = name;\r
6920       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6921     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6922     sel.cpMin++;\r
6923 \r
6924     do {\r
6925       tr.chrg.cpMin = sel.cpMax;\r
6926       tr.chrg.cpMax = ++sel.cpMax;\r
6927       tr.lpstrText = name;\r
6928       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6929     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6930     sel.cpMax--;\r
6931 \r
6932     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6933       MessageBeep(MB_ICONEXCLAMATION);\r
6934       return;\r
6935     }\r
6936     tr.chrg = sel;\r
6937     tr.lpstrText = name;\r
6938     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6939   } else {\r
6940     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6941       MessageBeep(MB_ICONEXCLAMATION);\r
6942       return;\r
6943     }\r
6944     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6945   }\r
6946   if (immediate) {\r
6947     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6948     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6949     SetWindowText(hInput, buf);\r
6950     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6951   } else {\r
6952     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6953       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6954     SetWindowText(hInput, buf);\r
6955     sel.cpMin = 999999;\r
6956     sel.cpMax = 999999;\r
6957     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6958     SetFocus(hInput);\r
6959   }\r
6960 }\r
6961 \r
6962 LRESULT CALLBACK \r
6963 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6964 {\r
6965   HWND hInput;\r
6966   CHARRANGE sel;\r
6967 \r
6968   switch (message) {\r
6969   case WM_KEYDOWN:\r
6970     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6971     if(wParam=='R') return 0;\r
6972     switch (wParam) {\r
6973     case VK_PRIOR:\r
6974       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6975       return 0;\r
6976     case VK_NEXT:\r
6977       sel.cpMin = 999999;\r
6978       sel.cpMax = 999999;\r
6979       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6980       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6981       return 0;\r
6982     }\r
6983     break;\r
6984   case WM_CHAR:\r
6985    if(wParam != '\022') {\r
6986     if (wParam == '\t') {\r
6987       if (GetKeyState(VK_SHIFT) < 0) {\r
6988         /* shifted */\r
6989         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6990         if (buttonDesc[0].hwnd) {\r
6991           SetFocus(buttonDesc[0].hwnd);\r
6992         } else {\r
6993           SetFocus(hwndMain);\r
6994         }\r
6995       } else {\r
6996         /* unshifted */\r
6997         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6998       }\r
6999     } else {\r
7000       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7001       JAWS_DELETE( SetFocus(hInput); )\r
7002       SendMessage(hInput, message, wParam, lParam);\r
7003     }\r
7004     return 0;\r
7005    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7006    lParam = -1;\r
7007   case WM_RBUTTONDOWN:\r
7008     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7009       /* Move selection here if it was empty */\r
7010       POINT pt;\r
7011       pt.x = LOWORD(lParam);\r
7012       pt.y = HIWORD(lParam);\r
7013       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7014       if (sel.cpMin == sel.cpMax) {\r
7015         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7016         sel.cpMax = sel.cpMin;\r
7017         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7018       }\r
7019       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7020 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7021       POINT pt;\r
7022       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7023       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7024       if (sel.cpMin == sel.cpMax) {\r
7025         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7026         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7027       }\r
7028       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7029         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7030       }\r
7031       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7032       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7033       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7034       MenuPopup(hwnd, pt, hmenu, -1);\r
7035 }\r
7036     }\r
7037     return 0;\r
7038   case WM_RBUTTONUP:\r
7039     if (GetKeyState(VK_SHIFT) & ~1) {\r
7040       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7041         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7042     }\r
7043     return 0;\r
7044   case WM_PASTE:\r
7045     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7046     SetFocus(hInput);\r
7047     return SendMessage(hInput, message, wParam, lParam);\r
7048   case WM_MBUTTONDOWN:\r
7049     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7050   case WM_COMMAND:\r
7051     switch (LOWORD(wParam)) {\r
7052     case IDM_QuickPaste:\r
7053       {\r
7054         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7055         if (sel.cpMin == sel.cpMax) {\r
7056           MessageBeep(MB_ICONEXCLAMATION);\r
7057           return 0;\r
7058         }\r
7059         SendMessage(hwnd, WM_COPY, 0, 0);\r
7060         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7061         SendMessage(hInput, WM_PASTE, 0, 0);\r
7062         SetFocus(hInput);\r
7063         return 0;\r
7064       }\r
7065     case IDM_Cut:\r
7066       SendMessage(hwnd, WM_CUT, 0, 0);\r
7067       return 0;\r
7068     case IDM_Paste:\r
7069       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7070       return 0;\r
7071     case IDM_Copy:\r
7072       SendMessage(hwnd, WM_COPY, 0, 0);\r
7073       return 0;\r
7074     default:\r
7075       {\r
7076         int i = LOWORD(wParam) - IDM_CommandX;\r
7077         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7078             icsTextMenuEntry[i].command != NULL) {\r
7079           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7080                    icsTextMenuEntry[i].getname,\r
7081                    icsTextMenuEntry[i].immediate);\r
7082           return 0;\r
7083         }\r
7084       }\r
7085       break;\r
7086     }\r
7087     break;\r
7088   }\r
7089   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7090 }\r
7091 \r
7092 WNDPROC consoleInputWindowProc;\r
7093 \r
7094 LRESULT CALLBACK\r
7095 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7096 {\r
7097   char buf[MSG_SIZ];\r
7098   char *p;\r
7099   static BOOL sendNextChar = FALSE;\r
7100   static BOOL quoteNextChar = FALSE;\r
7101   InputSource *is = consoleInputSource;\r
7102   CHARFORMAT cf;\r
7103   CHARRANGE sel;\r
7104 \r
7105   switch (message) {\r
7106   case WM_CHAR:\r
7107     if (!appData.localLineEditing || sendNextChar) {\r
7108       is->buf[0] = (CHAR) wParam;\r
7109       is->count = 1;\r
7110       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7111       sendNextChar = FALSE;\r
7112       return 0;\r
7113     }\r
7114     if (quoteNextChar) {\r
7115       buf[0] = (char) wParam;\r
7116       buf[1] = NULLCHAR;\r
7117       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7118       quoteNextChar = FALSE;\r
7119       return 0;\r
7120     }\r
7121     switch (wParam) {\r
7122     case '\r':   /* Enter key */\r
7123       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7124       if (consoleEcho) SaveInHistory(is->buf);\r
7125       is->buf[is->count++] = '\n';\r
7126       is->buf[is->count] = NULLCHAR;\r
7127       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7128       if (consoleEcho) {\r
7129         ConsoleOutput(is->buf, is->count, TRUE);\r
7130       } else if (appData.localLineEditing) {\r
7131         ConsoleOutput("\n", 1, TRUE);\r
7132       }\r
7133       /* fall thru */\r
7134     case '\033': /* Escape key */\r
7135       SetWindowText(hwnd, "");\r
7136       cf.cbSize = sizeof(CHARFORMAT);\r
7137       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7138       if (consoleEcho) {\r
7139         cf.crTextColor = textAttribs[ColorNormal].color;\r
7140       } else {\r
7141         cf.crTextColor = COLOR_ECHOOFF;\r
7142       }\r
7143       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7144       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7145       return 0;\r
7146     case '\t':   /* Tab key */\r
7147       if (GetKeyState(VK_SHIFT) < 0) {\r
7148         /* shifted */\r
7149         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7150       } else {\r
7151         /* unshifted */\r
7152         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7153         if (buttonDesc[0].hwnd) {\r
7154           SetFocus(buttonDesc[0].hwnd);\r
7155         } else {\r
7156           SetFocus(hwndMain);\r
7157         }\r
7158       }\r
7159       return 0;\r
7160     case '\023': /* Ctrl+S */\r
7161       sendNextChar = TRUE;\r
7162       return 0;\r
7163     case '\021': /* Ctrl+Q */\r
7164       quoteNextChar = TRUE;\r
7165       return 0;\r
7166     JAWS_REPLAY\r
7167     default:\r
7168       break;\r
7169     }\r
7170     break;\r
7171   case WM_KEYDOWN:\r
7172     switch (wParam) {\r
7173     case VK_UP:\r
7174       GetWindowText(hwnd, buf, MSG_SIZ);\r
7175       p = PrevInHistory(buf);\r
7176       if (p != NULL) {\r
7177         SetWindowText(hwnd, p);\r
7178         sel.cpMin = 999999;\r
7179         sel.cpMax = 999999;\r
7180         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7181         return 0;\r
7182       }\r
7183       break;\r
7184     case VK_DOWN:\r
7185       p = NextInHistory();\r
7186       if (p != NULL) {\r
7187         SetWindowText(hwnd, p);\r
7188         sel.cpMin = 999999;\r
7189         sel.cpMax = 999999;\r
7190         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7191         return 0;\r
7192       }\r
7193       break;\r
7194     case VK_HOME:\r
7195     case VK_END:\r
7196       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7197       /* fall thru */\r
7198     case VK_PRIOR:\r
7199     case VK_NEXT:\r
7200       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7201       return 0;\r
7202     }\r
7203     break;\r
7204   case WM_MBUTTONDOWN:\r
7205     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7206       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7207     break;\r
7208   case WM_RBUTTONUP:\r
7209     if (GetKeyState(VK_SHIFT) & ~1) {\r
7210       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7211         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7212     } else {\r
7213       POINT pt;\r
7214       HMENU hmenu;\r
7215       hmenu = LoadMenu(hInst, "InputMenu");\r
7216       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7217       if (sel.cpMin == sel.cpMax) {\r
7218         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7219         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7220       }\r
7221       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7222         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7223       }\r
7224       pt.x = LOWORD(lParam);\r
7225       pt.y = HIWORD(lParam);\r
7226       MenuPopup(hwnd, pt, hmenu, -1);\r
7227     }\r
7228     return 0;\r
7229   case WM_COMMAND:\r
7230     switch (LOWORD(wParam)) { \r
7231     case IDM_Undo:\r
7232       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7233       return 0;\r
7234     case IDM_SelectAll:\r
7235       sel.cpMin = 0;\r
7236       sel.cpMax = -1; /*999999?*/\r
7237       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7238       return 0;\r
7239     case IDM_Cut:\r
7240       SendMessage(hwnd, WM_CUT, 0, 0);\r
7241       return 0;\r
7242     case IDM_Paste:\r
7243       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7244       return 0;\r
7245     case IDM_Copy:\r
7246       SendMessage(hwnd, WM_COPY, 0, 0);\r
7247       return 0;\r
7248     }\r
7249     break;\r
7250   }\r
7251   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7252 }\r
7253 \r
7254 #define CO_MAX  100000\r
7255 #define CO_TRIM   1000\r
7256 \r
7257 LRESULT CALLBACK\r
7258 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7259 {\r
7260   static SnapData sd;\r
7261   HWND hText, hInput;\r
7262   RECT rect;\r
7263   static int sizeX, sizeY;\r
7264   int newSizeX, newSizeY;\r
7265   MINMAXINFO *mmi;\r
7266   WORD wMask;\r
7267 \r
7268   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7269   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7270 \r
7271   switch (message) {\r
7272   case WM_NOTIFY:\r
7273     if (((NMHDR*)lParam)->code == EN_LINK)\r
7274     {\r
7275       ENLINK *pLink = (ENLINK*)lParam;\r
7276       if (pLink->msg == WM_LBUTTONUP)\r
7277       {\r
7278         TEXTRANGE tr;\r
7279 \r
7280         tr.chrg = pLink->chrg;\r
7281         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7282         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7283         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7284         free(tr.lpstrText);\r
7285       }\r
7286     }\r
7287     break;\r
7288   case WM_INITDIALOG: /* message: initialize dialog box */\r
7289     hwndConsole = hDlg;\r
7290     SetFocus(hInput);\r
7291     consoleTextWindowProc = (WNDPROC)\r
7292       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7293     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7294     consoleInputWindowProc = (WNDPROC)\r
7295       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7296     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7297     Colorize(ColorNormal, TRUE);\r
7298     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7299     ChangedConsoleFont();\r
7300     GetClientRect(hDlg, &rect);\r
7301     sizeX = rect.right;\r
7302     sizeY = rect.bottom;\r
7303     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7304         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7305       WINDOWPLACEMENT wp;\r
7306       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7307       wp.length = sizeof(WINDOWPLACEMENT);\r
7308       wp.flags = 0;\r
7309       wp.showCmd = SW_SHOW;\r
7310       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7311       wp.rcNormalPosition.left = wpConsole.x;\r
7312       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7313       wp.rcNormalPosition.top = wpConsole.y;\r
7314       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7315       SetWindowPlacement(hDlg, &wp);\r
7316     }\r
7317 \r
7318    // [HGM] Chessknight's change 2004-07-13\r
7319    else { /* Determine Defaults */\r
7320        WINDOWPLACEMENT wp;\r
7321        wpConsole.x = wpMain.width + 1;\r
7322        wpConsole.y = wpMain.y;\r
7323        wpConsole.width = screenWidth -  wpMain.width;\r
7324        wpConsole.height = wpMain.height;\r
7325        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7326        wp.length = sizeof(WINDOWPLACEMENT);\r
7327        wp.flags = 0;\r
7328        wp.showCmd = SW_SHOW;\r
7329        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7330        wp.rcNormalPosition.left = wpConsole.x;\r
7331        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7332        wp.rcNormalPosition.top = wpConsole.y;\r
7333        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7334        SetWindowPlacement(hDlg, &wp);\r
7335     }\r
7336 \r
7337    // Allow hText to highlight URLs and send notifications on them\r
7338    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7339    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7340    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7341    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7342 \r
7343     return FALSE;\r
7344 \r
7345   case WM_SETFOCUS:\r
7346     SetFocus(hInput);\r
7347     return 0;\r
7348 \r
7349   case WM_CLOSE:\r
7350     ExitEvent(0);\r
7351     /* not reached */\r
7352     break;\r
7353 \r
7354   case WM_SIZE:\r
7355     if (IsIconic(hDlg)) break;\r
7356     newSizeX = LOWORD(lParam);\r
7357     newSizeY = HIWORD(lParam);\r
7358     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7359       RECT rectText, rectInput;\r
7360       POINT pt;\r
7361       int newTextHeight, newTextWidth;\r
7362       GetWindowRect(hText, &rectText);\r
7363       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7364       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7365       if (newTextHeight < 0) {\r
7366         newSizeY += -newTextHeight;\r
7367         newTextHeight = 0;\r
7368       }\r
7369       SetWindowPos(hText, NULL, 0, 0,\r
7370         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7371       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7372       pt.x = rectInput.left;\r
7373       pt.y = rectInput.top + newSizeY - sizeY;\r
7374       ScreenToClient(hDlg, &pt);\r
7375       SetWindowPos(hInput, NULL, \r
7376         pt.x, pt.y, /* needs client coords */   \r
7377         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7378         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7379     }\r
7380     sizeX = newSizeX;\r
7381     sizeY = newSizeY;\r
7382     break;\r
7383 \r
7384   case WM_GETMINMAXINFO:\r
7385     /* Prevent resizing window too small */\r
7386     mmi = (MINMAXINFO *) lParam;\r
7387     mmi->ptMinTrackSize.x = 100;\r
7388     mmi->ptMinTrackSize.y = 100;\r
7389     break;\r
7390 \r
7391   /* [AS] Snapping */\r
7392   case WM_ENTERSIZEMOVE:\r
7393     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7394 \r
7395   case WM_SIZING:\r
7396     return OnSizing( &sd, hDlg, wParam, lParam );\r
7397 \r
7398   case WM_MOVING:\r
7399     return OnMoving( &sd, hDlg, wParam, lParam );\r
7400 \r
7401   case WM_EXITSIZEMOVE:\r
7402         UpdateICSWidth(hText);\r
7403     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7404   }\r
7405 \r
7406   return DefWindowProc(hDlg, message, wParam, lParam);\r
7407 }\r
7408 \r
7409 \r
7410 VOID\r
7411 ConsoleCreate()\r
7412 {\r
7413   HWND hCons;\r
7414   if (hwndConsole) return;\r
7415   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7416   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7417 }\r
7418 \r
7419 \r
7420 VOID\r
7421 ConsoleOutput(char* data, int length, int forceVisible)\r
7422 {\r
7423   HWND hText;\r
7424   int trim, exlen;\r
7425   char *p, *q;\r
7426   char buf[CO_MAX+1];\r
7427   POINT pEnd;\r
7428   RECT rect;\r
7429   static int delayLF = 0;\r
7430   CHARRANGE savesel, sel;\r
7431 \r
7432   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7433   p = data;\r
7434   q = buf;\r
7435   if (delayLF) {\r
7436     *q++ = '\r';\r
7437     *q++ = '\n';\r
7438     delayLF = 0;\r
7439   }\r
7440   while (length--) {\r
7441     if (*p == '\n') {\r
7442       if (*++p) {\r
7443         *q++ = '\r';\r
7444         *q++ = '\n';\r
7445       } else {\r
7446         delayLF = 1;\r
7447       }\r
7448     } else if (*p == '\007') {\r
7449        MyPlaySound(&sounds[(int)SoundBell]);\r
7450        p++;\r
7451     } else {\r
7452       *q++ = *p++;\r
7453     }\r
7454   }\r
7455   *q = NULLCHAR;\r
7456   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7457   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7458   /* Save current selection */\r
7459   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7460   exlen = GetWindowTextLength(hText);\r
7461   /* Find out whether current end of text is visible */\r
7462   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7463   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7464   /* Trim existing text if it's too long */\r
7465   if (exlen + (q - buf) > CO_MAX) {\r
7466     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7467     sel.cpMin = 0;\r
7468     sel.cpMax = trim;\r
7469     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7470     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7471     exlen -= trim;\r
7472     savesel.cpMin -= trim;\r
7473     savesel.cpMax -= trim;\r
7474     if (exlen < 0) exlen = 0;\r
7475     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7476     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7477   }\r
7478   /* Append the new text */\r
7479   sel.cpMin = exlen;\r
7480   sel.cpMax = exlen;\r
7481   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7482   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7483   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7484   if (forceVisible || exlen == 0 ||\r
7485       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7486        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7487     /* Scroll to make new end of text visible if old end of text\r
7488        was visible or new text is an echo of user typein */\r
7489     sel.cpMin = 9999999;\r
7490     sel.cpMax = 9999999;\r
7491     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7492     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7493     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7494     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7495   }\r
7496   if (savesel.cpMax == exlen || forceVisible) {\r
7497     /* Move insert point to new end of text if it was at the old\r
7498        end of text or if the new text is an echo of user typein */\r
7499     sel.cpMin = 9999999;\r
7500     sel.cpMax = 9999999;\r
7501     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7502   } else {\r
7503     /* Restore previous selection */\r
7504     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7505   }\r
7506   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7507 }\r
7508 \r
7509 /*---------*/\r
7510 \r
7511 \r
7512 void\r
7513 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7514 {\r
7515   char buf[100];\r
7516   char *str;\r
7517   COLORREF oldFg, oldBg;\r
7518   HFONT oldFont;\r
7519   RECT rect;\r
7520 \r
7521   if(copyNumber > 1)\r
7522     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7523 \r
7524   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7525   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7526   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7527 \r
7528   rect.left = x;\r
7529   rect.right = x + squareSize;\r
7530   rect.top  = y;\r
7531   rect.bottom = y + squareSize;\r
7532   str = buf;\r
7533 \r
7534   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7535                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7536              y, ETO_CLIPPED|ETO_OPAQUE,\r
7537              &rect, str, strlen(str), NULL);\r
7538 \r
7539   (void) SetTextColor(hdc, oldFg);\r
7540   (void) SetBkColor(hdc, oldBg);\r
7541   (void) SelectObject(hdc, oldFont);\r
7542 }\r
7543 \r
7544 void\r
7545 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7546               RECT *rect, char *color, char *flagFell)\r
7547 {\r
7548   char buf[100];\r
7549   char *str;\r
7550   COLORREF oldFg, oldBg;\r
7551   HFONT oldFont;\r
7552 \r
7553   if (twoBoards && partnerUp) return;\r
7554   if (appData.clockMode) {\r
7555     if (tinyLayout)\r
7556       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7557     else\r
7558       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7559     str = buf;\r
7560   } else {\r
7561     str = color;\r
7562   }\r
7563 \r
7564   if (highlight) {\r
7565     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7566     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7567   } else {\r
7568     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7569     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7570   }\r
7571   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7572 \r
7573   JAWS_SILENCE\r
7574 \r
7575   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7576              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7577              rect, str, strlen(str), NULL);\r
7578   if(logoHeight > 0 && appData.clockMode) {\r
7579       RECT r;\r
7580       str += strlen(color)+2;\r
7581       r.top = rect->top + logoHeight/2;\r
7582       r.left = rect->left;\r
7583       r.right = rect->right;\r
7584       r.bottom = rect->bottom;\r
7585       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7586                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7587                  &r, str, strlen(str), NULL);\r
7588   }\r
7589   (void) SetTextColor(hdc, oldFg);\r
7590   (void) SetBkColor(hdc, oldBg);\r
7591   (void) SelectObject(hdc, oldFont);\r
7592 }\r
7593 \r
7594 \r
7595 int\r
7596 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7597            OVERLAPPED *ovl)\r
7598 {\r
7599   int ok, err;\r
7600 \r
7601   /* [AS]  */\r
7602   if( count <= 0 ) {\r
7603     if (appData.debugMode) {\r
7604       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7605     }\r
7606 \r
7607     return ERROR_INVALID_USER_BUFFER;\r
7608   }\r
7609 \r
7610   ResetEvent(ovl->hEvent);\r
7611   ovl->Offset = ovl->OffsetHigh = 0;\r
7612   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7613   if (ok) {\r
7614     err = NO_ERROR;\r
7615   } else {\r
7616     err = GetLastError();\r
7617     if (err == ERROR_IO_PENDING) {\r
7618       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7619       if (ok)\r
7620         err = NO_ERROR;\r
7621       else\r
7622         err = GetLastError();\r
7623     }\r
7624   }\r
7625   return err;\r
7626 }\r
7627 \r
7628 int\r
7629 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7630             OVERLAPPED *ovl)\r
7631 {\r
7632   int ok, err;\r
7633 \r
7634   ResetEvent(ovl->hEvent);\r
7635   ovl->Offset = ovl->OffsetHigh = 0;\r
7636   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7637   if (ok) {\r
7638     err = NO_ERROR;\r
7639   } else {\r
7640     err = GetLastError();\r
7641     if (err == ERROR_IO_PENDING) {\r
7642       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7643       if (ok)\r
7644         err = NO_ERROR;\r
7645       else\r
7646         err = GetLastError();\r
7647     }\r
7648   }\r
7649   return err;\r
7650 }\r
7651 \r
7652 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7653 void CheckForInputBufferFull( InputSource * is )\r
7654 {\r
7655     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7656         /* Look for end of line */\r
7657         char * p = is->buf;\r
7658         \r
7659         while( p < is->next && *p != '\n' ) {\r
7660             p++;\r
7661         }\r
7662 \r
7663         if( p >= is->next ) {\r
7664             if (appData.debugMode) {\r
7665                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7666             }\r
7667 \r
7668             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7669             is->count = (DWORD) -1;\r
7670             is->next = is->buf;\r
7671         }\r
7672     }\r
7673 }\r
7674 \r
7675 DWORD\r
7676 InputThread(LPVOID arg)\r
7677 {\r
7678   InputSource *is;\r
7679   OVERLAPPED ovl;\r
7680 \r
7681   is = (InputSource *) arg;\r
7682   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7683   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7684   while (is->hThread != NULL) {\r
7685     is->error = DoReadFile(is->hFile, is->next,\r
7686                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7687                            &is->count, &ovl);\r
7688     if (is->error == NO_ERROR) {\r
7689       is->next += is->count;\r
7690     } else {\r
7691       if (is->error == ERROR_BROKEN_PIPE) {\r
7692         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7693         is->count = 0;\r
7694       } else {\r
7695         is->count = (DWORD) -1;\r
7696         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7697         break; \r
7698       }\r
7699     }\r
7700 \r
7701     CheckForInputBufferFull( is );\r
7702 \r
7703     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7704 \r
7705     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7706 \r
7707     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7708   }\r
7709 \r
7710   CloseHandle(ovl.hEvent);\r
7711   CloseHandle(is->hFile);\r
7712 \r
7713   if (appData.debugMode) {\r
7714     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7715   }\r
7716 \r
7717   return 0;\r
7718 }\r
7719 \r
7720 \r
7721 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7722 DWORD\r
7723 NonOvlInputThread(LPVOID arg)\r
7724 {\r
7725   InputSource *is;\r
7726   char *p, *q;\r
7727   int i;\r
7728   char prev;\r
7729 \r
7730   is = (InputSource *) arg;\r
7731   while (is->hThread != NULL) {\r
7732     is->error = ReadFile(is->hFile, is->next,\r
7733                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7734                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7735     if (is->error == NO_ERROR) {\r
7736       /* Change CRLF to LF */\r
7737       if (is->next > is->buf) {\r
7738         p = is->next - 1;\r
7739         i = is->count + 1;\r
7740       } else {\r
7741         p = is->next;\r
7742         i = is->count;\r
7743       }\r
7744       q = p;\r
7745       prev = NULLCHAR;\r
7746       while (i > 0) {\r
7747         if (prev == '\r' && *p == '\n') {\r
7748           *(q-1) = '\n';\r
7749           is->count--;\r
7750         } else { \r
7751           *q++ = *p;\r
7752         }\r
7753         prev = *p++;\r
7754         i--;\r
7755       }\r
7756       *q = NULLCHAR;\r
7757       is->next = q;\r
7758     } else {\r
7759       if (is->error == ERROR_BROKEN_PIPE) {\r
7760         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7761         is->count = 0; \r
7762       } else {\r
7763         is->count = (DWORD) -1;\r
7764       }\r
7765     }\r
7766 \r
7767     CheckForInputBufferFull( is );\r
7768 \r
7769     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7770 \r
7771     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7772 \r
7773     if (is->count < 0) break;  /* Quit on error */\r
7774   }\r
7775   CloseHandle(is->hFile);\r
7776   return 0;\r
7777 }\r
7778 \r
7779 DWORD\r
7780 SocketInputThread(LPVOID arg)\r
7781 {\r
7782   InputSource *is;\r
7783 \r
7784   is = (InputSource *) arg;\r
7785   while (is->hThread != NULL) {\r
7786     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7787     if ((int)is->count == SOCKET_ERROR) {\r
7788       is->count = (DWORD) -1;\r
7789       is->error = WSAGetLastError();\r
7790     } else {\r
7791       is->error = NO_ERROR;\r
7792       is->next += is->count;\r
7793       if (is->count == 0 && is->second == is) {\r
7794         /* End of file on stderr; quit with no message */\r
7795         break;\r
7796       }\r
7797     }\r
7798     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7799 \r
7800     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7801 \r
7802     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7803   }\r
7804   return 0;\r
7805 }\r
7806 \r
7807 VOID\r
7808 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7809 {\r
7810   InputSource *is;\r
7811 \r
7812   is = (InputSource *) lParam;\r
7813   if (is->lineByLine) {\r
7814     /* Feed in lines one by one */\r
7815     char *p = is->buf;\r
7816     char *q = p;\r
7817     while (q < is->next) {\r
7818       if (*q++ == '\n') {\r
7819         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7820         p = q;\r
7821       }\r
7822     }\r
7823     \r
7824     /* Move any partial line to the start of the buffer */\r
7825     q = is->buf;\r
7826     while (p < is->next) {\r
7827       *q++ = *p++;\r
7828     }\r
7829     is->next = q;\r
7830 \r
7831     if (is->error != NO_ERROR || is->count == 0) {\r
7832       /* Notify backend of the error.  Note: If there was a partial\r
7833          line at the end, it is not flushed through. */\r
7834       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7835     }\r
7836   } else {\r
7837     /* Feed in the whole chunk of input at once */\r
7838     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7839     is->next = is->buf;\r
7840   }\r
7841 }\r
7842 \r
7843 /*---------------------------------------------------------------------------*\\r
7844  *\r
7845  *  Menu enables. Used when setting various modes.\r
7846  *\r
7847 \*---------------------------------------------------------------------------*/\r
7848 \r
7849 typedef struct {\r
7850   int item;\r
7851   int flags;\r
7852 } Enables;\r
7853 \r
7854 VOID\r
7855 GreyRevert(Boolean grey)\r
7856 { // [HGM] vari: for retracting variations in local mode\r
7857   HMENU hmenu = GetMenu(hwndMain);\r
7858   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7859   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7860 }\r
7861 \r
7862 VOID\r
7863 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7864 {\r
7865   while (enab->item > 0) {\r
7866     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7867     enab++;\r
7868   }\r
7869 }\r
7870 \r
7871 Enables gnuEnables[] = {\r
7872   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7873   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7874   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7875   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7878   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7885 \r
7886   // Needed to switch from ncp to GNU mode on Engine Load\r
7887   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7888   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7889   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7890   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7891   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7892   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7893   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7894   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7895   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7896   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7897   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7898   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7899   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7900   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7901   { -1, -1 }\r
7902 };\r
7903 \r
7904 Enables icsEnables[] = {\r
7905   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7913   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7921   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7925   { -1, -1 }\r
7926 };\r
7927 \r
7928 #if ZIPPY\r
7929 Enables zippyEnables[] = {\r
7930   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7931   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7932   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7934   { -1, -1 }\r
7935 };\r
7936 #endif\r
7937 \r
7938 Enables ncpEnables[] = {\r
7939   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7941   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7942   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7943   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7944   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7948   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7949   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7961   { -1, -1 }\r
7962 };\r
7963 \r
7964 Enables trainingOnEnables[] = {\r
7965   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7974   { -1, -1 }\r
7975 };\r
7976 \r
7977 Enables trainingOffEnables[] = {\r
7978   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7979   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7980   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7981   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7982   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7983   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7984   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7987   { -1, -1 }\r
7988 };\r
7989 \r
7990 /* These modify either ncpEnables or gnuEnables */\r
7991 Enables cmailEnables[] = {\r
7992   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7993   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7994   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7995   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7997   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7999   { -1, -1 }\r
8000 };\r
8001 \r
8002 Enables machineThinkingEnables[] = {\r
8003   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8004   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8005   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8016 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8017   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8018   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8019   { -1, -1 }\r
8020 };\r
8021 \r
8022 Enables userThinkingEnables[] = {\r
8023   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8024   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8025   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8026   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8027   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8028   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8029   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8030   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8031   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8032   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8033   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8034   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8035   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8036 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8039   { -1, -1 }\r
8040 };\r
8041 \r
8042 /*---------------------------------------------------------------------------*\\r
8043  *\r
8044  *  Front-end interface functions exported by XBoard.\r
8045  *  Functions appear in same order as prototypes in frontend.h.\r
8046  * \r
8047 \*---------------------------------------------------------------------------*/\r
8048 VOID\r
8049 CheckMark(UINT item, int state)\r
8050 {\r
8051     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8052 }\r
8053 \r
8054 VOID\r
8055 ModeHighlight()\r
8056 {\r
8057   static UINT prevChecked = 0;\r
8058   static int prevPausing = 0;\r
8059   UINT nowChecked;\r
8060 \r
8061   if (pausing != prevPausing) {\r
8062     prevPausing = pausing;\r
8063     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8064                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8065     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8066   }\r
8067 \r
8068   switch (gameMode) {\r
8069   case BeginningOfGame:\r
8070     if (appData.icsActive)\r
8071       nowChecked = IDM_IcsClient;\r
8072     else if (appData.noChessProgram)\r
8073       nowChecked = IDM_EditGame;\r
8074     else\r
8075       nowChecked = IDM_MachineBlack;\r
8076     break;\r
8077   case MachinePlaysBlack:\r
8078     nowChecked = IDM_MachineBlack;\r
8079     break;\r
8080   case MachinePlaysWhite:\r
8081     nowChecked = IDM_MachineWhite;\r
8082     break;\r
8083   case TwoMachinesPlay:\r
8084     nowChecked = IDM_TwoMachines;\r
8085     break;\r
8086   case AnalyzeMode:\r
8087     nowChecked = IDM_AnalysisMode;\r
8088     break;\r
8089   case AnalyzeFile:\r
8090     nowChecked = IDM_AnalyzeFile;\r
8091     break;\r
8092   case EditGame:\r
8093     nowChecked = IDM_EditGame;\r
8094     break;\r
8095   case PlayFromGameFile:\r
8096     nowChecked = IDM_LoadGame;\r
8097     break;\r
8098   case EditPosition:\r
8099     nowChecked = IDM_EditPosition;\r
8100     break;\r
8101   case Training:\r
8102     nowChecked = IDM_Training;\r
8103     break;\r
8104   case IcsPlayingWhite:\r
8105   case IcsPlayingBlack:\r
8106   case IcsObserving:\r
8107   case IcsIdle:\r
8108     nowChecked = IDM_IcsClient;\r
8109     break;\r
8110   default:\r
8111   case EndOfGame:\r
8112     nowChecked = 0;\r
8113     break;\r
8114   }\r
8115   CheckMark(prevChecked, MF_UNCHECKED);\r
8116   CheckMark(nowChecked, MF_CHECKED);\r
8117   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8118 \r
8119   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8120     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8121                           MF_BYCOMMAND|MF_ENABLED);\r
8122   } else {\r
8123     (void) EnableMenuItem(GetMenu(hwndMain), \r
8124                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8125   }\r
8126 \r
8127   prevChecked = nowChecked;\r
8128 \r
8129   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8130   if (appData.icsActive) {\r
8131        if (appData.icsEngineAnalyze) {\r
8132                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8133        } else {\r
8134                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8135        }\r
8136   }\r
8137   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8138 }\r
8139 \r
8140 VOID\r
8141 SetICSMode()\r
8142 {\r
8143   HMENU hmenu = GetMenu(hwndMain);\r
8144   SetMenuEnables(hmenu, icsEnables);\r
8145   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8146     MF_BYCOMMAND|MF_ENABLED);\r
8147 #if ZIPPY\r
8148   if (appData.zippyPlay) {\r
8149     SetMenuEnables(hmenu, zippyEnables);\r
8150     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8151          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8152           MF_BYCOMMAND|MF_ENABLED);\r
8153   }\r
8154 #endif\r
8155 }\r
8156 \r
8157 VOID\r
8158 SetGNUMode()\r
8159 {\r
8160   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8161 }\r
8162 \r
8163 VOID\r
8164 SetNCPMode()\r
8165 {\r
8166   HMENU hmenu = GetMenu(hwndMain);\r
8167   SetMenuEnables(hmenu, ncpEnables);\r
8168     DrawMenuBar(hwndMain);\r
8169 }\r
8170 \r
8171 VOID\r
8172 SetCmailMode()\r
8173 {\r
8174   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8175 }\r
8176 \r
8177 VOID \r
8178 SetTrainingModeOn()\r
8179 {\r
8180   int i;\r
8181   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8182   for (i = 0; i < N_BUTTONS; i++) {\r
8183     if (buttonDesc[i].hwnd != NULL)\r
8184       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8185   }\r
8186   CommentPopDown();\r
8187 }\r
8188 \r
8189 VOID SetTrainingModeOff()\r
8190 {\r
8191   int i;\r
8192   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8193   for (i = 0; i < N_BUTTONS; i++) {\r
8194     if (buttonDesc[i].hwnd != NULL)\r
8195       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8196   }\r
8197 }\r
8198 \r
8199 \r
8200 VOID\r
8201 SetUserThinkingEnables()\r
8202 {\r
8203   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8204 }\r
8205 \r
8206 VOID\r
8207 SetMachineThinkingEnables()\r
8208 {\r
8209   HMENU hMenu = GetMenu(hwndMain);\r
8210   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8211 \r
8212   SetMenuEnables(hMenu, machineThinkingEnables);\r
8213 \r
8214   if (gameMode == MachinePlaysBlack) {\r
8215     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8216   } else if (gameMode == MachinePlaysWhite) {\r
8217     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8218   } else if (gameMode == TwoMachinesPlay) {\r
8219     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8220   }\r
8221 }\r
8222 \r
8223 \r
8224 VOID\r
8225 DisplayTitle(char *str)\r
8226 {\r
8227   char title[MSG_SIZ], *host;\r
8228   if (str[0] != NULLCHAR) {\r
8229     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8230   } else if (appData.icsActive) {\r
8231     if (appData.icsCommPort[0] != NULLCHAR)\r
8232       host = "ICS";\r
8233     else \r
8234       host = appData.icsHost;\r
8235       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8236   } else if (appData.noChessProgram) {\r
8237     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8238   } else {\r
8239     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8240     strcat(title, ": ");\r
8241     strcat(title, first.tidy);\r
8242   }\r
8243   SetWindowText(hwndMain, title);\r
8244 }\r
8245 \r
8246 \r
8247 VOID\r
8248 DisplayMessage(char *str1, char *str2)\r
8249 {\r
8250   HDC hdc;\r
8251   HFONT oldFont;\r
8252   int remain = MESSAGE_TEXT_MAX - 1;\r
8253   int len;\r
8254 \r
8255   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8256   messageText[0] = NULLCHAR;\r
8257   if (*str1) {\r
8258     len = strlen(str1);\r
8259     if (len > remain) len = remain;\r
8260     strncpy(messageText, str1, len);\r
8261     messageText[len] = NULLCHAR;\r
8262     remain -= len;\r
8263   }\r
8264   if (*str2 && remain >= 2) {\r
8265     if (*str1) {\r
8266       strcat(messageText, "  ");\r
8267       remain -= 2;\r
8268     }\r
8269     len = strlen(str2);\r
8270     if (len > remain) len = remain;\r
8271     strncat(messageText, str2, len);\r
8272   }\r
8273   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8274   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8275 \r
8276   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8277 \r
8278   SAYMACHINEMOVE();\r
8279 \r
8280   hdc = GetDC(hwndMain);\r
8281   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8282   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8283              &messageRect, messageText, strlen(messageText), NULL);\r
8284   (void) SelectObject(hdc, oldFont);\r
8285   (void) ReleaseDC(hwndMain, hdc);\r
8286 }\r
8287 \r
8288 VOID\r
8289 DisplayError(char *str, int error)\r
8290 {\r
8291   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8292   int len;\r
8293 \r
8294   if (error == 0) {\r
8295     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8296   } else {\r
8297     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8298                         NULL, error, LANG_NEUTRAL,\r
8299                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8300     if (len > 0) {\r
8301       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8302     } else {\r
8303       ErrorMap *em = errmap;\r
8304       while (em->err != 0 && em->err != error) em++;\r
8305       if (em->err != 0) {\r
8306         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8307       } else {\r
8308         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8309       }\r
8310     }\r
8311   }\r
8312   \r
8313   ErrorPopUp(_("Error"), buf);\r
8314 }\r
8315 \r
8316 \r
8317 VOID\r
8318 DisplayMoveError(char *str)\r
8319 {\r
8320   fromX = fromY = -1;\r
8321   ClearHighlights();\r
8322   DrawPosition(FALSE, NULL);\r
8323   if (appData.popupMoveErrors) {\r
8324     ErrorPopUp(_("Error"), str);\r
8325   } else {\r
8326     DisplayMessage(str, "");\r
8327     moveErrorMessageUp = TRUE;\r
8328   }\r
8329 }\r
8330 \r
8331 VOID\r
8332 DisplayFatalError(char *str, int error, int exitStatus)\r
8333 {\r
8334   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8335   int len;\r
8336   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8337 \r
8338   if (error != 0) {\r
8339     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8340                         NULL, error, LANG_NEUTRAL,\r
8341                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8342     if (len > 0) {\r
8343       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8344     } else {\r
8345       ErrorMap *em = errmap;\r
8346       while (em->err != 0 && em->err != error) em++;\r
8347       if (em->err != 0) {\r
8348         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8349       } else {\r
8350         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8351       }\r
8352     }\r
8353     str = buf;\r
8354   }\r
8355   if (appData.debugMode) {\r
8356     fprintf(debugFP, "%s: %s\n", label, str);\r
8357   }\r
8358   if (appData.popupExitMessage) {\r
8359     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8360                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8361   }\r
8362   ExitEvent(exitStatus);\r
8363 }\r
8364 \r
8365 \r
8366 VOID\r
8367 DisplayInformation(char *str)\r
8368 {\r
8369   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8370 }\r
8371 \r
8372 \r
8373 VOID\r
8374 DisplayNote(char *str)\r
8375 {\r
8376   ErrorPopUp(_("Note"), str);\r
8377 }\r
8378 \r
8379 \r
8380 typedef struct {\r
8381   char *title, *question, *replyPrefix;\r
8382   ProcRef pr;\r
8383 } QuestionParams;\r
8384 \r
8385 LRESULT CALLBACK\r
8386 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8387 {\r
8388   static QuestionParams *qp;\r
8389   char reply[MSG_SIZ];\r
8390   int len, err;\r
8391 \r
8392   switch (message) {\r
8393   case WM_INITDIALOG:\r
8394     qp = (QuestionParams *) lParam;\r
8395     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8396     Translate(hDlg, DLG_Question);\r
8397     SetWindowText(hDlg, qp->title);\r
8398     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8399     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8400     return FALSE;\r
8401 \r
8402   case WM_COMMAND:\r
8403     switch (LOWORD(wParam)) {\r
8404     case IDOK:\r
8405       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8406       if (*reply) strcat(reply, " ");\r
8407       len = strlen(reply);\r
8408       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8409       strcat(reply, "\n");\r
8410       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8411       EndDialog(hDlg, TRUE);\r
8412       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8413       return TRUE;\r
8414     case IDCANCEL:\r
8415       EndDialog(hDlg, FALSE);\r
8416       return TRUE;\r
8417     default:\r
8418       break;\r
8419     }\r
8420     break;\r
8421   }\r
8422   return FALSE;\r
8423 }\r
8424 \r
8425 VOID\r
8426 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8427 {\r
8428     QuestionParams qp;\r
8429     FARPROC lpProc;\r
8430     \r
8431     qp.title = title;\r
8432     qp.question = question;\r
8433     qp.replyPrefix = replyPrefix;\r
8434     qp.pr = pr;\r
8435     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8436     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8437       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8438     FreeProcInstance(lpProc);\r
8439 }\r
8440 \r
8441 /* [AS] Pick FRC position */\r
8442 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8443 {\r
8444     static int * lpIndexFRC;\r
8445     BOOL index_is_ok;\r
8446     char buf[16];\r
8447 \r
8448     switch( message )\r
8449     {\r
8450     case WM_INITDIALOG:\r
8451         lpIndexFRC = (int *) lParam;\r
8452 \r
8453         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8454         Translate(hDlg, DLG_NewGameFRC);\r
8455 \r
8456         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8457         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8458         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8459         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8460 \r
8461         break;\r
8462 \r
8463     case WM_COMMAND:\r
8464         switch( LOWORD(wParam) ) {\r
8465         case IDOK:\r
8466             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8467             EndDialog( hDlg, 0 );\r
8468             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8469             return TRUE;\r
8470         case IDCANCEL:\r
8471             EndDialog( hDlg, 1 );   \r
8472             return TRUE;\r
8473         case IDC_NFG_Edit:\r
8474             if( HIWORD(wParam) == EN_CHANGE ) {\r
8475                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8476 \r
8477                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8478             }\r
8479             return TRUE;\r
8480         case IDC_NFG_Random:\r
8481           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8482             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8483             return TRUE;\r
8484         }\r
8485 \r
8486         break;\r
8487     }\r
8488 \r
8489     return FALSE;\r
8490 }\r
8491 \r
8492 int NewGameFRC()\r
8493 {\r
8494     int result;\r
8495     int index = appData.defaultFrcPosition;\r
8496     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8497 \r
8498     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8499 \r
8500     if( result == 0 ) {\r
8501         appData.defaultFrcPosition = index;\r
8502     }\r
8503 \r
8504     return result;\r
8505 }\r
8506 \r
8507 /* [AS] Game list options. Refactored by HGM */\r
8508 \r
8509 HWND gameListOptionsDialog;\r
8510 \r
8511 // low-level front-end: clear text edit / list widget\r
8512 void\r
8513 GLT_ClearList()\r
8514 {\r
8515     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8516 }\r
8517 \r
8518 // low-level front-end: clear text edit / list widget\r
8519 void\r
8520 GLT_DeSelectList()\r
8521 {\r
8522     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8523 }\r
8524 \r
8525 // low-level front-end: append line to text edit / list widget\r
8526 void\r
8527 GLT_AddToList( char *name )\r
8528 {\r
8529     if( name != 0 ) {\r
8530             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8531     }\r
8532 }\r
8533 \r
8534 // low-level front-end: get line from text edit / list widget\r
8535 Boolean\r
8536 GLT_GetFromList( int index, char *name )\r
8537 {\r
8538     if( name != 0 ) {\r
8539             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8540                 return TRUE;\r
8541     }\r
8542     return FALSE;\r
8543 }\r
8544 \r
8545 void GLT_MoveSelection( HWND hDlg, int delta )\r
8546 {\r
8547     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8548     int idx2 = idx1 + delta;\r
8549     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8550 \r
8551     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8552         char buf[128];\r
8553 \r
8554         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8555         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8556         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8557         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8558     }\r
8559 }\r
8560 \r
8561 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8562 {\r
8563     switch( message )\r
8564     {\r
8565     case WM_INITDIALOG:\r
8566         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8567         \r
8568         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8569         Translate(hDlg, DLG_GameListOptions);\r
8570 \r
8571         /* Initialize list */\r
8572         GLT_TagsToList( lpUserGLT );\r
8573 \r
8574         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8575 \r
8576         break;\r
8577 \r
8578     case WM_COMMAND:\r
8579         switch( LOWORD(wParam) ) {\r
8580         case IDOK:\r
8581             GLT_ParseList();\r
8582             EndDialog( hDlg, 0 );\r
8583             return TRUE;\r
8584         case IDCANCEL:\r
8585             EndDialog( hDlg, 1 );\r
8586             return TRUE;\r
8587 \r
8588         case IDC_GLT_Default:\r
8589             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8590             return TRUE;\r
8591 \r
8592         case IDC_GLT_Restore:\r
8593             GLT_TagsToList( appData.gameListTags );\r
8594             return TRUE;\r
8595 \r
8596         case IDC_GLT_Up:\r
8597             GLT_MoveSelection( hDlg, -1 );\r
8598             return TRUE;\r
8599 \r
8600         case IDC_GLT_Down:\r
8601             GLT_MoveSelection( hDlg, +1 );\r
8602             return TRUE;\r
8603         }\r
8604 \r
8605         break;\r
8606     }\r
8607 \r
8608     return FALSE;\r
8609 }\r
8610 \r
8611 int GameListOptions()\r
8612 {\r
8613     int result;\r
8614     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8615 \r
8616       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8617 \r
8618     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8619 \r
8620     if( result == 0 ) {\r
8621         /* [AS] Memory leak here! */\r
8622         appData.gameListTags = strdup( lpUserGLT ); \r
8623     }\r
8624 \r
8625     return result;\r
8626 }\r
8627 \r
8628 VOID\r
8629 DisplayIcsInteractionTitle(char *str)\r
8630 {\r
8631   char consoleTitle[MSG_SIZ];\r
8632 \r
8633     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8634     SetWindowText(hwndConsole, consoleTitle);\r
8635 \r
8636     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8637       char buf[MSG_SIZ], *p = buf, *q;\r
8638         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8639       do {\r
8640         q = strchr(p, ';');\r
8641         if(q) *q++ = 0;\r
8642         if(*p) ChatPopUp(p);\r
8643       } while(p=q);\r
8644     }\r
8645 \r
8646     SetActiveWindow(hwndMain);\r
8647 }\r
8648 \r
8649 void\r
8650 DrawPosition(int fullRedraw, Board board)\r
8651 {\r
8652   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8653 }\r
8654 \r
8655 void NotifyFrontendLogin()\r
8656 {\r
8657         if (hwndConsole)\r
8658                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8659 }\r
8660 \r
8661 VOID\r
8662 ResetFrontEnd()\r
8663 {\r
8664   fromX = fromY = -1;\r
8665   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8666     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8667     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8668     dragInfo.lastpos = dragInfo.pos;\r
8669     dragInfo.start.x = dragInfo.start.y = -1;\r
8670     dragInfo.from = dragInfo.start;\r
8671     ReleaseCapture();\r
8672     DrawPosition(TRUE, NULL);\r
8673   }\r
8674   TagsPopDown();\r
8675 }\r
8676 \r
8677 \r
8678 VOID\r
8679 CommentPopUp(char *title, char *str)\r
8680 {\r
8681   HWND hwnd = GetActiveWindow();\r
8682   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8683   SAY(str);\r
8684   SetActiveWindow(hwnd);\r
8685 }\r
8686 \r
8687 VOID\r
8688 CommentPopDown(void)\r
8689 {\r
8690   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8691   if (commentDialog) {\r
8692     ShowWindow(commentDialog, SW_HIDE);\r
8693   }\r
8694   commentUp = FALSE;\r
8695 }\r
8696 \r
8697 VOID\r
8698 EditCommentPopUp(int index, char *title, char *str)\r
8699 {\r
8700   EitherCommentPopUp(index, title, str, TRUE);\r
8701 }\r
8702 \r
8703 \r
8704 VOID\r
8705 RingBell()\r
8706 {\r
8707   MyPlaySound(&sounds[(int)SoundMove]);\r
8708 }\r
8709 \r
8710 VOID PlayIcsWinSound()\r
8711 {\r
8712   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8713 }\r
8714 \r
8715 VOID PlayIcsLossSound()\r
8716 {\r
8717   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8718 }\r
8719 \r
8720 VOID PlayIcsDrawSound()\r
8721 {\r
8722   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8723 }\r
8724 \r
8725 VOID PlayIcsUnfinishedSound()\r
8726 {\r
8727   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8728 }\r
8729 \r
8730 VOID\r
8731 PlayAlarmSound()\r
8732 {\r
8733   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8734 }\r
8735 \r
8736 VOID\r
8737 PlayTellSound()\r
8738 {\r
8739   MyPlaySound(&textAttribs[ColorTell].sound);\r
8740 }\r
8741 \r
8742 \r
8743 VOID\r
8744 EchoOn()\r
8745 {\r
8746   HWND hInput;\r
8747   consoleEcho = TRUE;\r
8748   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8749   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8750   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8751 }\r
8752 \r
8753 \r
8754 VOID\r
8755 EchoOff()\r
8756 {\r
8757   CHARFORMAT cf;\r
8758   HWND hInput;\r
8759   consoleEcho = FALSE;\r
8760   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8761   /* This works OK: set text and background both to the same color */\r
8762   cf = consoleCF;\r
8763   cf.crTextColor = COLOR_ECHOOFF;\r
8764   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8765   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8766 }\r
8767 \r
8768 /* No Raw()...? */\r
8769 \r
8770 void Colorize(ColorClass cc, int continuation)\r
8771 {\r
8772   currentColorClass = cc;\r
8773   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8774   consoleCF.crTextColor = textAttribs[cc].color;\r
8775   consoleCF.dwEffects = textAttribs[cc].effects;\r
8776   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8777 }\r
8778 \r
8779 char *\r
8780 UserName()\r
8781 {\r
8782   static char buf[MSG_SIZ];\r
8783   DWORD bufsiz = MSG_SIZ;\r
8784 \r
8785   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8786         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8787   }\r
8788   if (!GetUserName(buf, &bufsiz)) {\r
8789     /*DisplayError("Error getting user name", GetLastError());*/\r
8790     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8791   }\r
8792   return buf;\r
8793 }\r
8794 \r
8795 char *\r
8796 HostName()\r
8797 {\r
8798   static char buf[MSG_SIZ];\r
8799   DWORD bufsiz = MSG_SIZ;\r
8800 \r
8801   if (!GetComputerName(buf, &bufsiz)) {\r
8802     /*DisplayError("Error getting host name", GetLastError());*/\r
8803     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8804   }\r
8805   return buf;\r
8806 }\r
8807 \r
8808 \r
8809 int\r
8810 ClockTimerRunning()\r
8811 {\r
8812   return clockTimerEvent != 0;\r
8813 }\r
8814 \r
8815 int\r
8816 StopClockTimer()\r
8817 {\r
8818   if (clockTimerEvent == 0) return FALSE;\r
8819   KillTimer(hwndMain, clockTimerEvent);\r
8820   clockTimerEvent = 0;\r
8821   return TRUE;\r
8822 }\r
8823 \r
8824 void\r
8825 StartClockTimer(long millisec)\r
8826 {\r
8827   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8828                              (UINT) millisec, NULL);\r
8829 }\r
8830 \r
8831 void\r
8832 DisplayWhiteClock(long timeRemaining, int highlight)\r
8833 {\r
8834   HDC hdc;\r
8835   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8836 \r
8837   if(appData.noGUI) return;\r
8838   hdc = GetDC(hwndMain);\r
8839   if (!IsIconic(hwndMain)) {\r
8840     DisplayAClock(hdc, timeRemaining, highlight, \r
8841                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8842   }\r
8843   if (highlight && iconCurrent == iconBlack) {\r
8844     iconCurrent = iconWhite;\r
8845     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8846     if (IsIconic(hwndMain)) {\r
8847       DrawIcon(hdc, 2, 2, iconCurrent);\r
8848     }\r
8849   }\r
8850   (void) ReleaseDC(hwndMain, hdc);\r
8851   if (hwndConsole)\r
8852     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8853 }\r
8854 \r
8855 void\r
8856 DisplayBlackClock(long timeRemaining, int highlight)\r
8857 {\r
8858   HDC hdc;\r
8859   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8860 \r
8861   if(appData.noGUI) return;\r
8862   hdc = GetDC(hwndMain);\r
8863   if (!IsIconic(hwndMain)) {\r
8864     DisplayAClock(hdc, timeRemaining, highlight, \r
8865                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8866   }\r
8867   if (highlight && iconCurrent == iconWhite) {\r
8868     iconCurrent = iconBlack;\r
8869     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8870     if (IsIconic(hwndMain)) {\r
8871       DrawIcon(hdc, 2, 2, iconCurrent);\r
8872     }\r
8873   }\r
8874   (void) ReleaseDC(hwndMain, hdc);\r
8875   if (hwndConsole)\r
8876     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8877 }\r
8878 \r
8879 \r
8880 int\r
8881 LoadGameTimerRunning()\r
8882 {\r
8883   return loadGameTimerEvent != 0;\r
8884 }\r
8885 \r
8886 int\r
8887 StopLoadGameTimer()\r
8888 {\r
8889   if (loadGameTimerEvent == 0) return FALSE;\r
8890   KillTimer(hwndMain, loadGameTimerEvent);\r
8891   loadGameTimerEvent = 0;\r
8892   return TRUE;\r
8893 }\r
8894 \r
8895 void\r
8896 StartLoadGameTimer(long millisec)\r
8897 {\r
8898   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8899                                 (UINT) millisec, NULL);\r
8900 }\r
8901 \r
8902 void\r
8903 AutoSaveGame()\r
8904 {\r
8905   char *defName;\r
8906   FILE *f;\r
8907   char fileTitle[MSG_SIZ];\r
8908 \r
8909   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8910   f = OpenFileDialog(hwndMain, "a", defName,\r
8911                      appData.oldSaveStyle ? "gam" : "pgn",\r
8912                      GAME_FILT, \r
8913                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8914   if (f != NULL) {\r
8915     SaveGame(f, 0, "");\r
8916     fclose(f);\r
8917   }\r
8918 }\r
8919 \r
8920 \r
8921 void\r
8922 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8923 {\r
8924   if (delayedTimerEvent != 0) {\r
8925     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8926       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8927     }\r
8928     KillTimer(hwndMain, delayedTimerEvent);\r
8929     delayedTimerEvent = 0;\r
8930     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8931     delayedTimerCallback();\r
8932   }\r
8933   delayedTimerCallback = cb;\r
8934   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8935                                 (UINT) millisec, NULL);\r
8936 }\r
8937 \r
8938 DelayedEventCallback\r
8939 GetDelayedEvent()\r
8940 {\r
8941   if (delayedTimerEvent) {\r
8942     return delayedTimerCallback;\r
8943   } else {\r
8944     return NULL;\r
8945   }\r
8946 }\r
8947 \r
8948 void\r
8949 CancelDelayedEvent()\r
8950 {\r
8951   if (delayedTimerEvent) {\r
8952     KillTimer(hwndMain, delayedTimerEvent);\r
8953     delayedTimerEvent = 0;\r
8954   }\r
8955 }\r
8956 \r
8957 DWORD GetWin32Priority(int nice)\r
8958 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8959 /*\r
8960 REALTIME_PRIORITY_CLASS     0x00000100\r
8961 HIGH_PRIORITY_CLASS         0x00000080\r
8962 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8963 NORMAL_PRIORITY_CLASS       0x00000020\r
8964 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8965 IDLE_PRIORITY_CLASS         0x00000040\r
8966 */\r
8967         if (nice < -15) return 0x00000080;\r
8968         if (nice < 0)   return 0x00008000;\r
8969         if (nice == 0)  return 0x00000020;\r
8970         if (nice < 15)  return 0x00004000;\r
8971         return 0x00000040;\r
8972 }\r
8973 \r
8974 void RunCommand(char *cmdLine)\r
8975 {\r
8976   /* Now create the child process. */\r
8977   STARTUPINFO siStartInfo;\r
8978   PROCESS_INFORMATION piProcInfo;\r
8979 \r
8980   siStartInfo.cb = sizeof(STARTUPINFO);\r
8981   siStartInfo.lpReserved = NULL;\r
8982   siStartInfo.lpDesktop = NULL;\r
8983   siStartInfo.lpTitle = NULL;\r
8984   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8985   siStartInfo.cbReserved2 = 0;\r
8986   siStartInfo.lpReserved2 = NULL;\r
8987   siStartInfo.hStdInput = NULL;\r
8988   siStartInfo.hStdOutput = NULL;\r
8989   siStartInfo.hStdError = NULL;\r
8990 \r
8991   CreateProcess(NULL,\r
8992                 cmdLine,           /* command line */\r
8993                 NULL,      /* process security attributes */\r
8994                 NULL,      /* primary thread security attrs */\r
8995                 TRUE,      /* handles are inherited */\r
8996                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8997                 NULL,      /* use parent's environment */\r
8998                 NULL,\r
8999                 &siStartInfo, /* STARTUPINFO pointer */\r
9000                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9001 \r
9002   CloseHandle(piProcInfo.hThread);\r
9003 }\r
9004 \r
9005 /* Start a child process running the given program.\r
9006    The process's standard output can be read from "from", and its\r
9007    standard input can be written to "to".\r
9008    Exit with fatal error if anything goes wrong.\r
9009    Returns an opaque pointer that can be used to destroy the process\r
9010    later.\r
9011 */\r
9012 int\r
9013 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9014 {\r
9015 #define BUFSIZE 4096\r
9016 \r
9017   HANDLE hChildStdinRd, hChildStdinWr,\r
9018     hChildStdoutRd, hChildStdoutWr;\r
9019   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9020   SECURITY_ATTRIBUTES saAttr;\r
9021   BOOL fSuccess;\r
9022   PROCESS_INFORMATION piProcInfo;\r
9023   STARTUPINFO siStartInfo;\r
9024   ChildProc *cp;\r
9025   char buf[MSG_SIZ];\r
9026   DWORD err;\r
9027 \r
9028   if (appData.debugMode) {\r
9029     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9030   }\r
9031 \r
9032   *pr = NoProc;\r
9033 \r
9034   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9035   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9036   saAttr.bInheritHandle = TRUE;\r
9037   saAttr.lpSecurityDescriptor = NULL;\r
9038 \r
9039   /*\r
9040    * The steps for redirecting child's STDOUT:\r
9041    *     1. Create anonymous pipe to be STDOUT for child.\r
9042    *     2. Create a noninheritable duplicate of read handle,\r
9043    *         and close the inheritable read handle.\r
9044    */\r
9045 \r
9046   /* Create a pipe for the child's STDOUT. */\r
9047   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9048     return GetLastError();\r
9049   }\r
9050 \r
9051   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9052   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9053                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9054                              FALSE,     /* not inherited */\r
9055                              DUPLICATE_SAME_ACCESS);\r
9056   if (! fSuccess) {\r
9057     return GetLastError();\r
9058   }\r
9059   CloseHandle(hChildStdoutRd);\r
9060 \r
9061   /*\r
9062    * The steps for redirecting child's STDIN:\r
9063    *     1. Create anonymous pipe to be STDIN for child.\r
9064    *     2. Create a noninheritable duplicate of write handle,\r
9065    *         and close the inheritable write handle.\r
9066    */\r
9067 \r
9068   /* Create a pipe for the child's STDIN. */\r
9069   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9070     return GetLastError();\r
9071   }\r
9072 \r
9073   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9074   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9075                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9076                              FALSE,     /* not inherited */\r
9077                              DUPLICATE_SAME_ACCESS);\r
9078   if (! fSuccess) {\r
9079     return GetLastError();\r
9080   }\r
9081   CloseHandle(hChildStdinWr);\r
9082 \r
9083   /* Arrange to (1) look in dir for the child .exe file, and\r
9084    * (2) have dir be the child's working directory.  Interpret\r
9085    * dir relative to the directory WinBoard loaded from. */\r
9086   GetCurrentDirectory(MSG_SIZ, buf);\r
9087   SetCurrentDirectory(installDir);\r
9088   SetCurrentDirectory(dir);\r
9089 \r
9090   /* Now create the child process. */\r
9091 \r
9092   siStartInfo.cb = sizeof(STARTUPINFO);\r
9093   siStartInfo.lpReserved = NULL;\r
9094   siStartInfo.lpDesktop = NULL;\r
9095   siStartInfo.lpTitle = NULL;\r
9096   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9097   siStartInfo.cbReserved2 = 0;\r
9098   siStartInfo.lpReserved2 = NULL;\r
9099   siStartInfo.hStdInput = hChildStdinRd;\r
9100   siStartInfo.hStdOutput = hChildStdoutWr;\r
9101   siStartInfo.hStdError = hChildStdoutWr;\r
9102 \r
9103   fSuccess = CreateProcess(NULL,\r
9104                            cmdLine,        /* command line */\r
9105                            NULL,           /* process security attributes */\r
9106                            NULL,           /* primary thread security attrs */\r
9107                            TRUE,           /* handles are inherited */\r
9108                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9109                            NULL,           /* use parent's environment */\r
9110                            NULL,\r
9111                            &siStartInfo, /* STARTUPINFO pointer */\r
9112                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9113 \r
9114   err = GetLastError();\r
9115   SetCurrentDirectory(buf); /* return to prev directory */\r
9116   if (! fSuccess) {\r
9117     return err;\r
9118   }\r
9119 \r
9120   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9121     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9122     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9123   }\r
9124 \r
9125   /* Close the handles we don't need in the parent */\r
9126   CloseHandle(piProcInfo.hThread);\r
9127   CloseHandle(hChildStdinRd);\r
9128   CloseHandle(hChildStdoutWr);\r
9129 \r
9130   /* Prepare return value */\r
9131   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9132   cp->kind = CPReal;\r
9133   cp->hProcess = piProcInfo.hProcess;\r
9134   cp->pid = piProcInfo.dwProcessId;\r
9135   cp->hFrom = hChildStdoutRdDup;\r
9136   cp->hTo = hChildStdinWrDup;\r
9137 \r
9138   *pr = (void *) cp;\r
9139 \r
9140   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9141      2000 where engines sometimes don't see the initial command(s)\r
9142      from WinBoard and hang.  I don't understand how that can happen,\r
9143      but the Sleep is harmless, so I've put it in.  Others have also\r
9144      reported what may be the same problem, so hopefully this will fix\r
9145      it for them too.  */\r
9146   Sleep(500);\r
9147 \r
9148   return NO_ERROR;\r
9149 }\r
9150 \r
9151 \r
9152 void\r
9153 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9154 {\r
9155   ChildProc *cp; int result;\r
9156 \r
9157   cp = (ChildProc *) pr;\r
9158   if (cp == NULL) return;\r
9159 \r
9160   switch (cp->kind) {\r
9161   case CPReal:\r
9162     /* TerminateProcess is considered harmful, so... */\r
9163     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9164     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9165     /* The following doesn't work because the chess program\r
9166        doesn't "have the same console" as WinBoard.  Maybe\r
9167        we could arrange for this even though neither WinBoard\r
9168        nor the chess program uses a console for stdio? */\r
9169     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9170 \r
9171     /* [AS] Special termination modes for misbehaving programs... */\r
9172     if( signal == 9 ) { \r
9173         result = TerminateProcess( cp->hProcess, 0 );\r
9174 \r
9175         if ( appData.debugMode) {\r
9176             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9177         }\r
9178     }\r
9179     else if( signal == 10 ) {\r
9180         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9181 \r
9182         if( dw != WAIT_OBJECT_0 ) {\r
9183             result = TerminateProcess( cp->hProcess, 0 );\r
9184 \r
9185             if ( appData.debugMode) {\r
9186                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9187             }\r
9188 \r
9189         }\r
9190     }\r
9191 \r
9192     CloseHandle(cp->hProcess);\r
9193     break;\r
9194 \r
9195   case CPComm:\r
9196     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9197     break;\r
9198 \r
9199   case CPSock:\r
9200     closesocket(cp->sock);\r
9201     WSACleanup();\r
9202     break;\r
9203 \r
9204   case CPRcmd:\r
9205     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9206     closesocket(cp->sock);\r
9207     closesocket(cp->sock2);\r
9208     WSACleanup();\r
9209     break;\r
9210   }\r
9211   free(cp);\r
9212 }\r
9213 \r
9214 void\r
9215 InterruptChildProcess(ProcRef pr)\r
9216 {\r
9217   ChildProc *cp;\r
9218 \r
9219   cp = (ChildProc *) pr;\r
9220   if (cp == NULL) return;\r
9221   switch (cp->kind) {\r
9222   case CPReal:\r
9223     /* The following doesn't work because the chess program\r
9224        doesn't "have the same console" as WinBoard.  Maybe\r
9225        we could arrange for this even though neither WinBoard\r
9226        nor the chess program uses a console for stdio */\r
9227     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9228     break;\r
9229 \r
9230   case CPComm:\r
9231   case CPSock:\r
9232     /* Can't interrupt */\r
9233     break;\r
9234 \r
9235   case CPRcmd:\r
9236     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9237     break;\r
9238   }\r
9239 }\r
9240 \r
9241 \r
9242 int\r
9243 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9244 {\r
9245   char cmdLine[MSG_SIZ];\r
9246 \r
9247   if (port[0] == NULLCHAR) {\r
9248     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9249   } else {\r
9250     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9251   }\r
9252   return StartChildProcess(cmdLine, "", pr);\r
9253 }\r
9254 \r
9255 \r
9256 /* Code to open TCP sockets */\r
9257 \r
9258 int\r
9259 OpenTCP(char *host, char *port, ProcRef *pr)\r
9260 {\r
9261   ChildProc *cp;\r
9262   int err;\r
9263   SOCKET s;\r
9264 \r
9265   struct sockaddr_in sa, mysa;\r
9266   struct hostent FAR *hp;\r
9267   unsigned short uport;\r
9268   WORD wVersionRequested;\r
9269   WSADATA wsaData;\r
9270 \r
9271   /* Initialize socket DLL */\r
9272   wVersionRequested = MAKEWORD(1, 1);\r
9273   err = WSAStartup(wVersionRequested, &wsaData);\r
9274   if (err != 0) return err;\r
9275 \r
9276   /* Make socket */\r
9277   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9278     err = WSAGetLastError();\r
9279     WSACleanup();\r
9280     return err;\r
9281   }\r
9282 \r
9283   /* Bind local address using (mostly) don't-care values.\r
9284    */\r
9285   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9286   mysa.sin_family = AF_INET;\r
9287   mysa.sin_addr.s_addr = INADDR_ANY;\r
9288   uport = (unsigned short) 0;\r
9289   mysa.sin_port = htons(uport);\r
9290   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9291       == SOCKET_ERROR) {\r
9292     err = WSAGetLastError();\r
9293     WSACleanup();\r
9294     return err;\r
9295   }\r
9296 \r
9297   /* Resolve remote host name */\r
9298   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9299   if (!(hp = gethostbyname(host))) {\r
9300     unsigned int b0, b1, b2, b3;\r
9301 \r
9302     err = WSAGetLastError();\r
9303 \r
9304     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9305       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9306       hp->h_addrtype = AF_INET;\r
9307       hp->h_length = 4;\r
9308       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9309       hp->h_addr_list[0] = (char *) malloc(4);\r
9310       hp->h_addr_list[0][0] = (char) b0;\r
9311       hp->h_addr_list[0][1] = (char) b1;\r
9312       hp->h_addr_list[0][2] = (char) b2;\r
9313       hp->h_addr_list[0][3] = (char) b3;\r
9314     } else {\r
9315       WSACleanup();\r
9316       return err;\r
9317     }\r
9318   }\r
9319   sa.sin_family = hp->h_addrtype;\r
9320   uport = (unsigned short) atoi(port);\r
9321   sa.sin_port = htons(uport);\r
9322   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9323 \r
9324   /* Make connection */\r
9325   if (connect(s, (struct sockaddr *) &sa,\r
9326               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9327     err = WSAGetLastError();\r
9328     WSACleanup();\r
9329     return err;\r
9330   }\r
9331 \r
9332   /* Prepare return value */\r
9333   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9334   cp->kind = CPSock;\r
9335   cp->sock = s;\r
9336   *pr = (ProcRef *) cp;\r
9337 \r
9338   return NO_ERROR;\r
9339 }\r
9340 \r
9341 int\r
9342 OpenCommPort(char *name, ProcRef *pr)\r
9343 {\r
9344   HANDLE h;\r
9345   COMMTIMEOUTS ct;\r
9346   ChildProc *cp;\r
9347   char fullname[MSG_SIZ];\r
9348 \r
9349   if (*name != '\\')\r
9350     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9351   else\r
9352     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9353 \r
9354   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9355                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9356   if (h == (HANDLE) -1) {\r
9357     return GetLastError();\r
9358   }\r
9359   hCommPort = h;\r
9360 \r
9361   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9362 \r
9363   /* Accumulate characters until a 100ms pause, then parse */\r
9364   ct.ReadIntervalTimeout = 100;\r
9365   ct.ReadTotalTimeoutMultiplier = 0;\r
9366   ct.ReadTotalTimeoutConstant = 0;\r
9367   ct.WriteTotalTimeoutMultiplier = 0;\r
9368   ct.WriteTotalTimeoutConstant = 0;\r
9369   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9370 \r
9371   /* Prepare return value */\r
9372   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9373   cp->kind = CPComm;\r
9374   cp->hFrom = h;\r
9375   cp->hTo = h;\r
9376   *pr = (ProcRef *) cp;\r
9377 \r
9378   return NO_ERROR;\r
9379 }\r
9380 \r
9381 int\r
9382 OpenLoopback(ProcRef *pr)\r
9383 {\r
9384   DisplayFatalError(_("Not implemented"), 0, 1);\r
9385   return NO_ERROR;\r
9386 }\r
9387 \r
9388 \r
9389 int\r
9390 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9391 {\r
9392   ChildProc *cp;\r
9393   int err;\r
9394   SOCKET s, s2, s3;\r
9395   struct sockaddr_in sa, mysa;\r
9396   struct hostent FAR *hp;\r
9397   unsigned short uport;\r
9398   WORD wVersionRequested;\r
9399   WSADATA wsaData;\r
9400   int fromPort;\r
9401   char stderrPortStr[MSG_SIZ];\r
9402 \r
9403   /* Initialize socket DLL */\r
9404   wVersionRequested = MAKEWORD(1, 1);\r
9405   err = WSAStartup(wVersionRequested, &wsaData);\r
9406   if (err != 0) return err;\r
9407 \r
9408   /* Resolve remote host name */\r
9409   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9410   if (!(hp = gethostbyname(host))) {\r
9411     unsigned int b0, b1, b2, b3;\r
9412 \r
9413     err = WSAGetLastError();\r
9414 \r
9415     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9416       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9417       hp->h_addrtype = AF_INET;\r
9418       hp->h_length = 4;\r
9419       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9420       hp->h_addr_list[0] = (char *) malloc(4);\r
9421       hp->h_addr_list[0][0] = (char) b0;\r
9422       hp->h_addr_list[0][1] = (char) b1;\r
9423       hp->h_addr_list[0][2] = (char) b2;\r
9424       hp->h_addr_list[0][3] = (char) b3;\r
9425     } else {\r
9426       WSACleanup();\r
9427       return err;\r
9428     }\r
9429   }\r
9430   sa.sin_family = hp->h_addrtype;\r
9431   uport = (unsigned short) 514;\r
9432   sa.sin_port = htons(uport);\r
9433   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9434 \r
9435   /* Bind local socket to unused "privileged" port address\r
9436    */\r
9437   s = INVALID_SOCKET;\r
9438   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9439   mysa.sin_family = AF_INET;\r
9440   mysa.sin_addr.s_addr = INADDR_ANY;\r
9441   for (fromPort = 1023;; fromPort--) {\r
9442     if (fromPort < 0) {\r
9443       WSACleanup();\r
9444       return WSAEADDRINUSE;\r
9445     }\r
9446     if (s == INVALID_SOCKET) {\r
9447       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9448         err = WSAGetLastError();\r
9449         WSACleanup();\r
9450         return err;\r
9451       }\r
9452     }\r
9453     uport = (unsigned short) fromPort;\r
9454     mysa.sin_port = htons(uport);\r
9455     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9456         == SOCKET_ERROR) {\r
9457       err = WSAGetLastError();\r
9458       if (err == WSAEADDRINUSE) continue;\r
9459       WSACleanup();\r
9460       return err;\r
9461     }\r
9462     if (connect(s, (struct sockaddr *) &sa,\r
9463       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9464       err = WSAGetLastError();\r
9465       if (err == WSAEADDRINUSE) {\r
9466         closesocket(s);\r
9467         s = -1;\r
9468         continue;\r
9469       }\r
9470       WSACleanup();\r
9471       return err;\r
9472     }\r
9473     break;\r
9474   }\r
9475 \r
9476   /* Bind stderr local socket to unused "privileged" port address\r
9477    */\r
9478   s2 = INVALID_SOCKET;\r
9479   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9480   mysa.sin_family = AF_INET;\r
9481   mysa.sin_addr.s_addr = INADDR_ANY;\r
9482   for (fromPort = 1023;; fromPort--) {\r
9483     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9484     if (fromPort < 0) {\r
9485       (void) closesocket(s);\r
9486       WSACleanup();\r
9487       return WSAEADDRINUSE;\r
9488     }\r
9489     if (s2 == INVALID_SOCKET) {\r
9490       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9491         err = WSAGetLastError();\r
9492         closesocket(s);\r
9493         WSACleanup();\r
9494         return err;\r
9495       }\r
9496     }\r
9497     uport = (unsigned short) fromPort;\r
9498     mysa.sin_port = htons(uport);\r
9499     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9500         == SOCKET_ERROR) {\r
9501       err = WSAGetLastError();\r
9502       if (err == WSAEADDRINUSE) continue;\r
9503       (void) closesocket(s);\r
9504       WSACleanup();\r
9505       return err;\r
9506     }\r
9507     if (listen(s2, 1) == SOCKET_ERROR) {\r
9508       err = WSAGetLastError();\r
9509       if (err == WSAEADDRINUSE) {\r
9510         closesocket(s2);\r
9511         s2 = INVALID_SOCKET;\r
9512         continue;\r
9513       }\r
9514       (void) closesocket(s);\r
9515       (void) closesocket(s2);\r
9516       WSACleanup();\r
9517       return err;\r
9518     }\r
9519     break;\r
9520   }\r
9521   prevStderrPort = fromPort; // remember port used\r
9522   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9523 \r
9524   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9525     err = WSAGetLastError();\r
9526     (void) closesocket(s);\r
9527     (void) closesocket(s2);\r
9528     WSACleanup();\r
9529     return err;\r
9530   }\r
9531 \r
9532   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9533     err = WSAGetLastError();\r
9534     (void) closesocket(s);\r
9535     (void) closesocket(s2);\r
9536     WSACleanup();\r
9537     return err;\r
9538   }\r
9539   if (*user == NULLCHAR) user = UserName();\r
9540   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9541     err = WSAGetLastError();\r
9542     (void) closesocket(s);\r
9543     (void) closesocket(s2);\r
9544     WSACleanup();\r
9545     return err;\r
9546   }\r
9547   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9548     err = WSAGetLastError();\r
9549     (void) closesocket(s);\r
9550     (void) closesocket(s2);\r
9551     WSACleanup();\r
9552     return err;\r
9553   }\r
9554 \r
9555   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9556     err = WSAGetLastError();\r
9557     (void) closesocket(s);\r
9558     (void) closesocket(s2);\r
9559     WSACleanup();\r
9560     return err;\r
9561   }\r
9562   (void) closesocket(s2);  /* Stop listening */\r
9563 \r
9564   /* Prepare return value */\r
9565   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9566   cp->kind = CPRcmd;\r
9567   cp->sock = s;\r
9568   cp->sock2 = s3;\r
9569   *pr = (ProcRef *) cp;\r
9570 \r
9571   return NO_ERROR;\r
9572 }\r
9573 \r
9574 \r
9575 InputSourceRef\r
9576 AddInputSource(ProcRef pr, int lineByLine,\r
9577                InputCallback func, VOIDSTAR closure)\r
9578 {\r
9579   InputSource *is, *is2 = NULL;\r
9580   ChildProc *cp = (ChildProc *) pr;\r
9581 \r
9582   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9583   is->lineByLine = lineByLine;\r
9584   is->func = func;\r
9585   is->closure = closure;\r
9586   is->second = NULL;\r
9587   is->next = is->buf;\r
9588   if (pr == NoProc) {\r
9589     is->kind = CPReal;\r
9590     consoleInputSource = is;\r
9591   } else {\r
9592     is->kind = cp->kind;\r
9593     /* \r
9594         [AS] Try to avoid a race condition if the thread is given control too early:\r
9595         we create all threads suspended so that the is->hThread variable can be\r
9596         safely assigned, then let the threads start with ResumeThread.\r
9597     */\r
9598     switch (cp->kind) {\r
9599     case CPReal:\r
9600       is->hFile = cp->hFrom;\r
9601       cp->hFrom = NULL; /* now owned by InputThread */\r
9602       is->hThread =\r
9603         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9604                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9605       break;\r
9606 \r
9607     case CPComm:\r
9608       is->hFile = cp->hFrom;\r
9609       cp->hFrom = NULL; /* now owned by InputThread */\r
9610       is->hThread =\r
9611         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9612                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9613       break;\r
9614 \r
9615     case CPSock:\r
9616       is->sock = cp->sock;\r
9617       is->hThread =\r
9618         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9619                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9620       break;\r
9621 \r
9622     case CPRcmd:\r
9623       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9624       *is2 = *is;\r
9625       is->sock = cp->sock;\r
9626       is->second = is2;\r
9627       is2->sock = cp->sock2;\r
9628       is2->second = is2;\r
9629       is->hThread =\r
9630         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9631                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9632       is2->hThread =\r
9633         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9634                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9635       break;\r
9636     }\r
9637 \r
9638     if( is->hThread != NULL ) {\r
9639         ResumeThread( is->hThread );\r
9640     }\r
9641 \r
9642     if( is2 != NULL && is2->hThread != NULL ) {\r
9643         ResumeThread( is2->hThread );\r
9644     }\r
9645   }\r
9646 \r
9647   return (InputSourceRef) is;\r
9648 }\r
9649 \r
9650 void\r
9651 RemoveInputSource(InputSourceRef isr)\r
9652 {\r
9653   InputSource *is;\r
9654 \r
9655   is = (InputSource *) isr;\r
9656   is->hThread = NULL;  /* tell thread to stop */\r
9657   CloseHandle(is->hThread);\r
9658   if (is->second != NULL) {\r
9659     is->second->hThread = NULL;\r
9660     CloseHandle(is->second->hThread);\r
9661   }\r
9662 }\r
9663 \r
9664 int no_wrap(char *message, int count)\r
9665 {\r
9666     ConsoleOutput(message, count, FALSE);\r
9667     return count;\r
9668 }\r
9669 \r
9670 int\r
9671 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9672 {\r
9673   DWORD dOutCount;\r
9674   int outCount = SOCKET_ERROR;\r
9675   ChildProc *cp = (ChildProc *) pr;\r
9676   static OVERLAPPED ovl;\r
9677   static int line = 0;\r
9678 \r
9679   if (pr == NoProc)\r
9680   {\r
9681     if (appData.noJoin || !appData.useInternalWrap)\r
9682       return no_wrap(message, count);\r
9683     else\r
9684     {\r
9685       int width = get_term_width();\r
9686       int len = wrap(NULL, message, count, width, &line);\r
9687       char *msg = malloc(len);\r
9688       int dbgchk;\r
9689 \r
9690       if (!msg)\r
9691         return no_wrap(message, count);\r
9692       else\r
9693       {\r
9694         dbgchk = wrap(msg, message, count, width, &line);\r
9695         if (dbgchk != len && appData.debugMode)\r
9696             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9697         ConsoleOutput(msg, len, FALSE);\r
9698         free(msg);\r
9699         return len;\r
9700       }\r
9701     }\r
9702   }\r
9703 \r
9704   if (ovl.hEvent == NULL) {\r
9705     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9706   }\r
9707   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9708 \r
9709   switch (cp->kind) {\r
9710   case CPSock:\r
9711   case CPRcmd:\r
9712     outCount = send(cp->sock, message, count, 0);\r
9713     if (outCount == SOCKET_ERROR) {\r
9714       *outError = WSAGetLastError();\r
9715     } else {\r
9716       *outError = NO_ERROR;\r
9717     }\r
9718     break;\r
9719 \r
9720   case CPReal:\r
9721     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9722                   &dOutCount, NULL)) {\r
9723       *outError = NO_ERROR;\r
9724       outCount = (int) dOutCount;\r
9725     } else {\r
9726       *outError = GetLastError();\r
9727     }\r
9728     break;\r
9729 \r
9730   case CPComm:\r
9731     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9732                             &dOutCount, &ovl);\r
9733     if (*outError == NO_ERROR) {\r
9734       outCount = (int) dOutCount;\r
9735     }\r
9736     break;\r
9737   }\r
9738   return outCount;\r
9739 }\r
9740 \r
9741 void\r
9742 DoSleep(int n)\r
9743 {\r
9744     if(n != 0) Sleep(n);\r
9745 }\r
9746 \r
9747 int\r
9748 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9749                        long msdelay)\r
9750 {\r
9751   /* Ignore delay, not implemented for WinBoard */\r
9752   return OutputToProcess(pr, message, count, outError);\r
9753 }\r
9754 \r
9755 \r
9756 void\r
9757 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9758                         char *buf, int count, int error)\r
9759 {\r
9760   DisplayFatalError(_("Not implemented"), 0, 1);\r
9761 }\r
9762 \r
9763 /* see wgamelist.c for Game List functions */\r
9764 /* see wedittags.c for Edit Tags functions */\r
9765 \r
9766 \r
9767 int\r
9768 ICSInitScript()\r
9769 {\r
9770   FILE *f;\r
9771   char buf[MSG_SIZ];\r
9772   char *dummy;\r
9773 \r
9774   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9775     f = fopen(buf, "r");\r
9776     if (f != NULL) {\r
9777       ProcessICSInitScript(f);\r
9778       fclose(f);\r
9779       return TRUE;\r
9780     }\r
9781   }\r
9782   return FALSE;\r
9783 }\r
9784 \r
9785 \r
9786 VOID\r
9787 StartAnalysisClock()\r
9788 {\r
9789   if (analysisTimerEvent) return;\r
9790   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9791                                         (UINT) 2000, NULL);\r
9792 }\r
9793 \r
9794 VOID\r
9795 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9796 {\r
9797   highlightInfo.sq[0].x = fromX;\r
9798   highlightInfo.sq[0].y = fromY;\r
9799   highlightInfo.sq[1].x = toX;\r
9800   highlightInfo.sq[1].y = toY;\r
9801 }\r
9802 \r
9803 VOID\r
9804 ClearHighlights()\r
9805 {\r
9806   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9807     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9808 }\r
9809 \r
9810 VOID\r
9811 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9812 {\r
9813   premoveHighlightInfo.sq[0].x = fromX;\r
9814   premoveHighlightInfo.sq[0].y = fromY;\r
9815   premoveHighlightInfo.sq[1].x = toX;\r
9816   premoveHighlightInfo.sq[1].y = toY;\r
9817 }\r
9818 \r
9819 VOID\r
9820 ClearPremoveHighlights()\r
9821 {\r
9822   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9823     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9824 }\r
9825 \r
9826 VOID\r
9827 ShutDownFrontEnd()\r
9828 {\r
9829   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9830   DeleteClipboardTempFiles();\r
9831 }\r
9832 \r
9833 void\r
9834 BoardToTop()\r
9835 {\r
9836     if (IsIconic(hwndMain))\r
9837       ShowWindow(hwndMain, SW_RESTORE);\r
9838 \r
9839     SetActiveWindow(hwndMain);\r
9840 }\r
9841 \r
9842 /*\r
9843  * Prototypes for animation support routines\r
9844  */\r
9845 static void ScreenSquare(int column, int row, POINT * pt);\r
9846 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9847      POINT frames[], int * nFrames);\r
9848 \r
9849 \r
9850 #define kFactor 4\r
9851 \r
9852 void\r
9853 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9854 {       // [HGM] atomic: animate blast wave\r
9855         int i;\r
9856 \r
9857         explodeInfo.fromX = fromX;\r
9858         explodeInfo.fromY = fromY;\r
9859         explodeInfo.toX = toX;\r
9860         explodeInfo.toY = toY;\r
9861         for(i=1; i<4*kFactor; i++) {\r
9862             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9863             DrawPosition(FALSE, board);\r
9864             Sleep(appData.animSpeed);\r
9865         }\r
9866         explodeInfo.radius = 0;\r
9867         DrawPosition(TRUE, board);\r
9868 }\r
9869 \r
9870 void\r
9871 AnimateMove(board, fromX, fromY, toX, toY)\r
9872      Board board;\r
9873      int fromX;\r
9874      int fromY;\r
9875      int toX;\r
9876      int toY;\r
9877 {\r
9878   ChessSquare piece;\r
9879   POINT start, finish, mid;\r
9880   POINT frames[kFactor * 2 + 1];\r
9881   int nFrames, n;\r
9882 \r
9883   if (!appData.animate) return;\r
9884   if (doingSizing) return;\r
9885   if (fromY < 0 || fromX < 0) return;\r
9886   piece = board[fromY][fromX];\r
9887   if (piece >= EmptySquare) return;\r
9888 \r
9889   ScreenSquare(fromX, fromY, &start);\r
9890   ScreenSquare(toX, toY, &finish);\r
9891 \r
9892   /* All moves except knight jumps move in straight line */\r
9893   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9894     mid.x = start.x + (finish.x - start.x) / 2;\r
9895     mid.y = start.y + (finish.y - start.y) / 2;\r
9896   } else {\r
9897     /* Knight: make straight movement then diagonal */\r
9898     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9899        mid.x = start.x + (finish.x - start.x) / 2;\r
9900        mid.y = start.y;\r
9901      } else {\r
9902        mid.x = start.x;\r
9903        mid.y = start.y + (finish.y - start.y) / 2;\r
9904      }\r
9905   }\r
9906   \r
9907   /* Don't use as many frames for very short moves */\r
9908   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9909     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9910   else\r
9911     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9912 \r
9913   animInfo.from.x = fromX;\r
9914   animInfo.from.y = fromY;\r
9915   animInfo.to.x = toX;\r
9916   animInfo.to.y = toY;\r
9917   animInfo.lastpos = start;\r
9918   animInfo.piece = piece;\r
9919   for (n = 0; n < nFrames; n++) {\r
9920     animInfo.pos = frames[n];\r
9921     DrawPosition(FALSE, NULL);\r
9922     animInfo.lastpos = animInfo.pos;\r
9923     Sleep(appData.animSpeed);\r
9924   }\r
9925   animInfo.pos = finish;\r
9926   DrawPosition(FALSE, NULL);\r
9927   animInfo.piece = EmptySquare;\r
9928   Explode(board, fromX, fromY, toX, toY);\r
9929 }\r
9930 \r
9931 /*      Convert board position to corner of screen rect and color       */\r
9932 \r
9933 static void\r
9934 ScreenSquare(column, row, pt)\r
9935      int column; int row; POINT * pt;\r
9936 {\r
9937   if (flipView) {\r
9938     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9939     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9940   } else {\r
9941     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9942     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9943   }\r
9944 }\r
9945 \r
9946 /*      Generate a series of frame coords from start->mid->finish.\r
9947         The movement rate doubles until the half way point is\r
9948         reached, then halves back down to the final destination,\r
9949         which gives a nice slow in/out effect. The algorithmn\r
9950         may seem to generate too many intermediates for short\r
9951         moves, but remember that the purpose is to attract the\r
9952         viewers attention to the piece about to be moved and\r
9953         then to where it ends up. Too few frames would be less\r
9954         noticeable.                                             */\r
9955 \r
9956 static void\r
9957 Tween(start, mid, finish, factor, frames, nFrames)\r
9958      POINT * start; POINT * mid;\r
9959      POINT * finish; int factor;\r
9960      POINT frames[]; int * nFrames;\r
9961 {\r
9962   int n, fraction = 1, count = 0;\r
9963 \r
9964   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9965   for (n = 0; n < factor; n++)\r
9966     fraction *= 2;\r
9967   for (n = 0; n < factor; n++) {\r
9968     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9969     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9970     count ++;\r
9971     fraction = fraction / 2;\r
9972   }\r
9973   \r
9974   /* Midpoint */\r
9975   frames[count] = *mid;\r
9976   count ++;\r
9977   \r
9978   /* Slow out, stepping 1/2, then 1/4, ... */\r
9979   fraction = 2;\r
9980   for (n = 0; n < factor; n++) {\r
9981     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9982     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9983     count ++;\r
9984     fraction = fraction * 2;\r
9985   }\r
9986   *nFrames = count;\r
9987 }\r
9988 \r
9989 void\r
9990 SettingsPopUp(ChessProgramState *cps)\r
9991 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9992       EngineOptionsPopup(savedHwnd, cps);\r
9993 }\r
9994 \r
9995 int flock(int fid, int code)\r
9996 {\r
9997     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9998     OVERLAPPED ov;\r
9999     ov.hEvent = NULL;\r
10000     ov.Offset = 0;\r
10001     ov.OffsetHigh = 0;\r
10002     switch(code) {\r
10003       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10004       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10005       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10006       default: return -1;\r
10007     }\r
10008     return 0;\r
10009 }\r
10010 \r
10011 char *\r
10012 Col2Text (int n)\r
10013 {\r
10014     static int i=0;\r
10015     static char col[8][20];\r
10016     COLORREF color = *(COLORREF *) colorVariable[n];\r
10017     i = i+1 & 7;\r
10018     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10019     return col[i];\r
10020 }\r
10021 \r
10022 void\r
10023 ActivateTheme (int new)\r
10024 {   // Redo initialization of features depending on options that can occur in themes\r
10025    InitTextures();\r
10026    if(new) InitDrawingColors();\r
10027    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10028    InitDrawingSizes(-2, 0);\r
10029    InvalidateRect(hwndMain, NULL, TRUE);\r
10030 }\r