new version number for release of 4.7.0
[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     SetCurrentDirectory(installDir);\r
1067   } else {\r
1068     GetCurrentDirectory(MSG_SIZ, installDir);\r
1069   }\r
1070   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1071   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1072   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1073   /* xboard, and older WinBoards, controlled the move sound with the\r
1074      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1075      always turn the option on (so that the backend will call us),\r
1076      then let the user turn the sound off by setting it to silence if\r
1077      desired.  To accommodate old winboard.ini files saved by old\r
1078      versions of WinBoard, we also turn off the sound if the option\r
1079      was initially set to false. [HGM] taken out of InitAppData */\r
1080   if (!appData.ringBellAfterMoves) {\r
1081     sounds[(int)SoundMove].name = strdup("");\r
1082     appData.ringBellAfterMoves = TRUE;\r
1083   }\r
1084   if (appData.debugMode) {\r
1085     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1086     setbuf(debugFP, NULL);\r
1087   }\r
1088 \r
1089   LoadLanguageFile(appData.language);\r
1090 \r
1091   InitBackEnd1();\r
1092 \r
1093 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1094 //  InitEngineUCI( installDir, &second );\r
1095 \r
1096   /* Create a main window for this application instance. */\r
1097   hwnd = CreateWindow(szAppName, szTitle,\r
1098                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1099                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1100                       NULL, NULL, hInstance, NULL);\r
1101   hwndMain = hwnd;\r
1102 \r
1103   /* If window could not be created, return "failure" */\r
1104   if (!hwnd) {\r
1105     return (FALSE);\r
1106   }\r
1107 \r
1108   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1109   LoadLogo(&first, 0, FALSE);\r
1110   LoadLogo(&second, 1, appData.icsActive);\r
1111 \r
1112   SetUserLogo();\r
1113 \r
1114   iconWhite = LoadIcon(hInstance, "icon_white");\r
1115   iconBlack = LoadIcon(hInstance, "icon_black");\r
1116   iconCurrent = iconWhite;\r
1117   InitDrawingColors();\r
1118   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1119   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1120   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1121     /* Compute window size for each board size, and use the largest\r
1122        size that fits on this screen as the default. */\r
1123     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1124     if (boardSize == (BoardSize)-1 &&\r
1125         winH <= screenHeight\r
1126            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1127         && winW <= screenWidth) {\r
1128       boardSize = (BoardSize)ibs;\r
1129     }\r
1130   }\r
1131 \r
1132   InitDrawingSizes(boardSize, 0);\r
1133   RecentEngineMenu(appData.recentEngineList);\r
1134   InitMenuChecks();\r
1135   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1136 \r
1137   /* [AS] Load textures if specified */\r
1138   InitTextures();\r
1139 \r
1140   mysrandom( (unsigned) time(NULL) );\r
1141 \r
1142   /* [AS] Restore layout */\r
1143   if( wpMoveHistory.visible ) {\r
1144       MoveHistoryPopUp();\r
1145   }\r
1146 \r
1147   if( wpEvalGraph.visible ) {\r
1148       EvalGraphPopUp();\r
1149   }\r
1150 \r
1151   if( wpEngineOutput.visible ) {\r
1152       EngineOutputPopUp();\r
1153   }\r
1154 \r
1155   /* Make the window visible; update its client area; and return "success" */\r
1156   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1157   wp.length = sizeof(WINDOWPLACEMENT);\r
1158   wp.flags = 0;\r
1159   wp.showCmd = nCmdShow;\r
1160   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1161   wp.rcNormalPosition.left = wpMain.x;\r
1162   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1163   wp.rcNormalPosition.top = wpMain.y;\r
1164   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1165   SetWindowPlacement(hwndMain, &wp);\r
1166 \r
1167   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1168 \r
1169   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1170                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1171 \r
1172   if (hwndConsole) {\r
1173 #if AOT_CONSOLE\r
1174     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1175                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1176 #endif\r
1177     ShowWindow(hwndConsole, nCmdShow);\r
1178     SetActiveWindow(hwndConsole);\r
1179   }\r
1180   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1181   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1182 \r
1183   return TRUE;\r
1184 \r
1185 }\r
1186 \r
1187 VOID\r
1188 InitMenuChecks()\r
1189 {\r
1190   HMENU hmenu = GetMenu(hwndMain);\r
1191 \r
1192   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1193                         MF_BYCOMMAND|((appData.icsActive &&\r
1194                                        *appData.icsCommPort != NULLCHAR) ?\r
1195                                       MF_ENABLED : MF_GRAYED));\r
1196   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1197                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1198                                      MF_CHECKED : MF_UNCHECKED));\r
1199 }\r
1200 \r
1201 //---------------------------------------------------------------------------------------------------------\r
1202 \r
1203 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1204 #define XBOARD FALSE\r
1205 \r
1206 #define OPTCHAR "/"\r
1207 #define SEPCHAR "="\r
1208 #define TOPLEVEL 0\r
1209 \r
1210 #include "args.h"\r
1211 \r
1212 // front-end part of option handling\r
1213 \r
1214 VOID\r
1215 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1216 {\r
1217   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1218   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1219   DeleteDC(hdc);\r
1220   lf->lfWidth = 0;\r
1221   lf->lfEscapement = 0;\r
1222   lf->lfOrientation = 0;\r
1223   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1224   lf->lfItalic = mfp->italic;\r
1225   lf->lfUnderline = mfp->underline;\r
1226   lf->lfStrikeOut = mfp->strikeout;\r
1227   lf->lfCharSet = mfp->charset;\r
1228   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1229   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1230   lf->lfQuality = DEFAULT_QUALITY;\r
1231   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1232     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1233 }\r
1234 \r
1235 void\r
1236 CreateFontInMF(MyFont *mf)\r
1237\r
1238   LFfromMFP(&mf->lf, &mf->mfp);\r
1239   if (mf->hf) DeleteObject(mf->hf);\r
1240   mf->hf = CreateFontIndirect(&mf->lf);\r
1241 }\r
1242 \r
1243 // [HGM] This platform-dependent table provides the location for storing the color info\r
1244 void *\r
1245 colorVariable[] = {\r
1246   &whitePieceColor, \r
1247   &blackPieceColor, \r
1248   &lightSquareColor,\r
1249   &darkSquareColor, \r
1250   &highlightSquareColor,\r
1251   &premoveHighlightColor,\r
1252   NULL,\r
1253   &consoleBackgroundColor,\r
1254   &appData.fontForeColorWhite,\r
1255   &appData.fontBackColorWhite,\r
1256   &appData.fontForeColorBlack,\r
1257   &appData.fontBackColorBlack,\r
1258   &appData.evalHistColorWhite,\r
1259   &appData.evalHistColorBlack,\r
1260   &appData.highlightArrowColor,\r
1261 };\r
1262 \r
1263 /* Command line font name parser.  NULL name means do nothing.\r
1264    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1265    For backward compatibility, syntax without the colon is also\r
1266    accepted, but font names with digits in them won't work in that case.\r
1267 */\r
1268 VOID\r
1269 ParseFontName(char *name, MyFontParams *mfp)\r
1270 {\r
1271   char *p, *q;\r
1272   if (name == NULL) return;\r
1273   p = name;\r
1274   q = strchr(p, ':');\r
1275   if (q) {\r
1276     if (q - p >= sizeof(mfp->faceName))\r
1277       ExitArgError(_("Font name too long:"), name, TRUE);\r
1278     memcpy(mfp->faceName, p, q - p);\r
1279     mfp->faceName[q - p] = NULLCHAR;\r
1280     p = q + 1;\r
1281   } else {\r
1282     q = mfp->faceName;\r
1283 \r
1284     while (*p && !isdigit(*p)) {\r
1285       *q++ = *p++;\r
1286       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1287         ExitArgError(_("Font name too long:"), name, TRUE);\r
1288     }\r
1289     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1290     *q = NULLCHAR;\r
1291   }\r
1292   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1293   mfp->pointSize = (float) atof(p);\r
1294   mfp->bold = (strchr(p, 'b') != NULL);\r
1295   mfp->italic = (strchr(p, 'i') != NULL);\r
1296   mfp->underline = (strchr(p, 'u') != NULL);\r
1297   mfp->strikeout = (strchr(p, 's') != NULL);\r
1298   mfp->charset = DEFAULT_CHARSET;\r
1299   q = strchr(p, 'c');\r
1300   if (q)\r
1301     mfp->charset = (BYTE) atoi(q+1);\r
1302 }\r
1303 \r
1304 void\r
1305 ParseFont(char *name, int number)\r
1306 { // wrapper to shield back-end from 'font'\r
1307   ParseFontName(name, &font[boardSize][number]->mfp);\r
1308 }\r
1309 \r
1310 void\r
1311 SetFontDefaults()\r
1312 { // in WB  we have a 2D array of fonts; this initializes their description\r
1313   int i, j;\r
1314   /* Point font array elements to structures and\r
1315      parse default font names */\r
1316   for (i=0; i<NUM_FONTS; i++) {\r
1317     for (j=0; j<NUM_SIZES; j++) {\r
1318       font[j][i] = &fontRec[j][i];\r
1319       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1320     }\r
1321   }\r
1322 }\r
1323 \r
1324 void\r
1325 CreateFonts()\r
1326 { // here we create the actual fonts from the selected descriptions\r
1327   int i, j;\r
1328   for (i=0; i<NUM_FONTS; i++) {\r
1329     for (j=0; j<NUM_SIZES; j++) {\r
1330       CreateFontInMF(font[j][i]);\r
1331     }\r
1332   }\r
1333 }\r
1334 /* Color name parser.\r
1335    X version accepts X color names, but this one\r
1336    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1337 COLORREF\r
1338 ParseColorName(char *name)\r
1339 {\r
1340   int red, green, blue, count;\r
1341   char buf[MSG_SIZ];\r
1342 \r
1343   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1344   if (count != 3) {\r
1345     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1346       &red, &green, &blue);\r
1347   }\r
1348   if (count != 3) {\r
1349     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1350     DisplayError(buf, 0);\r
1351     return RGB(0, 0, 0);\r
1352   }\r
1353   return PALETTERGB(red, green, blue);\r
1354 }\r
1355 \r
1356 void\r
1357 ParseColor(int n, char *name)\r
1358 { // for WinBoard the color is an int, which needs to be derived from the string\r
1359   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1360 }\r
1361 \r
1362 void\r
1363 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1364 {\r
1365   char *e = argValue;\r
1366   int eff = 0;\r
1367 \r
1368   while (*e) {\r
1369     if (*e == 'b')      eff |= CFE_BOLD;\r
1370     else if (*e == 'i') eff |= CFE_ITALIC;\r
1371     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1372     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1373     else if (*e == '#' || isdigit(*e)) break;\r
1374     e++;\r
1375   }\r
1376   *effects = eff;\r
1377   *color   = ParseColorName(e);\r
1378 }\r
1379 \r
1380 void\r
1381 ParseTextAttribs(ColorClass cc, char *s)\r
1382 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1383     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1384     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1385 }\r
1386 \r
1387 void\r
1388 ParseBoardSize(void *addr, char *name)\r
1389 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1390   BoardSize bs = SizeTiny;\r
1391   while (sizeInfo[bs].name != NULL) {\r
1392     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1393         *(BoardSize *)addr = bs;\r
1394         return;\r
1395     }\r
1396     bs++;\r
1397   }\r
1398   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1399 }\r
1400 \r
1401 void\r
1402 LoadAllSounds()\r
1403 { // [HGM] import name from appData first\r
1404   ColorClass cc;\r
1405   SoundClass sc;\r
1406   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1407     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1408     textAttribs[cc].sound.data = NULL;\r
1409     MyLoadSound(&textAttribs[cc].sound);\r
1410   }\r
1411   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1412     textAttribs[cc].sound.name = strdup("");\r
1413     textAttribs[cc].sound.data = NULL;\r
1414   }\r
1415   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1416     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1417     sounds[sc].data = NULL;\r
1418     MyLoadSound(&sounds[sc]);\r
1419   }\r
1420 }\r
1421 \r
1422 void\r
1423 SetCommPortDefaults()\r
1424 {\r
1425    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1426   dcb.DCBlength = sizeof(DCB);\r
1427   dcb.BaudRate = 9600;\r
1428   dcb.fBinary = TRUE;\r
1429   dcb.fParity = FALSE;\r
1430   dcb.fOutxCtsFlow = FALSE;\r
1431   dcb.fOutxDsrFlow = FALSE;\r
1432   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1433   dcb.fDsrSensitivity = FALSE;\r
1434   dcb.fTXContinueOnXoff = TRUE;\r
1435   dcb.fOutX = FALSE;\r
1436   dcb.fInX = FALSE;\r
1437   dcb.fNull = FALSE;\r
1438   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1439   dcb.fAbortOnError = FALSE;\r
1440   dcb.ByteSize = 7;\r
1441   dcb.Parity = SPACEPARITY;\r
1442   dcb.StopBits = ONESTOPBIT;\r
1443 }\r
1444 \r
1445 // [HGM] args: these three cases taken out to stay in front-end\r
1446 void\r
1447 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1448 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1449         // while the curent board size determines the element. This system should be ported to XBoard.\r
1450         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1451         int bs;\r
1452         for (bs=0; bs<NUM_SIZES; bs++) {\r
1453           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1454           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1455           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1456             ad->argName, mfp->faceName, mfp->pointSize,\r
1457             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1458             mfp->bold ? "b" : "",\r
1459             mfp->italic ? "i" : "",\r
1460             mfp->underline ? "u" : "",\r
1461             mfp->strikeout ? "s" : "",\r
1462             (int)mfp->charset);\r
1463         }\r
1464       }\r
1465 \r
1466 void\r
1467 ExportSounds()\r
1468 { // [HGM] copy the names from the internal WB variables to appData\r
1469   ColorClass cc;\r
1470   SoundClass sc;\r
1471   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1472     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1473   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1474     (&appData.soundMove)[sc] = sounds[sc].name;\r
1475 }\r
1476 \r
1477 void\r
1478 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1479 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1480         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1481         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1482           (ta->effects & CFE_BOLD) ? "b" : "",\r
1483           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1484           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1485           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1486           (ta->effects) ? " " : "",\r
1487           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1488       }\r
1489 \r
1490 void\r
1491 SaveColor(FILE *f, ArgDescriptor *ad)\r
1492 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1493         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1494         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1495           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1496 }\r
1497 \r
1498 void\r
1499 SaveBoardSize(FILE *f, char *name, void *addr)\r
1500 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1501   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1502 }\r
1503 \r
1504 void\r
1505 ParseCommPortSettings(char *s)\r
1506 { // wrapper to keep dcb from back-end\r
1507   ParseCommSettings(s, &dcb);\r
1508 }\r
1509 \r
1510 void\r
1511 GetWindowCoords()\r
1512 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1513   GetActualPlacement(hwndMain, &wpMain);\r
1514   GetActualPlacement(hwndConsole, &wpConsole);\r
1515   GetActualPlacement(commentDialog, &wpComment);\r
1516   GetActualPlacement(editTagsDialog, &wpTags);\r
1517   GetActualPlacement(gameListDialog, &wpGameList);\r
1518   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1519   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1520   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1521 }\r
1522 \r
1523 void\r
1524 PrintCommPortSettings(FILE *f, char *name)\r
1525 { // wrapper to shield back-end from DCB\r
1526       PrintCommSettings(f, name, &dcb);\r
1527 }\r
1528 \r
1529 int\r
1530 MySearchPath(char *installDir, char *name, char *fullname)\r
1531 {\r
1532   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1533   if(name[0]== '%') {\r
1534     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1535     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1536       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1537       *strchr(buf, '%') = 0;\r
1538       strcat(fullname, getenv(buf));\r
1539       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1540     }\r
1541     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1542     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1543     return (int) strlen(fullname);\r
1544   }\r
1545   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1546 }\r
1547 \r
1548 int\r
1549 MyGetFullPathName(char *name, char *fullname)\r
1550 {\r
1551   char *dummy;\r
1552   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1553 }\r
1554 \r
1555 int\r
1556 MainWindowUp()\r
1557 { // [HGM] args: allows testing if main window is realized from back-end\r
1558   return hwndMain != NULL;\r
1559 }\r
1560 \r
1561 void\r
1562 PopUpStartupDialog()\r
1563 {\r
1564     FARPROC lpProc;\r
1565     \r
1566     LoadLanguageFile(appData.language);\r
1567     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1568     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1569     FreeProcInstance(lpProc);\r
1570 }\r
1571 \r
1572 /*---------------------------------------------------------------------------*\\r
1573  *\r
1574  * GDI board drawing routines\r
1575  *\r
1576 \*---------------------------------------------------------------------------*/\r
1577 \r
1578 /* [AS] Draw square using background texture */\r
1579 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1580 {\r
1581     XFORM   x;\r
1582 \r
1583     if( mode == 0 ) {\r
1584         return; /* Should never happen! */\r
1585     }\r
1586 \r
1587     SetGraphicsMode( dst, GM_ADVANCED );\r
1588 \r
1589     switch( mode ) {\r
1590     case 1:\r
1591         /* Identity */\r
1592         break;\r
1593     case 2:\r
1594         /* X reflection */\r
1595         x.eM11 = -1.0;\r
1596         x.eM12 = 0;\r
1597         x.eM21 = 0;\r
1598         x.eM22 = 1.0;\r
1599         x.eDx = (FLOAT) dw + dx - 1;\r
1600         x.eDy = 0;\r
1601         dx = 0;\r
1602         SetWorldTransform( dst, &x );\r
1603         break;\r
1604     case 3:\r
1605         /* Y reflection */\r
1606         x.eM11 = 1.0;\r
1607         x.eM12 = 0;\r
1608         x.eM21 = 0;\r
1609         x.eM22 = -1.0;\r
1610         x.eDx = 0;\r
1611         x.eDy = (FLOAT) dh + dy - 1;\r
1612         dy = 0;\r
1613         SetWorldTransform( dst, &x );\r
1614         break;\r
1615     case 4:\r
1616         /* X/Y flip */\r
1617         x.eM11 = 0;\r
1618         x.eM12 = 1.0;\r
1619         x.eM21 = 1.0;\r
1620         x.eM22 = 0;\r
1621         x.eDx = (FLOAT) dx;\r
1622         x.eDy = (FLOAT) dy;\r
1623         dx = 0;\r
1624         dy = 0;\r
1625         SetWorldTransform( dst, &x );\r
1626         break;\r
1627     }\r
1628 \r
1629     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1630 \r
1631     x.eM11 = 1.0;\r
1632     x.eM12 = 0;\r
1633     x.eM21 = 0;\r
1634     x.eM22 = 1.0;\r
1635     x.eDx = 0;\r
1636     x.eDy = 0;\r
1637     SetWorldTransform( dst, &x );\r
1638 \r
1639     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1640 }\r
1641 \r
1642 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1643 enum {\r
1644     PM_WP = (int) WhitePawn, \r
1645     PM_WN = (int) WhiteKnight, \r
1646     PM_WB = (int) WhiteBishop, \r
1647     PM_WR = (int) WhiteRook, \r
1648     PM_WQ = (int) WhiteQueen, \r
1649     PM_WF = (int) WhiteFerz, \r
1650     PM_WW = (int) WhiteWazir, \r
1651     PM_WE = (int) WhiteAlfil, \r
1652     PM_WM = (int) WhiteMan, \r
1653     PM_WO = (int) WhiteCannon, \r
1654     PM_WU = (int) WhiteUnicorn, \r
1655     PM_WH = (int) WhiteNightrider, \r
1656     PM_WA = (int) WhiteAngel, \r
1657     PM_WC = (int) WhiteMarshall, \r
1658     PM_WAB = (int) WhiteCardinal, \r
1659     PM_WD = (int) WhiteDragon, \r
1660     PM_WL = (int) WhiteLance, \r
1661     PM_WS = (int) WhiteCobra, \r
1662     PM_WV = (int) WhiteFalcon, \r
1663     PM_WSG = (int) WhiteSilver, \r
1664     PM_WG = (int) WhiteGrasshopper, \r
1665     PM_WK = (int) WhiteKing,\r
1666     PM_BP = (int) BlackPawn, \r
1667     PM_BN = (int) BlackKnight, \r
1668     PM_BB = (int) BlackBishop, \r
1669     PM_BR = (int) BlackRook, \r
1670     PM_BQ = (int) BlackQueen, \r
1671     PM_BF = (int) BlackFerz, \r
1672     PM_BW = (int) BlackWazir, \r
1673     PM_BE = (int) BlackAlfil, \r
1674     PM_BM = (int) BlackMan,\r
1675     PM_BO = (int) BlackCannon, \r
1676     PM_BU = (int) BlackUnicorn, \r
1677     PM_BH = (int) BlackNightrider, \r
1678     PM_BA = (int) BlackAngel, \r
1679     PM_BC = (int) BlackMarshall, \r
1680     PM_BG = (int) BlackGrasshopper, \r
1681     PM_BAB = (int) BlackCardinal,\r
1682     PM_BD = (int) BlackDragon,\r
1683     PM_BL = (int) BlackLance,\r
1684     PM_BS = (int) BlackCobra,\r
1685     PM_BV = (int) BlackFalcon,\r
1686     PM_BSG = (int) BlackSilver,\r
1687     PM_BK = (int) BlackKing\r
1688 };\r
1689 \r
1690 static HFONT hPieceFont = NULL;\r
1691 static HBITMAP hPieceMask[(int) EmptySquare];\r
1692 static HBITMAP hPieceFace[(int) EmptySquare];\r
1693 static int fontBitmapSquareSize = 0;\r
1694 static char pieceToFontChar[(int) EmptySquare] =\r
1695                               { 'p', 'n', 'b', 'r', 'q', \r
1696                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1697                       'k', 'o', 'm', 'v', 't', 'w', \r
1698                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1699                                                               'l' };\r
1700 \r
1701 extern BOOL SetCharTable( char *table, const char * map );\r
1702 /* [HGM] moved to backend.c */\r
1703 \r
1704 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1705 {\r
1706     HBRUSH hbrush;\r
1707     BYTE r1 = GetRValue( color );\r
1708     BYTE g1 = GetGValue( color );\r
1709     BYTE b1 = GetBValue( color );\r
1710     BYTE r2 = r1 / 2;\r
1711     BYTE g2 = g1 / 2;\r
1712     BYTE b2 = b1 / 2;\r
1713     RECT rc;\r
1714 \r
1715     /* Create a uniform background first */\r
1716     hbrush = CreateSolidBrush( color );\r
1717     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1718     FillRect( hdc, &rc, hbrush );\r
1719     DeleteObject( hbrush );\r
1720     \r
1721     if( mode == 1 ) {\r
1722         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1723         int steps = squareSize / 2;\r
1724         int i;\r
1725 \r
1726         for( i=0; i<steps; i++ ) {\r
1727             BYTE r = r1 - (r1-r2) * i / steps;\r
1728             BYTE g = g1 - (g1-g2) * i / steps;\r
1729             BYTE b = b1 - (b1-b2) * i / steps;\r
1730 \r
1731             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1732             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1733             FillRect( hdc, &rc, hbrush );\r
1734             DeleteObject(hbrush);\r
1735         }\r
1736     }\r
1737     else if( mode == 2 ) {\r
1738         /* Diagonal gradient, good more or less for every piece */\r
1739         POINT triangle[3];\r
1740         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1741         HBRUSH hbrush_old;\r
1742         int steps = squareSize;\r
1743         int i;\r
1744 \r
1745         triangle[0].x = squareSize - steps;\r
1746         triangle[0].y = squareSize;\r
1747         triangle[1].x = squareSize;\r
1748         triangle[1].y = squareSize;\r
1749         triangle[2].x = squareSize;\r
1750         triangle[2].y = squareSize - steps;\r
1751 \r
1752         for( i=0; i<steps; i++ ) {\r
1753             BYTE r = r1 - (r1-r2) * i / steps;\r
1754             BYTE g = g1 - (g1-g2) * i / steps;\r
1755             BYTE b = b1 - (b1-b2) * i / steps;\r
1756 \r
1757             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1758             hbrush_old = SelectObject( hdc, hbrush );\r
1759             Polygon( hdc, triangle, 3 );\r
1760             SelectObject( hdc, hbrush_old );\r
1761             DeleteObject(hbrush);\r
1762             triangle[0].x++;\r
1763             triangle[2].y++;\r
1764         }\r
1765 \r
1766         SelectObject( hdc, hpen );\r
1767     }\r
1768 }\r
1769 \r
1770 /*\r
1771     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1772     seems to work ok. The main problem here is to find the "inside" of a chess\r
1773     piece: follow the steps as explained below.\r
1774 */\r
1775 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1776 {\r
1777     HBITMAP hbm;\r
1778     HBITMAP hbm_old;\r
1779     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1780     RECT rc;\r
1781     SIZE sz;\r
1782     POINT pt;\r
1783     int backColor = whitePieceColor; \r
1784     int foreColor = blackPieceColor;\r
1785     \r
1786     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1787         backColor = appData.fontBackColorWhite;\r
1788         foreColor = appData.fontForeColorWhite;\r
1789     }\r
1790     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1791         backColor = appData.fontBackColorBlack;\r
1792         foreColor = appData.fontForeColorBlack;\r
1793     }\r
1794 \r
1795     /* Mask */\r
1796     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1797 \r
1798     hbm_old = SelectObject( hdc, hbm );\r
1799 \r
1800     rc.left = 0;\r
1801     rc.top = 0;\r
1802     rc.right = squareSize;\r
1803     rc.bottom = squareSize;\r
1804 \r
1805     /* Step 1: background is now black */\r
1806     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1807 \r
1808     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1809 \r
1810     pt.x = (squareSize - sz.cx) / 2;\r
1811     pt.y = (squareSize - sz.cy) / 2;\r
1812 \r
1813     SetBkMode( hdc, TRANSPARENT );\r
1814     SetTextColor( hdc, chroma );\r
1815     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1816     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1817 \r
1818     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1819     /* Step 3: the area outside the piece is filled with white */\r
1820 //    FloodFill( hdc, 0, 0, chroma );\r
1821     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1822     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1823     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1824     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1825     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1826     /* \r
1827         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1828         but if the start point is not inside the piece we're lost!\r
1829         There should be a better way to do this... if we could create a region or path\r
1830         from the fill operation we would be fine for example.\r
1831     */\r
1832 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1833     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1834 \r
1835     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1836         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1837         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1838 \r
1839         SelectObject( dc2, bm2 );\r
1840         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1841         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1842         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1843         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1844         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1845 \r
1846         DeleteDC( dc2 );\r
1847         DeleteObject( bm2 );\r
1848     }\r
1849 \r
1850     SetTextColor( hdc, 0 );\r
1851     /* \r
1852         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1853         draw the piece again in black for safety.\r
1854     */\r
1855     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1856 \r
1857     SelectObject( hdc, hbm_old );\r
1858 \r
1859     if( hPieceMask[index] != NULL ) {\r
1860         DeleteObject( hPieceMask[index] );\r
1861     }\r
1862 \r
1863     hPieceMask[index] = hbm;\r
1864 \r
1865     /* Face */\r
1866     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1867 \r
1868     SelectObject( hdc, hbm );\r
1869 \r
1870     {\r
1871         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1872         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1873         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1874 \r
1875         SelectObject( dc1, hPieceMask[index] );\r
1876         SelectObject( dc2, bm2 );\r
1877         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1878         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1879         \r
1880         /* \r
1881             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1882             the piece background and deletes (makes transparent) the rest.\r
1883             Thanks to that mask, we are free to paint the background with the greates\r
1884             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1885             We use this, to make gradients and give the pieces a "roundish" look.\r
1886         */\r
1887         SetPieceBackground( hdc, backColor, 2 );\r
1888         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1889 \r
1890         DeleteDC( dc2 );\r
1891         DeleteDC( dc1 );\r
1892         DeleteObject( bm2 );\r
1893     }\r
1894 \r
1895     SetTextColor( hdc, foreColor );\r
1896     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1897 \r
1898     SelectObject( hdc, hbm_old );\r
1899 \r
1900     if( hPieceFace[index] != NULL ) {\r
1901         DeleteObject( hPieceFace[index] );\r
1902     }\r
1903 \r
1904     hPieceFace[index] = hbm;\r
1905 }\r
1906 \r
1907 static int TranslatePieceToFontPiece( int piece )\r
1908 {\r
1909     switch( piece ) {\r
1910     case BlackPawn:\r
1911         return PM_BP;\r
1912     case BlackKnight:\r
1913         return PM_BN;\r
1914     case BlackBishop:\r
1915         return PM_BB;\r
1916     case BlackRook:\r
1917         return PM_BR;\r
1918     case BlackQueen:\r
1919         return PM_BQ;\r
1920     case BlackKing:\r
1921         return PM_BK;\r
1922     case WhitePawn:\r
1923         return PM_WP;\r
1924     case WhiteKnight:\r
1925         return PM_WN;\r
1926     case WhiteBishop:\r
1927         return PM_WB;\r
1928     case WhiteRook:\r
1929         return PM_WR;\r
1930     case WhiteQueen:\r
1931         return PM_WQ;\r
1932     case WhiteKing:\r
1933         return PM_WK;\r
1934 \r
1935     case BlackAngel:\r
1936         return PM_BA;\r
1937     case BlackMarshall:\r
1938         return PM_BC;\r
1939     case BlackFerz:\r
1940         return PM_BF;\r
1941     case BlackNightrider:\r
1942         return PM_BH;\r
1943     case BlackAlfil:\r
1944         return PM_BE;\r
1945     case BlackWazir:\r
1946         return PM_BW;\r
1947     case BlackUnicorn:\r
1948         return PM_BU;\r
1949     case BlackCannon:\r
1950         return PM_BO;\r
1951     case BlackGrasshopper:\r
1952         return PM_BG;\r
1953     case BlackMan:\r
1954         return PM_BM;\r
1955     case BlackSilver:\r
1956         return PM_BSG;\r
1957     case BlackLance:\r
1958         return PM_BL;\r
1959     case BlackFalcon:\r
1960         return PM_BV;\r
1961     case BlackCobra:\r
1962         return PM_BS;\r
1963     case BlackCardinal:\r
1964         return PM_BAB;\r
1965     case BlackDragon:\r
1966         return PM_BD;\r
1967 \r
1968     case WhiteAngel:\r
1969         return PM_WA;\r
1970     case WhiteMarshall:\r
1971         return PM_WC;\r
1972     case WhiteFerz:\r
1973         return PM_WF;\r
1974     case WhiteNightrider:\r
1975         return PM_WH;\r
1976     case WhiteAlfil:\r
1977         return PM_WE;\r
1978     case WhiteWazir:\r
1979         return PM_WW;\r
1980     case WhiteUnicorn:\r
1981         return PM_WU;\r
1982     case WhiteCannon:\r
1983         return PM_WO;\r
1984     case WhiteGrasshopper:\r
1985         return PM_WG;\r
1986     case WhiteMan:\r
1987         return PM_WM;\r
1988     case WhiteSilver:\r
1989         return PM_WSG;\r
1990     case WhiteLance:\r
1991         return PM_WL;\r
1992     case WhiteFalcon:\r
1993         return PM_WV;\r
1994     case WhiteCobra:\r
1995         return PM_WS;\r
1996     case WhiteCardinal:\r
1997         return PM_WAB;\r
1998     case WhiteDragon:\r
1999         return PM_WD;\r
2000     }\r
2001 \r
2002     return 0;\r
2003 }\r
2004 \r
2005 void CreatePiecesFromFont()\r
2006 {\r
2007     LOGFONT lf;\r
2008     HDC hdc_window = NULL;\r
2009     HDC hdc = NULL;\r
2010     HFONT hfont_old;\r
2011     int fontHeight;\r
2012     int i;\r
2013 \r
2014     if( fontBitmapSquareSize < 0 ) {\r
2015         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2016         return;\r
2017     }\r
2018 \r
2019     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2020             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2021         fontBitmapSquareSize = -1;\r
2022         return;\r
2023     }\r
2024 \r
2025     if( fontBitmapSquareSize != squareSize ) {\r
2026         hdc_window = GetDC( hwndMain );\r
2027         hdc = CreateCompatibleDC( hdc_window );\r
2028 \r
2029         if( hPieceFont != NULL ) {\r
2030             DeleteObject( hPieceFont );\r
2031         }\r
2032         else {\r
2033             for( i=0; i<=(int)BlackKing; i++ ) {\r
2034                 hPieceMask[i] = NULL;\r
2035                 hPieceFace[i] = NULL;\r
2036             }\r
2037         }\r
2038 \r
2039         fontHeight = 75;\r
2040 \r
2041         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2042             fontHeight = appData.fontPieceSize;\r
2043         }\r
2044 \r
2045         fontHeight = (fontHeight * squareSize) / 100;\r
2046 \r
2047         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2048         lf.lfWidth = 0;\r
2049         lf.lfEscapement = 0;\r
2050         lf.lfOrientation = 0;\r
2051         lf.lfWeight = FW_NORMAL;\r
2052         lf.lfItalic = 0;\r
2053         lf.lfUnderline = 0;\r
2054         lf.lfStrikeOut = 0;\r
2055         lf.lfCharSet = DEFAULT_CHARSET;\r
2056         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2057         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2058         lf.lfQuality = PROOF_QUALITY;\r
2059         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2060         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2061         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2062 \r
2063         hPieceFont = CreateFontIndirect( &lf );\r
2064 \r
2065         if( hPieceFont == NULL ) {\r
2066             fontBitmapSquareSize = -2;\r
2067         }\r
2068         else {\r
2069             /* Setup font-to-piece character table */\r
2070             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2071                 /* No (or wrong) global settings, try to detect the font */\r
2072                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2073                     /* Alpha */\r
2074                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2075                 }\r
2076                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2077                     /* DiagramTT* family */\r
2078                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2079                 }\r
2080                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2081                     /* Fairy symbols */\r
2082                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2083                 }\r
2084                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2085                     /* Good Companion (Some characters get warped as literal :-( */\r
2086                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2087                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2088                     SetCharTable(pieceToFontChar, s);\r
2089                 }\r
2090                 else {\r
2091                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2092                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2093                 }\r
2094             }\r
2095 \r
2096             /* Create bitmaps */\r
2097             hfont_old = SelectObject( hdc, hPieceFont );\r
2098             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2099                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2100                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2101 \r
2102             SelectObject( hdc, hfont_old );\r
2103 \r
2104             fontBitmapSquareSize = squareSize;\r
2105         }\r
2106     }\r
2107 \r
2108     if( hdc != NULL ) {\r
2109         DeleteDC( hdc );\r
2110     }\r
2111 \r
2112     if( hdc_window != NULL ) {\r
2113         ReleaseDC( hwndMain, hdc_window );\r
2114     }\r
2115 }\r
2116 \r
2117 HBITMAP\r
2118 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2119 {\r
2120   char name[128], buf[MSG_SIZ];\r
2121 \r
2122     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2123   if(appData.pieceDirectory[0]) {\r
2124     HBITMAP res;\r
2125     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2126     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2127     if(res) return res;\r
2128   }\r
2129   if (gameInfo.event &&\r
2130       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2131       strcmp(name, "k80s") == 0) {\r
2132     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2133   }\r
2134   return LoadBitmap(hinst, name);\r
2135 }\r
2136 \r
2137 \r
2138 /* Insert a color into the program's logical palette\r
2139    structure.  This code assumes the given color is\r
2140    the result of the RGB or PALETTERGB macro, and it\r
2141    knows how those macros work (which is documented).\r
2142 */\r
2143 VOID\r
2144 InsertInPalette(COLORREF color)\r
2145 {\r
2146   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2147 \r
2148   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2149     DisplayFatalError(_("Too many colors"), 0, 1);\r
2150     pLogPal->palNumEntries--;\r
2151     return;\r
2152   }\r
2153 \r
2154   pe->peFlags = (char) 0;\r
2155   pe->peRed = (char) (0xFF & color);\r
2156   pe->peGreen = (char) (0xFF & (color >> 8));\r
2157   pe->peBlue = (char) (0xFF & (color >> 16));\r
2158   return;\r
2159 }\r
2160 \r
2161 \r
2162 VOID\r
2163 InitDrawingColors()\r
2164 {\r
2165   if (pLogPal == NULL) {\r
2166     /* Allocate enough memory for a logical palette with\r
2167      * PALETTESIZE entries and set the size and version fields\r
2168      * of the logical palette structure.\r
2169      */\r
2170     pLogPal = (NPLOGPALETTE)\r
2171       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2172                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2173     pLogPal->palVersion    = 0x300;\r
2174   }\r
2175   pLogPal->palNumEntries = 0;\r
2176 \r
2177   InsertInPalette(lightSquareColor);\r
2178   InsertInPalette(darkSquareColor);\r
2179   InsertInPalette(whitePieceColor);\r
2180   InsertInPalette(blackPieceColor);\r
2181   InsertInPalette(highlightSquareColor);\r
2182   InsertInPalette(premoveHighlightColor);\r
2183 \r
2184   /*  create a logical color palette according the information\r
2185    *  in the LOGPALETTE structure.\r
2186    */\r
2187   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2188 \r
2189   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2190   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2191   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2192   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2193   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2194   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2195   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2196   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2197   /* [AS] Force rendering of the font-based pieces */\r
2198   if( fontBitmapSquareSize > 0 ) {\r
2199     fontBitmapSquareSize = 0;\r
2200   }\r
2201 }\r
2202 \r
2203 \r
2204 int\r
2205 BoardWidth(int boardSize, int n)\r
2206 { /* [HGM] argument n added to allow different width and height */\r
2207   int lineGap = sizeInfo[boardSize].lineGap;\r
2208 \r
2209   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2210       lineGap = appData.overrideLineGap;\r
2211   }\r
2212 \r
2213   return (n + 1) * lineGap +\r
2214           n * sizeInfo[boardSize].squareSize;\r
2215 }\r
2216 \r
2217 /* Respond to board resize by dragging edge */\r
2218 VOID\r
2219 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2220 {\r
2221   BoardSize newSize = NUM_SIZES - 1;\r
2222   static int recurse = 0;\r
2223   if (IsIconic(hwndMain)) return;\r
2224   if (recurse > 0) return;\r
2225   recurse++;\r
2226   while (newSize > 0) {\r
2227         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2228         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2229            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2230     newSize--;\r
2231   } \r
2232   boardSize = newSize;\r
2233   InitDrawingSizes(boardSize, flags);\r
2234   recurse--;\r
2235 }\r
2236 \r
2237 \r
2238 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2239 \r
2240 VOID\r
2241 InitDrawingSizes(BoardSize boardSize, int flags)\r
2242 {\r
2243   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2244   ChessSquare piece;\r
2245   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2246   HDC hdc;\r
2247   SIZE clockSize, messageSize;\r
2248   HFONT oldFont;\r
2249   char buf[MSG_SIZ];\r
2250   char *str;\r
2251   HMENU hmenu = GetMenu(hwndMain);\r
2252   RECT crect, wrect, oldRect;\r
2253   int offby;\r
2254   LOGBRUSH logbrush;\r
2255   VariantClass v = gameInfo.variant;\r
2256 \r
2257   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2258   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2259 \r
2260   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2261   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2262   oldBoardSize = boardSize;\r
2263 \r
2264   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2265   { // correct board size to one where built-in pieces exist\r
2266     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2267        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2268       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2269       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2270       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2271       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2272       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2273                                    boardSize = SizeMiddling;\r
2274     }\r
2275   }\r
2276   if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2277 \r
2278   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2279   oldRect.top = wpMain.y;\r
2280   oldRect.right = wpMain.x + wpMain.width;\r
2281   oldRect.bottom = wpMain.y + wpMain.height;\r
2282 \r
2283   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2284   smallLayout = sizeInfo[boardSize].smallLayout;\r
2285   squareSize = sizeInfo[boardSize].squareSize;\r
2286   lineGap = sizeInfo[boardSize].lineGap;\r
2287   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2288   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2289 \r
2290   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2291       lineGap = appData.overrideLineGap;\r
2292   }\r
2293 \r
2294   if (tinyLayout != oldTinyLayout) {\r
2295     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2296     if (tinyLayout) {\r
2297       style &= ~WS_SYSMENU;\r
2298       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2299                  "&Minimize\tCtrl+F4");\r
2300     } else {\r
2301       style |= WS_SYSMENU;\r
2302       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2303     }\r
2304     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2305 \r
2306     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2307       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2308         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2309     }\r
2310     DrawMenuBar(hwndMain);\r
2311   }\r
2312 \r
2313   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2314   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2315 \r
2316   /* Get text area sizes */\r
2317   hdc = GetDC(hwndMain);\r
2318   if (appData.clockMode) {\r
2319     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2320   } else {\r
2321     snprintf(buf, MSG_SIZ, _("White"));\r
2322   }\r
2323   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2324   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2325   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2326   str = _("We only care about the height here");\r
2327   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2328   SelectObject(hdc, oldFont);\r
2329   ReleaseDC(hwndMain, hdc);\r
2330 \r
2331   /* Compute where everything goes */\r
2332   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2333         /* [HGM] logo: if either logo is on, reserve space for it */\r
2334         logoHeight =  2*clockSize.cy;\r
2335         leftLogoRect.left   = OUTER_MARGIN;\r
2336         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2337         leftLogoRect.top    = OUTER_MARGIN;\r
2338         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2339 \r
2340         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2341         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2342         rightLogoRect.top    = OUTER_MARGIN;\r
2343         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2344 \r
2345 \r
2346     whiteRect.left = leftLogoRect.right;\r
2347     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2348     whiteRect.top = OUTER_MARGIN;\r
2349     whiteRect.bottom = whiteRect.top + logoHeight;\r
2350 \r
2351     blackRect.right = rightLogoRect.left;\r
2352     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2353     blackRect.top = whiteRect.top;\r
2354     blackRect.bottom = whiteRect.bottom;\r
2355   } else {\r
2356     whiteRect.left = OUTER_MARGIN;\r
2357     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2358     whiteRect.top = OUTER_MARGIN;\r
2359     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2360 \r
2361     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2362     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2363     blackRect.top = whiteRect.top;\r
2364     blackRect.bottom = whiteRect.bottom;\r
2365 \r
2366     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2367   }\r
2368 \r
2369   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2370   if (appData.showButtonBar) {\r
2371     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2372       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2373   } else {\r
2374     messageRect.right = OUTER_MARGIN + boardWidth;\r
2375   }\r
2376   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2377   messageRect.bottom = messageRect.top + messageSize.cy;\r
2378 \r
2379   boardRect.left = OUTER_MARGIN;\r
2380   boardRect.right = boardRect.left + boardWidth;\r
2381   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2382   boardRect.bottom = boardRect.top + boardHeight;\r
2383 \r
2384   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2385   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2386   oldTinyLayout = tinyLayout;\r
2387   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2388   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2389     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2390   winW *= 1 + twoBoards;\r
2391   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2392   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2393   wpMain.height = winH; //       without disturbing window attachments\r
2394   GetWindowRect(hwndMain, &wrect);\r
2395   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2396                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2397 \r
2398   // [HGM] placement: let attached windows follow size change.\r
2399   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2400   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2401   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2402   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2403   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2404 \r
2405   /* compensate if menu bar wrapped */\r
2406   GetClientRect(hwndMain, &crect);\r
2407   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2408   wpMain.height += offby;\r
2409   switch (flags) {\r
2410   case WMSZ_TOPLEFT:\r
2411     SetWindowPos(hwndMain, NULL, \r
2412                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2413                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2414     break;\r
2415 \r
2416   case WMSZ_TOPRIGHT:\r
2417   case WMSZ_TOP:\r
2418     SetWindowPos(hwndMain, NULL, \r
2419                  wrect.left, wrect.bottom - wpMain.height, \r
2420                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2421     break;\r
2422 \r
2423   case WMSZ_BOTTOMLEFT:\r
2424   case WMSZ_LEFT:\r
2425     SetWindowPos(hwndMain, NULL, \r
2426                  wrect.right - wpMain.width, wrect.top, \r
2427                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2428     break;\r
2429 \r
2430   case WMSZ_BOTTOMRIGHT:\r
2431   case WMSZ_BOTTOM:\r
2432   case WMSZ_RIGHT:\r
2433   default:\r
2434     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2435                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2436     break;\r
2437   }\r
2438 \r
2439   hwndPause = NULL;\r
2440   for (i = 0; i < N_BUTTONS; i++) {\r
2441     if (buttonDesc[i].hwnd != NULL) {\r
2442       DestroyWindow(buttonDesc[i].hwnd);\r
2443       buttonDesc[i].hwnd = NULL;\r
2444     }\r
2445     if (appData.showButtonBar) {\r
2446       buttonDesc[i].hwnd =\r
2447         CreateWindow("BUTTON", buttonDesc[i].label,\r
2448                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2449                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2450                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2451                      (HMENU) buttonDesc[i].id,\r
2452                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2453       if (tinyLayout) {\r
2454         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2455                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2456                     MAKELPARAM(FALSE, 0));\r
2457       }\r
2458       if (buttonDesc[i].id == IDM_Pause)\r
2459         hwndPause = buttonDesc[i].hwnd;\r
2460       buttonDesc[i].wndproc = (WNDPROC)\r
2461         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2462     }\r
2463   }\r
2464   if (gridPen != NULL) DeleteObject(gridPen);\r
2465   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2466   if (premovePen != NULL) DeleteObject(premovePen);\r
2467   if (lineGap != 0) {\r
2468     logbrush.lbStyle = BS_SOLID;\r
2469     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2470     gridPen =\r
2471       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2472                    lineGap, &logbrush, 0, NULL);\r
2473     logbrush.lbColor = highlightSquareColor;\r
2474     highlightPen =\r
2475       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2476                    lineGap, &logbrush, 0, NULL);\r
2477 \r
2478     logbrush.lbColor = premoveHighlightColor; \r
2479     premovePen =\r
2480       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2481                    lineGap, &logbrush, 0, NULL);\r
2482 \r
2483     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2484     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2485       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2486       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2487         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2488       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2489         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2490       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2491     }\r
2492     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2493       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2494       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2495         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2496         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2497       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2498         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2499       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2500     }\r
2501   }\r
2502 \r
2503   /* [HGM] Licensing requirement */\r
2504 #ifdef GOTHIC\r
2505   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2506 #endif\r
2507 #ifdef FALCON\r
2508   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2509 #endif\r
2510   GothicPopUp( "", VariantNormal);\r
2511 \r
2512 \r
2513 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2514 \r
2515   /* Load piece bitmaps for this board size */\r
2516   for (i=0; i<=2; i++) {\r
2517     for (piece = WhitePawn;\r
2518          (int) piece < (int) BlackPawn;\r
2519          piece = (ChessSquare) ((int) piece + 1)) {\r
2520       if (pieceBitmap[i][piece] != NULL)\r
2521         DeleteObject(pieceBitmap[i][piece]);\r
2522     }\r
2523   }\r
2524 \r
2525   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2526   // Orthodox Chess pieces\r
2527   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2528   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2529   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2530   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2531   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2532   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2533   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2534   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2535   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2536   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2537   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2538   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2539   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2540   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2541   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2542   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2543     // in Shogi, Hijack the unused Queen for Lance\r
2544     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2545     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2546     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2547   } else {\r
2548     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2549     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2550     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2551   }\r
2552 \r
2553   if(squareSize <= 72 && squareSize >= 33) { \r
2554     /* A & C are available in most sizes now */\r
2555     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2556       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2557       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2558       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2559       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2560       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2561       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2562       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2563       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2564       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2565       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2566       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2567       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2568     } else { // Smirf-like\r
2569       if(gameInfo.variant == VariantSChess) {\r
2570         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2571         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2572         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2573       } else {\r
2574         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2575         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2576         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2577       }\r
2578     }\r
2579     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2580       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2581       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2582       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2583     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2584       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2585       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2586       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2587     } else { // WinBoard standard\r
2588       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2589       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2590       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2591     }\r
2592   }\r
2593 \r
2594 \r
2595   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2596     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2597     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2598     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2599     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2600     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2601     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2602     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2603     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2604     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2605     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2606     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2607     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2608     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2609     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2610     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2611     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2612     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2613     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2614     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2615     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2616     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2617     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2618     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2619     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2620     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2621     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2622     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2623     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2624     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2625     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2626 \r
2627     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2628       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2629       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2630       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2631       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2632       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2633       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2634       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2635       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2636       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2637       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2638       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2639       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2640     } else {\r
2641       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2642       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2643       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2644       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2645       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2646       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2647       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2648       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2649       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2650       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2651       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2652       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2653     }\r
2654 \r
2655   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2656     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2657     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2658     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2659     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2660     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2661     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2662     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2663     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2664     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2665     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2666     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2667     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2668     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2669     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2670   }\r
2671 \r
2672 \r
2673   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2674   /* special Shogi support in this size */\r
2675   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2676       for (piece = WhitePawn;\r
2677            (int) piece < (int) BlackPawn;\r
2678            piece = (ChessSquare) ((int) piece + 1)) {\r
2679         if (pieceBitmap[i][piece] != NULL)\r
2680           DeleteObject(pieceBitmap[i][piece]);\r
2681       }\r
2682     }\r
2683   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2684   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2685   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2686   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2687   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2688   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2689   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2690   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2691   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2692   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2693   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2694   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2695   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2696   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2697   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2698   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2699   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2700   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2701   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2702   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2703   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2704   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2705   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2706   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2707   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2708   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2709   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2710   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2711   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2712   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2713   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2714   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2715   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2716   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2717   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2718   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2719   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2720   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2721   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2722   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2723   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2724   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2725   minorSize = 0;\r
2726   }\r
2727 }\r
2728 \r
2729 HBITMAP\r
2730 PieceBitmap(ChessSquare p, int kind)\r
2731 {\r
2732   if ((int) p >= (int) BlackPawn)\r
2733     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2734 \r
2735   return pieceBitmap[kind][(int) p];\r
2736 }\r
2737 \r
2738 /***************************************************************/\r
2739 \r
2740 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2741 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2742 /*\r
2743 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2744 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2745 */\r
2746 \r
2747 VOID\r
2748 SquareToPos(int row, int column, int * x, int * y)\r
2749 {\r
2750   if (flipView) {\r
2751     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2752     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2753   } else {\r
2754     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2755     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2756   }\r
2757 }\r
2758 \r
2759 VOID\r
2760 DrawCoordsOnDC(HDC hdc)\r
2761 {\r
2762   static char files[] = "0123456789012345678901221098765432109876543210";\r
2763   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2764   char str[2] = { NULLCHAR, NULLCHAR };\r
2765   int oldMode, oldAlign, x, y, start, i;\r
2766   HFONT oldFont;\r
2767   HBRUSH oldBrush;\r
2768 \r
2769   if (!appData.showCoords)\r
2770     return;\r
2771 \r
2772   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2773 \r
2774   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2775   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2776   oldAlign = GetTextAlign(hdc);\r
2777   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2778 \r
2779   y = boardRect.top + lineGap;\r
2780   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2781 \r
2782   if(border) {\r
2783     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2784     x += border - lineGap - 4; y += squareSize - 6;\r
2785   } else\r
2786   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2787   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2788     str[0] = files[start + i];\r
2789     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2790     y += squareSize + lineGap;\r
2791   }\r
2792 \r
2793   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2794 \r
2795   if(border) {\r
2796     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2797     x += -border + 4; y += border - squareSize + 6;\r
2798   } else\r
2799   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2800   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2801     str[0] = ranks[start + i];\r
2802     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2803     x += squareSize + lineGap;\r
2804   }    \r
2805 \r
2806   SelectObject(hdc, oldBrush);\r
2807   SetBkMode(hdc, oldMode);\r
2808   SetTextAlign(hdc, oldAlign);\r
2809   SelectObject(hdc, oldFont);\r
2810 }\r
2811 \r
2812 VOID\r
2813 DrawGridOnDC(HDC hdc)\r
2814 {\r
2815   HPEN oldPen;\r
2816  \r
2817   if (lineGap != 0) {\r
2818     oldPen = SelectObject(hdc, gridPen);\r
2819     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2820     SelectObject(hdc, oldPen);\r
2821   }\r
2822 }\r
2823 \r
2824 #define HIGHLIGHT_PEN 0\r
2825 #define PREMOVE_PEN   1\r
2826 \r
2827 VOID\r
2828 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2829 {\r
2830   int x1, y1;\r
2831   HPEN oldPen, hPen;\r
2832   if (lineGap == 0) return;\r
2833   if (flipView) {\r
2834     x1 = boardRect.left +\r
2835       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2836     y1 = boardRect.top +\r
2837       lineGap/2 + y * (squareSize + lineGap) + border;\r
2838   } else {\r
2839     x1 = boardRect.left +\r
2840       lineGap/2 + x * (squareSize + lineGap) + border;\r
2841     y1 = boardRect.top +\r
2842       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2843   }\r
2844   hPen = pen ? premovePen : highlightPen;\r
2845   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2846   MoveToEx(hdc, x1, y1, NULL);\r
2847   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2848   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2849   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2850   LineTo(hdc, x1, y1);\r
2851   SelectObject(hdc, oldPen);\r
2852 }\r
2853 \r
2854 VOID\r
2855 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2856 {\r
2857   int i;\r
2858   for (i=0; i<2; i++) {\r
2859     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2860       DrawHighlightOnDC(hdc, TRUE,\r
2861                         h->sq[i].x, h->sq[i].y,\r
2862                         pen);\r
2863   }\r
2864 }\r
2865 \r
2866 /* Note: sqcolor is used only in monoMode */\r
2867 /* Note that this code is largely duplicated in woptions.c,\r
2868    function DrawSampleSquare, so that needs to be updated too */\r
2869 VOID\r
2870 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2871 {\r
2872   HBITMAP oldBitmap;\r
2873   HBRUSH oldBrush;\r
2874   int tmpSize;\r
2875 \r
2876   if (appData.blindfold) return;\r
2877 \r
2878   /* [AS] Use font-based pieces if needed */\r
2879   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2880     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2881     CreatePiecesFromFont();\r
2882 \r
2883     if( fontBitmapSquareSize == squareSize ) {\r
2884         int index = TranslatePieceToFontPiece(piece);\r
2885 \r
2886         SelectObject( tmphdc, hPieceMask[ index ] );\r
2887 \r
2888       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2889         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2890       else\r
2891         BitBlt( hdc,\r
2892             x, y,\r
2893             squareSize, squareSize,\r
2894             tmphdc,\r
2895             0, 0,\r
2896             SRCAND );\r
2897 \r
2898         SelectObject( tmphdc, hPieceFace[ index ] );\r
2899 \r
2900       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2901         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2902       else\r
2903         BitBlt( hdc,\r
2904             x, y,\r
2905             squareSize, squareSize,\r
2906             tmphdc,\r
2907             0, 0,\r
2908             SRCPAINT );\r
2909 \r
2910         return;\r
2911     }\r
2912   }\r
2913 \r
2914   if (appData.monoMode) {\r
2915     SelectObject(tmphdc, PieceBitmap(piece, \r
2916       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2917     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2918            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2919   } else {\r
2920     HBRUSH xBrush = whitePieceBrush;\r
2921     tmpSize = squareSize;\r
2922     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2923     if(minorSize &&\r
2924         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2925          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2926       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2927       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2928       x += (squareSize - minorSize)>>1;\r
2929       y += squareSize - minorSize - 2;\r
2930       tmpSize = minorSize;\r
2931     }\r
2932     if (color || appData.allWhite ) {\r
2933       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2934       if( color )\r
2935               oldBrush = SelectObject(hdc, xBrush);\r
2936       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2937       if(appData.upsideDown && color==flipView)\r
2938         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2939       else\r
2940         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2941       /* Use black for outline of white pieces */\r
2942       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2943       if(appData.upsideDown && color==flipView)\r
2944         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2945       else\r
2946         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2947     } else if(appData.pieceDirectory[0]) {\r
2948       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2949       oldBrush = SelectObject(hdc, xBrush);\r
2950       if(appData.upsideDown && color==flipView)\r
2951         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2952       else\r
2953         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2954       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2955       if(appData.upsideDown && color==flipView)\r
2956         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2957       else\r
2958         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2959     } else {\r
2960       /* Use square color for details of black pieces */\r
2961       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2962       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2963       if(appData.upsideDown && !flipView)\r
2964         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2965       else\r
2966         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2967     }\r
2968     SelectObject(hdc, oldBrush);\r
2969     SelectObject(tmphdc, oldBitmap);\r
2970   }\r
2971 }\r
2972 \r
2973 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2974 int GetBackTextureMode( int algo )\r
2975 {\r
2976     int result = BACK_TEXTURE_MODE_DISABLED;\r
2977 \r
2978     switch( algo ) \r
2979     {\r
2980         case BACK_TEXTURE_MODE_PLAIN:\r
2981             result = 1; /* Always use identity map */\r
2982             break;\r
2983         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2984             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2985             break;\r
2986     }\r
2987 \r
2988     return result;\r
2989 }\r
2990 \r
2991 /* \r
2992     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2993     to handle redraws cleanly (as random numbers would always be different).\r
2994 */\r
2995 VOID RebuildTextureSquareInfo()\r
2996 {\r
2997     BITMAP bi;\r
2998     int lite_w = 0;\r
2999     int lite_h = 0;\r
3000     int dark_w = 0;\r
3001     int dark_h = 0;\r
3002     int row;\r
3003     int col;\r
3004 \r
3005     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3006 \r
3007     if( liteBackTexture != NULL ) {\r
3008         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3009             lite_w = bi.bmWidth;\r
3010             lite_h = bi.bmHeight;\r
3011         }\r
3012     }\r
3013 \r
3014     if( darkBackTexture != NULL ) {\r
3015         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3016             dark_w = bi.bmWidth;\r
3017             dark_h = bi.bmHeight;\r
3018         }\r
3019     }\r
3020 \r
3021     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3022         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3023             if( (col + row) & 1 ) {\r
3024                 /* Lite square */\r
3025                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3026                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3027                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3028                   else\r
3029                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3030                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3031                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3032                   else\r
3033                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3034                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3035                 }\r
3036             }\r
3037             else {\r
3038                 /* Dark square */\r
3039                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3040                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3041                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3042                   else\r
3043                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3044                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3045                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3046                   else\r
3047                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3048                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3049                 }\r
3050             }\r
3051         }\r
3052     }\r
3053 }\r
3054 \r
3055 /* [AS] Arrow highlighting support */\r
3056 \r
3057 static double A_WIDTH = 5; /* Width of arrow body */\r
3058 \r
3059 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3060 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3061 \r
3062 static double Sqr( double x )\r
3063 {\r
3064     return x*x;\r
3065 }\r
3066 \r
3067 static int Round( double x )\r
3068 {\r
3069     return (int) (x + 0.5);\r
3070 }\r
3071 \r
3072 /* Draw an arrow between two points using current settings */\r
3073 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3074 {\r
3075     POINT arrow[7];\r
3076     double dx, dy, j, k, x, y;\r
3077 \r
3078     if( d_x == s_x ) {\r
3079         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3080 \r
3081         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3082         arrow[0].y = s_y;\r
3083 \r
3084         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3085         arrow[1].y = d_y - h;\r
3086 \r
3087         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3088         arrow[2].y = d_y - h;\r
3089 \r
3090         arrow[3].x = d_x;\r
3091         arrow[3].y = d_y;\r
3092 \r
3093         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3094         arrow[5].y = d_y - h;\r
3095 \r
3096         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3097         arrow[4].y = d_y - h;\r
3098 \r
3099         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3100         arrow[6].y = s_y;\r
3101     }\r
3102     else if( d_y == s_y ) {\r
3103         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3104 \r
3105         arrow[0].x = s_x;\r
3106         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3107 \r
3108         arrow[1].x = d_x - w;\r
3109         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3110 \r
3111         arrow[2].x = d_x - w;\r
3112         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3113 \r
3114         arrow[3].x = d_x;\r
3115         arrow[3].y = d_y;\r
3116 \r
3117         arrow[5].x = d_x - w;\r
3118         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3119 \r
3120         arrow[4].x = d_x - w;\r
3121         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3122 \r
3123         arrow[6].x = s_x;\r
3124         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3125     }\r
3126     else {\r
3127         /* [AS] Needed a lot of paper for this! :-) */\r
3128         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3129         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3130   \r
3131         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3132 \r
3133         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3134 \r
3135         x = s_x;\r
3136         y = s_y;\r
3137 \r
3138         arrow[0].x = Round(x - j);\r
3139         arrow[0].y = Round(y + j*dx);\r
3140 \r
3141         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3142         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3143 \r
3144         if( d_x > s_x ) {\r
3145             x = (double) d_x - k;\r
3146             y = (double) d_y - k*dy;\r
3147         }\r
3148         else {\r
3149             x = (double) d_x + k;\r
3150             y = (double) d_y + k*dy;\r
3151         }\r
3152 \r
3153         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3154 \r
3155         arrow[6].x = Round(x - j);\r
3156         arrow[6].y = Round(y + j*dx);\r
3157 \r
3158         arrow[2].x = Round(arrow[6].x + 2*j);\r
3159         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3160 \r
3161         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3162         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3163 \r
3164         arrow[4].x = d_x;\r
3165         arrow[4].y = d_y;\r
3166 \r
3167         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3168         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3169     }\r
3170 \r
3171     Polygon( hdc, arrow, 7 );\r
3172 }\r
3173 \r
3174 /* [AS] Draw an arrow between two squares */\r
3175 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3176 {\r
3177     int s_x, s_y, d_x, d_y;\r
3178     HPEN hpen;\r
3179     HPEN holdpen;\r
3180     HBRUSH hbrush;\r
3181     HBRUSH holdbrush;\r
3182     LOGBRUSH stLB;\r
3183 \r
3184     if( s_col == d_col && s_row == d_row ) {\r
3185         return;\r
3186     }\r
3187 \r
3188     /* Get source and destination points */\r
3189     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3190     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3191 \r
3192     if( d_y > s_y ) {\r
3193         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3194     }\r
3195     else if( d_y < s_y ) {\r
3196         d_y += squareSize / 2 + squareSize / 4;\r
3197     }\r
3198     else {\r
3199         d_y += squareSize / 2;\r
3200     }\r
3201 \r
3202     if( d_x > s_x ) {\r
3203         d_x += squareSize / 2 - squareSize / 4;\r
3204     }\r
3205     else if( d_x < s_x ) {\r
3206         d_x += squareSize / 2 + squareSize / 4;\r
3207     }\r
3208     else {\r
3209         d_x += squareSize / 2;\r
3210     }\r
3211 \r
3212     s_x += squareSize / 2;\r
3213     s_y += squareSize / 2;\r
3214 \r
3215     /* Adjust width */\r
3216     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3217 \r
3218     /* Draw */\r
3219     stLB.lbStyle = BS_SOLID;\r
3220     stLB.lbColor = appData.highlightArrowColor;\r
3221     stLB.lbHatch = 0;\r
3222 \r
3223     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3224     holdpen = SelectObject( hdc, hpen );\r
3225     hbrush = CreateBrushIndirect( &stLB );\r
3226     holdbrush = SelectObject( hdc, hbrush );\r
3227 \r
3228     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3229 \r
3230     SelectObject( hdc, holdpen );\r
3231     SelectObject( hdc, holdbrush );\r
3232     DeleteObject( hpen );\r
3233     DeleteObject( hbrush );\r
3234 }\r
3235 \r
3236 BOOL HasHighlightInfo()\r
3237 {\r
3238     BOOL result = FALSE;\r
3239 \r
3240     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3241         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3242     {\r
3243         result = TRUE;\r
3244     }\r
3245 \r
3246     return result;\r
3247 }\r
3248 \r
3249 BOOL IsDrawArrowEnabled()\r
3250 {\r
3251     BOOL result = FALSE;\r
3252 \r
3253     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3254         result = TRUE;\r
3255     }\r
3256 \r
3257     return result;\r
3258 }\r
3259 \r
3260 VOID DrawArrowHighlight( HDC hdc )\r
3261 {\r
3262     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3263         DrawArrowBetweenSquares( hdc,\r
3264             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3265             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3266     }\r
3267 }\r
3268 \r
3269 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3270 {\r
3271     HRGN result = NULL;\r
3272 \r
3273     if( HasHighlightInfo() ) {\r
3274         int x1, y1, x2, y2;\r
3275         int sx, sy, dx, dy;\r
3276 \r
3277         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3278         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3279 \r
3280         sx = MIN( x1, x2 );\r
3281         sy = MIN( y1, y2 );\r
3282         dx = MAX( x1, x2 ) + squareSize;\r
3283         dy = MAX( y1, y2 ) + squareSize;\r
3284 \r
3285         result = CreateRectRgn( sx, sy, dx, dy );\r
3286     }\r
3287 \r
3288     return result;\r
3289 }\r
3290 \r
3291 /*\r
3292     Warning: this function modifies the behavior of several other functions. \r
3293     \r
3294     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3295     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3296     repaint is scattered all over the place, which is not good for features such as\r
3297     "arrow highlighting" that require a full repaint of the board.\r
3298 \r
3299     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3300     user interaction, when speed is not so important) but especially to avoid errors\r
3301     in the displayed graphics.\r
3302 \r
3303     In such patched places, I always try refer to this function so there is a single\r
3304     place to maintain knowledge.\r
3305     \r
3306     To restore the original behavior, just return FALSE unconditionally.\r
3307 */\r
3308 BOOL IsFullRepaintPreferrable()\r
3309 {\r
3310     BOOL result = FALSE;\r
3311 \r
3312     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3313         /* Arrow may appear on the board */\r
3314         result = TRUE;\r
3315     }\r
3316 \r
3317     return result;\r
3318 }\r
3319 \r
3320 /* \r
3321     This function is called by DrawPosition to know whether a full repaint must\r
3322     be forced or not.\r
3323 \r
3324     Only DrawPosition may directly call this function, which makes use of \r
3325     some state information. Other function should call DrawPosition specifying \r
3326     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3327 */\r
3328 BOOL DrawPositionNeedsFullRepaint()\r
3329 {\r
3330     BOOL result = FALSE;\r
3331 \r
3332     /* \r
3333         Probably a slightly better policy would be to trigger a full repaint\r
3334         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3335         but animation is fast enough that it's difficult to notice.\r
3336     */\r
3337     if( animInfo.piece == EmptySquare ) {\r
3338         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3339             result = TRUE;\r
3340         }\r
3341     }\r
3342 \r
3343     return result;\r
3344 }\r
3345 \r
3346 static HBITMAP borderBitmap;\r
3347 \r
3348 VOID\r
3349 DrawBackgroundOnDC(HDC hdc)\r
3350 {\r
3351   \r
3352   BITMAP bi;\r
3353   HDC tmphdc;\r
3354   HBITMAP hbm;\r
3355   static char oldBorder[MSG_SIZ];\r
3356   int w = 600, h = 600, mode;\r
3357 \r
3358   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3359     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3360     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3361   }\r
3362   if(borderBitmap == NULL) { // loading failed, use white\r
3363     FillRect( hdc, &boardRect, whitePieceBrush );\r
3364     return;\r
3365   }\r
3366   tmphdc = CreateCompatibleDC(hdc);\r
3367   hbm = SelectObject(tmphdc, borderBitmap);\r
3368   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3369             w = bi.bmWidth;\r
3370             h = bi.bmHeight;\r
3371   }\r
3372   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3373   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3374                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3375   SetStretchBltMode(hdc, mode);\r
3376   SelectObject(tmphdc, hbm);\r
3377   DeleteDC(tmphdc);\r
3378 }\r
3379 \r
3380 VOID\r
3381 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3382 {\r
3383   int row, column, x, y, square_color, piece_color;\r
3384   ChessSquare piece;\r
3385   HBRUSH oldBrush;\r
3386   HDC texture_hdc = NULL;\r
3387 \r
3388   /* [AS] Initialize background textures if needed */\r
3389   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3390       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3391       if( backTextureSquareSize != squareSize \r
3392        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3393           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3394           backTextureSquareSize = squareSize;\r
3395           RebuildTextureSquareInfo();\r
3396       }\r
3397 \r
3398       texture_hdc = CreateCompatibleDC( hdc );\r
3399   }\r
3400 \r
3401   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3402     for (column = 0; column < BOARD_WIDTH; column++) {\r
3403   \r
3404       SquareToPos(row, column, &x, &y);\r
3405 \r
3406       piece = board[row][column];\r
3407 \r
3408       square_color = ((column + row) % 2) == 1;\r
3409       if( gameInfo.variant == VariantXiangqi ) {\r
3410           square_color = !InPalace(row, column);\r
3411           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3412           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3413       }\r
3414       piece_color = (int) piece < (int) BlackPawn;\r
3415 \r
3416 \r
3417       /* [HGM] holdings file: light square or black */\r
3418       if(column == BOARD_LEFT-2) {\r
3419             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3420                 square_color = 1;\r
3421             else {\r
3422                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3423                 continue;\r
3424             }\r
3425       } else\r
3426       if(column == BOARD_RGHT + 1 ) {\r
3427             if( row < gameInfo.holdingsSize )\r
3428                 square_color = 1;\r
3429             else {\r
3430                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3431                 continue;\r
3432             }\r
3433       }\r
3434       if(column == BOARD_LEFT-1 ) /* left align */\r
3435             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3436       else if( column == BOARD_RGHT) /* right align */\r
3437             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3438       else\r
3439       if (appData.monoMode) {\r
3440         if (piece == EmptySquare) {\r
3441           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3442                  square_color ? WHITENESS : BLACKNESS);\r
3443         } else {\r
3444           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3445         }\r
3446       } \r
3447       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3448           /* [AS] Draw the square using a texture bitmap */\r
3449           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3450           int r = row, c = column; // [HGM] do not flip board in flipView\r
3451           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3452 \r
3453           DrawTile( x, y, \r
3454               squareSize, squareSize, \r
3455               hdc, \r
3456               texture_hdc,\r
3457               backTextureSquareInfo[r][c].mode,\r
3458               backTextureSquareInfo[r][c].x,\r
3459               backTextureSquareInfo[r][c].y );\r
3460 \r
3461           SelectObject( texture_hdc, hbm );\r
3462 \r
3463           if (piece != EmptySquare) {\r
3464               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3465           }\r
3466       }\r
3467       else {\r
3468         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3469 \r
3470         oldBrush = SelectObject(hdc, brush );\r
3471         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3472         SelectObject(hdc, oldBrush);\r
3473         if (piece != EmptySquare)\r
3474           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3475       }\r
3476     }\r
3477   }\r
3478 \r
3479   if( texture_hdc != NULL ) {\r
3480     DeleteDC( texture_hdc );\r
3481   }\r
3482 }\r
3483 \r
3484 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3485 void fputDW(FILE *f, int x)\r
3486 {\r
3487         fputc(x     & 255, f);\r
3488         fputc(x>>8  & 255, f);\r
3489         fputc(x>>16 & 255, f);\r
3490         fputc(x>>24 & 255, f);\r
3491 }\r
3492 \r
3493 #define MAX_CLIPS 200   /* more than enough */\r
3494 \r
3495 VOID\r
3496 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3497 {\r
3498 //  HBITMAP bufferBitmap;\r
3499   BITMAP bi;\r
3500 //  RECT Rect;\r
3501   HDC tmphdc;\r
3502   HBITMAP hbm;\r
3503   int w = 100, h = 50;\r
3504 \r
3505   if(logo == NULL) {\r
3506     if(!logoHeight) return;\r
3507     FillRect( hdc, &logoRect, whitePieceBrush );\r
3508   }\r
3509 //  GetClientRect(hwndMain, &Rect);\r
3510 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3511 //                                      Rect.bottom-Rect.top+1);\r
3512   tmphdc = CreateCompatibleDC(hdc);\r
3513   hbm = SelectObject(tmphdc, logo);\r
3514   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3515             w = bi.bmWidth;\r
3516             h = bi.bmHeight;\r
3517   }\r
3518   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3519                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3520   SelectObject(tmphdc, hbm);\r
3521   DeleteDC(tmphdc);\r
3522 }\r
3523 \r
3524 VOID\r
3525 DisplayLogos()\r
3526 {\r
3527   if(logoHeight) {\r
3528         HDC hdc = GetDC(hwndMain);\r
3529         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3530         if(appData.autoLogo) {\r
3531           \r
3532           switch(gameMode) { // pick logos based on game mode\r
3533             case IcsObserving:\r
3534                 whiteLogo = second.programLogo; // ICS logo\r
3535                 blackLogo = second.programLogo;\r
3536             default:\r
3537                 break;\r
3538             case IcsPlayingWhite:\r
3539                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3540                 blackLogo = second.programLogo; // ICS logo\r
3541                 break;\r
3542             case IcsPlayingBlack:\r
3543                 whiteLogo = second.programLogo; // ICS logo\r
3544                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3545                 break;\r
3546             case TwoMachinesPlay:\r
3547                 if(first.twoMachinesColor[0] == 'b') {\r
3548                     whiteLogo = second.programLogo;\r
3549                     blackLogo = first.programLogo;\r
3550                 }\r
3551                 break;\r
3552             case MachinePlaysWhite:\r
3553                 blackLogo = userLogo;\r
3554                 break;\r
3555             case MachinePlaysBlack:\r
3556                 whiteLogo = userLogo;\r
3557                 blackLogo = first.programLogo;\r
3558           }\r
3559         }\r
3560         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3561         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3562         ReleaseDC(hwndMain, hdc);\r
3563   }\r
3564 }\r
3565 \r
3566 void\r
3567 UpdateLogos(int display)\r
3568 { // called after loading new engine(s), in tourney or from menu\r
3569   LoadLogo(&first, 0, FALSE);\r
3570   LoadLogo(&second, 1, appData.icsActive);\r
3571   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3572   if(display) DisplayLogos();\r
3573 }\r
3574 \r
3575 static HDC hdcSeek;\r
3576 \r
3577 // [HGM] seekgraph\r
3578 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3579 {\r
3580     POINT stPt;\r
3581     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3582     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3583     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3584     SelectObject( hdcSeek, hp );\r
3585 }\r
3586 \r
3587 // front-end wrapper for drawing functions to do rectangles\r
3588 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3589 {\r
3590     HPEN hp;\r
3591     RECT rc;\r
3592 \r
3593     if (hdcSeek == NULL) {\r
3594     hdcSeek = GetDC(hwndMain);\r
3595       if (!appData.monoMode) {\r
3596         SelectPalette(hdcSeek, hPal, FALSE);\r
3597         RealizePalette(hdcSeek);\r
3598       }\r
3599     }\r
3600     hp = SelectObject( hdcSeek, gridPen );\r
3601     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3602     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3603     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3604     SelectObject( hdcSeek, hp );\r
3605 }\r
3606 \r
3607 // front-end wrapper for putting text in graph\r
3608 void DrawSeekText(char *buf, int x, int y)\r
3609 {\r
3610         SIZE stSize;\r
3611         SetBkMode( hdcSeek, TRANSPARENT );\r
3612         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3613         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3614 }\r
3615 \r
3616 void DrawSeekDot(int x, int y, int color)\r
3617 {\r
3618         int square = color & 0x80;\r
3619         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3620                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3621         color &= 0x7F;\r
3622         if(square)\r
3623             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3624                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3625         else\r
3626             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3627                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3628             SelectObject(hdcSeek, oldBrush);\r
3629 }\r
3630 \r
3631 void DrawSeekOpen()\r
3632 {\r
3633 }\r
3634 \r
3635 void DrawSeekClose()\r
3636 {\r
3637 }\r
3638 \r
3639 VOID\r
3640 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3641 {\r
3642   static Board lastReq[2], lastDrawn[2];\r
3643   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3644   static int lastDrawnFlipView = 0;\r
3645   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3646   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3647   HDC tmphdc;\r
3648   HDC hdcmem;\r
3649   HBITMAP bufferBitmap;\r
3650   HBITMAP oldBitmap;\r
3651   RECT Rect;\r
3652   HRGN clips[MAX_CLIPS];\r
3653   ChessSquare dragged_piece = EmptySquare;\r
3654   int nr = twoBoards*partnerUp;\r
3655 \r
3656   /* I'm undecided on this - this function figures out whether a full\r
3657    * repaint is necessary on its own, so there's no real reason to have the\r
3658    * caller tell it that.  I think this can safely be set to FALSE - but\r
3659    * if we trust the callers not to request full repaints unnessesarily, then\r
3660    * we could skip some clipping work.  In other words, only request a full\r
3661    * redraw when the majority of pieces have changed positions (ie. flip, \r
3662    * gamestart and similar)  --Hawk\r
3663    */\r
3664   Boolean fullrepaint = repaint;\r
3665 \r
3666   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3667 \r
3668   if( DrawPositionNeedsFullRepaint() ) {\r
3669       fullrepaint = TRUE;\r
3670   }\r
3671 \r
3672   if (board == NULL) {\r
3673     if (!lastReqValid[nr]) {\r
3674       return;\r
3675     }\r
3676     board = lastReq[nr];\r
3677   } else {\r
3678     CopyBoard(lastReq[nr], board);\r
3679     lastReqValid[nr] = 1;\r
3680   }\r
3681 \r
3682   if (doingSizing) {\r
3683     return;\r
3684   }\r
3685 \r
3686   if (IsIconic(hwndMain)) {\r
3687     return;\r
3688   }\r
3689 \r
3690   if (hdc == NULL) {\r
3691     hdc = GetDC(hwndMain);\r
3692     if (!appData.monoMode) {\r
3693       SelectPalette(hdc, hPal, FALSE);\r
3694       RealizePalette(hdc);\r
3695     }\r
3696     releaseDC = TRUE;\r
3697   } else {\r
3698     releaseDC = FALSE;\r
3699   }\r
3700 \r
3701   /* Create some work-DCs */\r
3702   hdcmem = CreateCompatibleDC(hdc);\r
3703   tmphdc = CreateCompatibleDC(hdc);\r
3704 \r
3705   /* If dragging is in progress, we temporarely remove the piece */\r
3706   /* [HGM] or temporarily decrease count if stacked              */\r
3707   /*       !! Moved to before board compare !!                   */\r
3708   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3709     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3710     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3711             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3712         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3713     } else \r
3714     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3715             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3716         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3717     } else \r
3718         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3719   }\r
3720 \r
3721   /* Figure out which squares need updating by comparing the \r
3722    * newest board with the last drawn board and checking if\r
3723    * flipping has changed.\r
3724    */\r
3725   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3726     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3727       for (column = 0; column < BOARD_WIDTH; column++) {\r
3728         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3729           SquareToPos(row, column, &x, &y);\r
3730           clips[num_clips++] =\r
3731             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3732         }\r
3733       }\r
3734     }\r
3735    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3736     for (i=0; i<2; i++) {\r
3737       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3738           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3739         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3740             lastDrawnHighlight.sq[i].y >= 0) {\r
3741           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3742                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3743           clips[num_clips++] =\r
3744             CreateRectRgn(x - lineGap, y - lineGap, \r
3745                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3746         }\r
3747         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3748           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3749           clips[num_clips++] =\r
3750             CreateRectRgn(x - lineGap, y - lineGap, \r
3751                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3752         }\r
3753       }\r
3754     }\r
3755     for (i=0; i<2; i++) {\r
3756       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3757           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3758         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3759             lastDrawnPremove.sq[i].y >= 0) {\r
3760           SquareToPos(lastDrawnPremove.sq[i].y,\r
3761                       lastDrawnPremove.sq[i].x, &x, &y);\r
3762           clips[num_clips++] =\r
3763             CreateRectRgn(x - lineGap, y - lineGap, \r
3764                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3765         }\r
3766         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3767             premoveHighlightInfo.sq[i].y >= 0) {\r
3768           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3769                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3770           clips[num_clips++] =\r
3771             CreateRectRgn(x - lineGap, y - lineGap, \r
3772                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3773         }\r
3774       }\r
3775     }\r
3776    } else { // nr == 1\r
3777         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3778         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3779         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3780         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3781       for (i=0; i<2; i++) {\r
3782         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3783             partnerHighlightInfo.sq[i].y >= 0) {\r
3784           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3785                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3786           clips[num_clips++] =\r
3787             CreateRectRgn(x - lineGap, y - lineGap, \r
3788                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3789         }\r
3790         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3791             oldPartnerHighlight.sq[i].y >= 0) {\r
3792           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3793                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3794           clips[num_clips++] =\r
3795             CreateRectRgn(x - lineGap, y - lineGap, \r
3796                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3797         }\r
3798       }\r
3799    }\r
3800   } else {\r
3801     fullrepaint = TRUE;\r
3802   }\r
3803 \r
3804   /* Create a buffer bitmap - this is the actual bitmap\r
3805    * being written to.  When all the work is done, we can\r
3806    * copy it to the real DC (the screen).  This avoids\r
3807    * the problems with flickering.\r
3808    */\r
3809   GetClientRect(hwndMain, &Rect);\r
3810   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3811                                         Rect.bottom-Rect.top+1);\r
3812   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3813   if (!appData.monoMode) {\r
3814     SelectPalette(hdcmem, hPal, FALSE);\r
3815   }\r
3816 \r
3817   /* Create clips for dragging */\r
3818   if (!fullrepaint) {\r
3819     if (dragInfo.from.x >= 0) {\r
3820       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3821       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3822     }\r
3823     if (dragInfo.start.x >= 0) {\r
3824       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3825       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3826     }\r
3827     if (dragInfo.pos.x >= 0) {\r
3828       x = dragInfo.pos.x - squareSize / 2;\r
3829       y = dragInfo.pos.y - squareSize / 2;\r
3830       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3831     }\r
3832     if (dragInfo.lastpos.x >= 0) {\r
3833       x = dragInfo.lastpos.x - squareSize / 2;\r
3834       y = dragInfo.lastpos.y - squareSize / 2;\r
3835       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3836     }\r
3837   }\r
3838 \r
3839   /* Are we animating a move?  \r
3840    * If so, \r
3841    *   - remove the piece from the board (temporarely)\r
3842    *   - calculate the clipping region\r
3843    */\r
3844   if (!fullrepaint) {\r
3845     if (animInfo.piece != EmptySquare) {\r
3846       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3847       x = boardRect.left + animInfo.lastpos.x;\r
3848       y = boardRect.top + animInfo.lastpos.y;\r
3849       x2 = boardRect.left + animInfo.pos.x;\r
3850       y2 = boardRect.top + animInfo.pos.y;\r
3851       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3852       /* Slight kludge.  The real problem is that after AnimateMove is\r
3853          done, the position on the screen does not match lastDrawn.\r
3854          This currently causes trouble only on e.p. captures in\r
3855          atomic, where the piece moves to an empty square and then\r
3856          explodes.  The old and new positions both had an empty square\r
3857          at the destination, but animation has drawn a piece there and\r
3858          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3859       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3860     }\r
3861   }\r
3862 \r
3863   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3864   if (num_clips == 0)\r
3865     fullrepaint = TRUE;\r
3866 \r
3867   /* Set clipping on the memory DC */\r
3868   if (!fullrepaint) {\r
3869     SelectClipRgn(hdcmem, clips[0]);\r
3870     for (x = 1; x < num_clips; x++) {\r
3871       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3872         abort();  // this should never ever happen!\r
3873     }\r
3874   }\r
3875 \r
3876   /* Do all the drawing to the memory DC */\r
3877   if(explodeInfo.radius) { // [HGM] atomic\r
3878         HBRUSH oldBrush;\r
3879         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3880         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3881         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3882         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3883         x += squareSize/2;\r
3884         y += squareSize/2;\r
3885         if(!fullrepaint) {\r
3886           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3887           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3888         }\r
3889         DrawGridOnDC(hdcmem);\r
3890         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3891         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3892         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3893         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3894         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3895         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3896         SelectObject(hdcmem, oldBrush);\r
3897   } else {\r
3898     if(border) DrawBackgroundOnDC(hdcmem);\r
3899     DrawGridOnDC(hdcmem);\r
3900     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3901         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3902         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3903     } else {\r
3904         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3905         oldPartnerHighlight = partnerHighlightInfo;\r
3906     }\r
3907     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3908   }\r
3909   if(nr == 0) // [HGM] dual: markers only on left board\r
3910   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3911     for (column = 0; column < BOARD_WIDTH; column++) {\r
3912         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3913             HBRUSH oldBrush = SelectObject(hdcmem, \r
3914                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3915             SquareToPos(row, column, &x, &y);\r
3916             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3917                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3918             SelectObject(hdcmem, oldBrush);\r
3919         }\r
3920     }\r
3921   }\r
3922 \r
3923   if( appData.highlightMoveWithArrow ) {\r
3924     DrawArrowHighlight(hdcmem);\r
3925   }\r
3926 \r
3927   DrawCoordsOnDC(hdcmem);\r
3928 \r
3929   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3930                  /* to make sure lastDrawn contains what is actually drawn */\r
3931 \r
3932   /* Put the dragged piece back into place and draw it (out of place!) */\r
3933     if (dragged_piece != EmptySquare) {\r
3934     /* [HGM] or restack */\r
3935     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3936                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3937     else\r
3938     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3939                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3940     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3941     x = dragInfo.pos.x - squareSize / 2;\r
3942     y = dragInfo.pos.y - squareSize / 2;\r
3943     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3944                   ((int) dragInfo.piece < (int) BlackPawn), \r
3945                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3946   }   \r
3947   \r
3948   /* Put the animated piece back into place and draw it */\r
3949   if (animInfo.piece != EmptySquare) {\r
3950     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3951     x = boardRect.left + animInfo.pos.x;\r
3952     y = boardRect.top + animInfo.pos.y;\r
3953     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3954                   ((int) animInfo.piece < (int) BlackPawn),\r
3955                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3956   }\r
3957 \r
3958   /* Release the bufferBitmap by selecting in the old bitmap \r
3959    * and delete the memory DC\r
3960    */\r
3961   SelectObject(hdcmem, oldBitmap);\r
3962   DeleteDC(hdcmem);\r
3963 \r
3964   /* Set clipping on the target DC */\r
3965   if (!fullrepaint) {\r
3966     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3967         RECT rect;\r
3968         GetRgnBox(clips[x], &rect);\r
3969         DeleteObject(clips[x]);\r
3970         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3971                           rect.right + wpMain.width/2, rect.bottom);\r
3972     }\r
3973     SelectClipRgn(hdc, clips[0]);\r
3974     for (x = 1; x < num_clips; x++) {\r
3975       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3976         abort();   // this should never ever happen!\r
3977     } \r
3978   }\r
3979 \r
3980   /* Copy the new bitmap onto the screen in one go.\r
3981    * This way we avoid any flickering\r
3982    */\r
3983   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3984   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3985          boardRect.right - boardRect.left,\r
3986          boardRect.bottom - boardRect.top,\r
3987          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3988   if(saveDiagFlag) { \r
3989     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3990     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3991 \r
3992     GetObject(bufferBitmap, sizeof(b), &b);\r
3993     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3994         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3995         bih.biWidth = b.bmWidth;\r
3996         bih.biHeight = b.bmHeight;\r
3997         bih.biPlanes = 1;\r
3998         bih.biBitCount = b.bmBitsPixel;\r
3999         bih.biCompression = 0;\r
4000         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4001         bih.biXPelsPerMeter = 0;\r
4002         bih.biYPelsPerMeter = 0;\r
4003         bih.biClrUsed = 0;\r
4004         bih.biClrImportant = 0;\r
4005 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4006 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4007         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4008 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4009 \r
4010         wb = b.bmWidthBytes;\r
4011         // count colors\r
4012         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4013                 int k = ((int*) pData)[i];\r
4014                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4015                 if(j >= 16) break;\r
4016                 color[j] = k;\r
4017                 if(j >= nrColors) nrColors = j+1;\r
4018         }\r
4019         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4020                 INT p = 0;\r
4021                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4022                     for(w=0; w<(wb>>2); w+=2) {\r
4023                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4024                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4025                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4026                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4027                         pData[p++] = m | j<<4;\r
4028                     }\r
4029                     while(p&3) pData[p++] = 0;\r
4030                 }\r
4031                 fac = 3;\r
4032                 wb = ((wb+31)>>5)<<2;\r
4033         }\r
4034         // write BITMAPFILEHEADER\r
4035         fprintf(diagFile, "BM");\r
4036         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4037         fputDW(diagFile, 0);\r
4038         fputDW(diagFile, 0x36 + (fac?64:0));\r
4039         // write BITMAPINFOHEADER\r
4040         fputDW(diagFile, 40);\r
4041         fputDW(diagFile, b.bmWidth);\r
4042         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4043         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4044         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\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         fputDW(diagFile, 0);\r
4051         // write color table\r
4052         if(fac)\r
4053         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4054         // write bitmap data\r
4055         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4056                 fputc(pData[i], diagFile);\r
4057         free(pData);\r
4058      }\r
4059   }\r
4060 \r
4061   SelectObject(tmphdc, oldBitmap);\r
4062 \r
4063   /* Massive cleanup */\r
4064   for (x = 0; x < num_clips; x++)\r
4065     DeleteObject(clips[x]);\r
4066 \r
4067   DeleteDC(tmphdc);\r
4068   DeleteObject(bufferBitmap);\r
4069 \r
4070   if (releaseDC) \r
4071     ReleaseDC(hwndMain, hdc);\r
4072   \r
4073   if (lastDrawnFlipView != flipView && nr == 0) {\r
4074     if (flipView)\r
4075       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4076     else\r
4077       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4078   }\r
4079 \r
4080 /*  CopyBoard(lastDrawn, board);*/\r
4081   lastDrawnHighlight = highlightInfo;\r
4082   lastDrawnPremove   = premoveHighlightInfo;\r
4083   lastDrawnFlipView = flipView;\r
4084   lastDrawnValid[nr] = 1;\r
4085 }\r
4086 \r
4087 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4088 int\r
4089 SaveDiagram(f)\r
4090      FILE *f;\r
4091 {\r
4092     saveDiagFlag = 1; diagFile = f;\r
4093     HDCDrawPosition(NULL, TRUE, NULL);\r
4094     saveDiagFlag = 0;\r
4095 \r
4096     fclose(f);\r
4097     return TRUE;\r
4098 }\r
4099 \r
4100 \r
4101 /*---------------------------------------------------------------------------*\\r
4102 | CLIENT PAINT PROCEDURE\r
4103 |   This is the main event-handler for the WM_PAINT message.\r
4104 |\r
4105 \*---------------------------------------------------------------------------*/\r
4106 VOID\r
4107 PaintProc(HWND hwnd)\r
4108 {\r
4109   HDC         hdc;\r
4110   PAINTSTRUCT ps;\r
4111   HFONT       oldFont;\r
4112 \r
4113   if((hdc = BeginPaint(hwnd, &ps))) {\r
4114     if (IsIconic(hwnd)) {\r
4115       DrawIcon(hdc, 2, 2, iconCurrent);\r
4116     } else {\r
4117       if (!appData.monoMode) {\r
4118         SelectPalette(hdc, hPal, FALSE);\r
4119         RealizePalette(hdc);\r
4120       }\r
4121       HDCDrawPosition(hdc, 1, NULL);\r
4122       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4123         flipView = !flipView; partnerUp = !partnerUp;\r
4124         HDCDrawPosition(hdc, 1, NULL);\r
4125         flipView = !flipView; partnerUp = !partnerUp;\r
4126       }\r
4127       oldFont =\r
4128         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4129       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4130                  ETO_CLIPPED|ETO_OPAQUE,\r
4131                  &messageRect, messageText, strlen(messageText), NULL);\r
4132       SelectObject(hdc, oldFont);\r
4133       DisplayBothClocks();\r
4134       DisplayLogos();\r
4135     }\r
4136     EndPaint(hwnd,&ps);\r
4137   }\r
4138 \r
4139   return;\r
4140 }\r
4141 \r
4142 \r
4143 /*\r
4144  * If the user selects on a border boundary, return -1; if off the board,\r
4145  *   return -2.  Otherwise map the event coordinate to the square.\r
4146  * The offset boardRect.left or boardRect.top must already have been\r
4147  *   subtracted from x.\r
4148  */\r
4149 int EventToSquare(x, limit)\r
4150      int x, limit;\r
4151 {\r
4152   if (x <= border)\r
4153     return -2;\r
4154   if (x < lineGap + border)\r
4155     return -1;\r
4156   x -= lineGap + border;\r
4157   if ((x % (squareSize + lineGap)) >= squareSize)\r
4158     return -1;\r
4159   x /= (squareSize + lineGap);\r
4160     if (x >= limit)\r
4161     return -2;\r
4162   return x;\r
4163 }\r
4164 \r
4165 typedef struct {\r
4166   char piece;\r
4167   int command;\r
4168   char* name;\r
4169 } DropEnable;\r
4170 \r
4171 DropEnable dropEnables[] = {\r
4172   { 'P', DP_Pawn, N_("Pawn") },\r
4173   { 'N', DP_Knight, N_("Knight") },\r
4174   { 'B', DP_Bishop, N_("Bishop") },\r
4175   { 'R', DP_Rook, N_("Rook") },\r
4176   { 'Q', DP_Queen, N_("Queen") },\r
4177 };\r
4178 \r
4179 VOID\r
4180 SetupDropMenu(HMENU hmenu)\r
4181 {\r
4182   int i, count, enable;\r
4183   char *p;\r
4184   extern char white_holding[], black_holding[];\r
4185   char item[MSG_SIZ];\r
4186 \r
4187   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4188     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4189                dropEnables[i].piece);\r
4190     count = 0;\r
4191     while (p && *p++ == dropEnables[i].piece) count++;\r
4192       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4193     enable = count > 0 || !appData.testLegality\r
4194       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4195                       && !appData.icsActive);\r
4196     ModifyMenu(hmenu, dropEnables[i].command,\r
4197                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4198                dropEnables[i].command, item);\r
4199   }\r
4200 }\r
4201 \r
4202 void DragPieceBegin(int x, int y, Boolean instantly)\r
4203 {\r
4204       dragInfo.lastpos.x = boardRect.left + x;\r
4205       dragInfo.lastpos.y = boardRect.top + y;\r
4206       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4207       dragInfo.from.x = fromX;\r
4208       dragInfo.from.y = fromY;\r
4209       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4210       dragInfo.start = dragInfo.from;\r
4211       SetCapture(hwndMain);\r
4212 }\r
4213 \r
4214 void DragPieceEnd(int x, int y)\r
4215 {\r
4216     ReleaseCapture();\r
4217     dragInfo.start.x = dragInfo.start.y = -1;\r
4218     dragInfo.from = dragInfo.start;\r
4219     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4220 }\r
4221 \r
4222 void ChangeDragPiece(ChessSquare piece)\r
4223 {\r
4224     dragInfo.piece = piece;\r
4225 }\r
4226 \r
4227 /* Event handler for mouse messages */\r
4228 VOID\r
4229 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4230 {\r
4231   int x, y, menuNr;\r
4232   POINT pt;\r
4233   static int recursive = 0;\r
4234   HMENU hmenu;\r
4235   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4236 \r
4237   if (recursive) {\r
4238     if (message == WM_MBUTTONUP) {\r
4239       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4240          to the middle button: we simulate pressing the left button too!\r
4241          */\r
4242       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4243       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4244     }\r
4245     return;\r
4246   }\r
4247   recursive++;\r
4248   \r
4249   pt.x = LOWORD(lParam);\r
4250   pt.y = HIWORD(lParam);\r
4251   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4252   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4253   if (!flipView && y >= 0) {\r
4254     y = BOARD_HEIGHT - 1 - y;\r
4255   }\r
4256   if (flipView && x >= 0) {\r
4257     x = BOARD_WIDTH - 1 - x;\r
4258   }\r
4259 \r
4260   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4261 \r
4262   switch (message) {\r
4263   case WM_LBUTTONDOWN:\r
4264       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4265         ClockClick(flipClock); break;\r
4266       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4267         ClockClick(!flipClock); break;\r
4268       }\r
4269       dragInfo.start.x = dragInfo.start.y = -1;\r
4270       dragInfo.from = dragInfo.start;\r
4271     if(fromX == -1 && frozen) { // not sure where this is for\r
4272                 fromX = fromY = -1; \r
4273       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4274       break;\r
4275     }\r
4276       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4277       DrawPosition(TRUE, NULL);\r
4278     break;\r
4279 \r
4280   case WM_LBUTTONUP:\r
4281       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4282       DrawPosition(TRUE, NULL);\r
4283     break;\r
4284 \r
4285   case WM_MOUSEMOVE:\r
4286     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4287     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4288     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4289     if ((appData.animateDragging || appData.highlightDragging)\r
4290         && (wParam & MK_LBUTTON)\r
4291         && dragInfo.from.x >= 0) \r
4292     {\r
4293       BOOL full_repaint = FALSE;\r
4294 \r
4295       if (appData.animateDragging) {\r
4296         dragInfo.pos = pt;\r
4297       }\r
4298       if (appData.highlightDragging) {\r
4299         SetHighlights(fromX, fromY, x, y);\r
4300         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4301             full_repaint = TRUE;\r
4302         }\r
4303       }\r
4304       \r
4305       DrawPosition( full_repaint, NULL);\r
4306       \r
4307       dragInfo.lastpos = dragInfo.pos;\r
4308     }\r
4309     break;\r
4310 \r
4311   case WM_MOUSEWHEEL: // [DM]\r
4312     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4313        /* Mouse Wheel is being rolled forward\r
4314         * Play moves forward\r
4315         */\r
4316        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4317                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4318        /* Mouse Wheel is being rolled backward\r
4319         * Play moves backward\r
4320         */\r
4321        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4322                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4323     }\r
4324     break;\r
4325 \r
4326   case WM_MBUTTONUP:\r
4327   case WM_RBUTTONUP:\r
4328     ReleaseCapture();\r
4329     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4330     break;\r
4331  \r
4332   case WM_MBUTTONDOWN:\r
4333   case WM_RBUTTONDOWN:\r
4334     ErrorPopDown();\r
4335     ReleaseCapture();\r
4336     fromX = fromY = -1;\r
4337     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4338     dragInfo.start.x = dragInfo.start.y = -1;\r
4339     dragInfo.from = dragInfo.start;\r
4340     dragInfo.lastpos = dragInfo.pos;\r
4341     if (appData.highlightDragging) {\r
4342       ClearHighlights();\r
4343     }\r
4344     if(y == -2) {\r
4345       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4346       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4347           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4348       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4349           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4350       }\r
4351       break;\r
4352     }\r
4353     DrawPosition(TRUE, NULL);\r
4354 \r
4355     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4356     switch (menuNr) {\r
4357     case 0:\r
4358       if (message == WM_MBUTTONDOWN) {\r
4359         buttonCount = 3;  /* even if system didn't think so */\r
4360         if (wParam & MK_SHIFT) \r
4361           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4362         else\r
4363           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4364       } else { /* message == WM_RBUTTONDOWN */\r
4365         /* Just have one menu, on the right button.  Windows users don't\r
4366            think to try the middle one, and sometimes other software steals\r
4367            it, or it doesn't really exist. */\r
4368         if(gameInfo.variant != VariantShogi)\r
4369             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4370         else\r
4371             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4372       }\r
4373       break;\r
4374     case 2:\r
4375       SetCapture(hwndMain);\r
4376       break;\r
4377     case 1:\r
4378       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4379       SetupDropMenu(hmenu);\r
4380       MenuPopup(hwnd, pt, hmenu, -1);\r
4381     default:\r
4382       break;\r
4383     }\r
4384     break;\r
4385   }\r
4386 \r
4387   recursive--;\r
4388 }\r
4389 \r
4390 /* Preprocess messages for buttons in main window */\r
4391 LRESULT CALLBACK\r
4392 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4393 {\r
4394   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4395   int i, dir;\r
4396 \r
4397   for (i=0; i<N_BUTTONS; i++) {\r
4398     if (buttonDesc[i].id == id) break;\r
4399   }\r
4400   if (i == N_BUTTONS) return 0;\r
4401   switch (message) {\r
4402   case WM_KEYDOWN:\r
4403     switch (wParam) {\r
4404     case VK_LEFT:\r
4405     case VK_RIGHT:\r
4406       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4407       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4408       return TRUE;\r
4409     }\r
4410     break;\r
4411   case WM_CHAR:\r
4412     switch (wParam) {\r
4413     case '\r':\r
4414       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4415       return TRUE;\r
4416     default:\r
4417       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4418         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4419         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4420         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4421         SetFocus(h);\r
4422         SendMessage(h, WM_CHAR, wParam, lParam);\r
4423         return TRUE;\r
4424       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4425         TypeInEvent((char)wParam);\r
4426       }\r
4427       break;\r
4428     }\r
4429     break;\r
4430   }\r
4431   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4432 }\r
4433 \r
4434 /* Process messages for Promotion dialog box */\r
4435 LRESULT CALLBACK\r
4436 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4437 {\r
4438   char promoChar;\r
4439 \r
4440   switch (message) {\r
4441   case WM_INITDIALOG: /* message: initialize dialog box */\r
4442     /* Center the dialog over the application window */\r
4443     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4444     Translate(hDlg, DLG_PromotionKing);\r
4445     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4446       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4447        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4448        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4449                SW_SHOW : SW_HIDE);\r
4450     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4451     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4452        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4453          PieceToChar(WhiteAngel) != '~') ||\r
4454         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4455          PieceToChar(BlackAngel) != '~')   ) ?\r
4456                SW_SHOW : SW_HIDE);\r
4457     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4458        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4459          PieceToChar(WhiteMarshall) != '~') ||\r
4460         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4461          PieceToChar(BlackMarshall) != '~')   ) ?\r
4462                SW_SHOW : SW_HIDE);\r
4463     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4464     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4465        gameInfo.variant != VariantShogi ?\r
4466                SW_SHOW : SW_HIDE);\r
4467     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4468        gameInfo.variant != VariantShogi ?\r
4469                SW_SHOW : SW_HIDE);\r
4470     if(gameInfo.variant == VariantShogi) {\r
4471         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4472         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4473         SetWindowText(hDlg, "Promote?");\r
4474     }\r
4475     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4476        gameInfo.variant == VariantSuper ?\r
4477                SW_SHOW : SW_HIDE);\r
4478     return TRUE;\r
4479 \r
4480   case WM_COMMAND: /* message: received a command */\r
4481     switch (LOWORD(wParam)) {\r
4482     case IDCANCEL:\r
4483       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4484       ClearHighlights();\r
4485       DrawPosition(FALSE, NULL);\r
4486       return TRUE;\r
4487     case PB_King:\r
4488       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4489       break;\r
4490     case PB_Queen:\r
4491       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4492       break;\r
4493     case PB_Rook:\r
4494       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4495       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4496       break;\r
4497     case PB_Bishop:\r
4498       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4499       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4500       break;\r
4501     case PB_Chancellor:\r
4502       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4503       break;\r
4504     case PB_Archbishop:\r
4505       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4506       break;\r
4507     case PB_Knight:\r
4508       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4509       break;\r
4510     default:\r
4511       return FALSE;\r
4512     }\r
4513     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4514     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4515     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4516     fromX = fromY = -1;\r
4517     if (!appData.highlightLastMove) {\r
4518       ClearHighlights();\r
4519       DrawPosition(FALSE, NULL);\r
4520     }\r
4521     return TRUE;\r
4522   }\r
4523   return FALSE;\r
4524 }\r
4525 \r
4526 /* Pop up promotion dialog */\r
4527 VOID\r
4528 PromotionPopup(HWND hwnd)\r
4529 {\r
4530   FARPROC lpProc;\r
4531 \r
4532   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4533   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4534     hwnd, (DLGPROC)lpProc);\r
4535   FreeProcInstance(lpProc);\r
4536 }\r
4537 \r
4538 void\r
4539 PromotionPopUp()\r
4540 {\r
4541   DrawPosition(TRUE, NULL);\r
4542   PromotionPopup(hwndMain);\r
4543 }\r
4544 \r
4545 VOID\r
4546 LoadGameDialog(HWND hwnd, char* title)\r
4547 {\r
4548   UINT number = 0;\r
4549   FILE *f;\r
4550   char fileTitle[MSG_SIZ];\r
4551   f = OpenFileDialog(hwnd, "rb", "",\r
4552                      appData.oldSaveStyle ? "gam" : "pgn",\r
4553                      GAME_FILT,\r
4554                      title, &number, fileTitle, NULL);\r
4555   if (f != NULL) {\r
4556     cmailMsgLoaded = FALSE;\r
4557     if (number == 0) {\r
4558       int error = GameListBuild(f);\r
4559       if (error) {\r
4560         DisplayError(_("Cannot build game list"), error);\r
4561       } else if (!ListEmpty(&gameList) &&\r
4562                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4563         GameListPopUp(f, fileTitle);\r
4564         return;\r
4565       }\r
4566       GameListDestroy();\r
4567       number = 1;\r
4568     }\r
4569     LoadGame(f, number, fileTitle, FALSE);\r
4570   }\r
4571 }\r
4572 \r
4573 int get_term_width()\r
4574 {\r
4575     HDC hdc;\r
4576     TEXTMETRIC tm;\r
4577     RECT rc;\r
4578     HFONT hfont, hold_font;\r
4579     LOGFONT lf;\r
4580     HWND hText;\r
4581 \r
4582     if (hwndConsole)\r
4583         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4584     else\r
4585         return 79;\r
4586 \r
4587     // get the text metrics\r
4588     hdc = GetDC(hText);\r
4589     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4590     if (consoleCF.dwEffects & CFE_BOLD)\r
4591         lf.lfWeight = FW_BOLD;\r
4592     if (consoleCF.dwEffects & CFE_ITALIC)\r
4593         lf.lfItalic = TRUE;\r
4594     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4595         lf.lfStrikeOut = TRUE;\r
4596     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4597         lf.lfUnderline = TRUE;\r
4598     hfont = CreateFontIndirect(&lf);\r
4599     hold_font = SelectObject(hdc, hfont);\r
4600     GetTextMetrics(hdc, &tm);\r
4601     SelectObject(hdc, hold_font);\r
4602     DeleteObject(hfont);\r
4603     ReleaseDC(hText, hdc);\r
4604 \r
4605     // get the rectangle\r
4606     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4607 \r
4608     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4609 }\r
4610 \r
4611 void UpdateICSWidth(HWND hText)\r
4612 {\r
4613     LONG old_width, new_width;\r
4614 \r
4615     new_width = get_term_width(hText, FALSE);\r
4616     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4617     if (new_width != old_width)\r
4618     {\r
4619         ics_update_width(new_width);\r
4620         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4621     }\r
4622 }\r
4623 \r
4624 VOID\r
4625 ChangedConsoleFont()\r
4626 {\r
4627   CHARFORMAT cfmt;\r
4628   CHARRANGE tmpsel, sel;\r
4629   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4630   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4631   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4632   PARAFORMAT paraf;\r
4633 \r
4634   cfmt.cbSize = sizeof(CHARFORMAT);\r
4635   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4636     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4637                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4638   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4639    * size.  This was undocumented in the version of MSVC++ that I had\r
4640    * when I wrote the code, but is apparently documented now.\r
4641    */\r
4642   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4643   cfmt.bCharSet = f->lf.lfCharSet;\r
4644   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4645   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4646   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4647   /* Why are the following seemingly needed too? */\r
4648   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4649   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4650   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4651   tmpsel.cpMin = 0;\r
4652   tmpsel.cpMax = -1; /*999999?*/\r
4653   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4654   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4655   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4656    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4657    */\r
4658   paraf.cbSize = sizeof(paraf);\r
4659   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4660   paraf.dxStartIndent = 0;\r
4661   paraf.dxOffset = WRAP_INDENT;\r
4662   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4663   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4664   UpdateICSWidth(hText);\r
4665 }\r
4666 \r
4667 /*---------------------------------------------------------------------------*\\r
4668  *\r
4669  * Window Proc for main window\r
4670  *\r
4671 \*---------------------------------------------------------------------------*/\r
4672 \r
4673 /* Process messages for main window, etc. */\r
4674 LRESULT CALLBACK\r
4675 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4676 {\r
4677   FARPROC lpProc;\r
4678   int wmId, wmEvent;\r
4679   char *defName;\r
4680   FILE *f;\r
4681   UINT number;\r
4682   char fileTitle[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