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