Fix warning in WinBoard
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern enum ICS_TYPE ics_type;\r
104 \r
105 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P((char *s));\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129   ChessSquare piece;\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
142 \r
143 typedef struct { // [HGM] atomic\r
144   int fromX, fromY, toX, toY, radius;\r
145 } ExplodeInfo;\r
146 \r
147 static ExplodeInfo explodeInfo;\r
148 \r
149 /* Window class names */\r
150 char szAppName[] = "WinBoard";\r
151 char szConsoleName[] = "WBConsole";\r
152 \r
153 /* Title bar text */\r
154 char szTitle[] = "WinBoard";\r
155 char szConsoleTitle[] = "I C S Interaction";\r
156 \r
157 char *programName;\r
158 char *settingsFileName;\r
159 Boolean saveSettingsOnExit;\r
160 char installDir[MSG_SIZ];\r
161 int errorExitStatus;\r
162 \r
163 BoardSize boardSize;\r
164 Boolean chessProgram;\r
165 //static int boardX, boardY;\r
166 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
167 int squareSize, lineGap, minorSize, border;\r
168 static int winW, winH;\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
170 static int logoHeight = 0;\r
171 static char messageText[MESSAGE_TEXT_MAX];\r
172 static int clockTimerEvent = 0;\r
173 static int loadGameTimerEvent = 0;\r
174 static int analysisTimerEvent = 0;\r
175 static DelayedEventCallback delayedTimerCallback;\r
176 static int delayedTimerEvent = 0;\r
177 static int buttonCount = 2;\r
178 char *icsTextMenuString;\r
179 char *icsNames;\r
180 char *firstChessProgramNames;\r
181 char *secondChessProgramNames;\r
182 \r
183 #define PALETTESIZE 256\r
184 \r
185 HINSTANCE hInst;          /* current instance */\r
186 Boolean alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush,      /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #if defined(_winmajor)\r
226 #define oldDialog (_winmajor < 4)\r
227 #else\r
228 #define oldDialog 0\r
229 #endif\r
230 #endif\r
231 \r
232 #define INTERNATIONAL\r
233 \r
234 #ifdef INTERNATIONAL\r
235 #  define _(s) T_(s)\r
236 #  define N_(s) s\r
237 #else\r
238 #  define _(s) s\r
239 #  define N_(s) s\r
240 #  define T_(s) s\r
241 #  define Translate(x, y)\r
242 #  define LoadLanguageFile(s)\r
243 #endif\r
244 \r
245 #ifdef INTERNATIONAL\r
246 \r
247 Boolean barbaric; // flag indicating if translation is needed\r
248 \r
249 // list of item numbers used in each dialog (used to alter language at run time)\r
250 \r
251 #define ABOUTBOX -1  /* not sure why these are needed */\r
252 #define ABOUTBOX2 -1\r
253 \r
254 int dialogItems[][42] = {\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
257   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
259   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
261   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
264   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
267   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
268 { ABOUTBOX2, IDC_ChessBoard }, \r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
270   OPT_GameListClose, IDC_GameListDoFilter }, \r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
272 { DLG_Error, IDOK }, \r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
274   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
277   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
278   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
279 { DLG_IndexNumber, IDC_Index }, \r
280 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
281 { DLG_TypeInName, IDOK, IDCANCEL }, \r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
283   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
285   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
286   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
287   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
288   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
289   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
290   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
292   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
293   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
294   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
295   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
296   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
297   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
298   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
299   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
301   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
302   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
303   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
304   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
305   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
306   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
307   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
309   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
310   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
311   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
312   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
313   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
314   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
315   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
316   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
318   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
319   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
320   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
321   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
324   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
325 { DLG_MoveHistory }, \r
326 { DLG_EvalGraph }, \r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
330   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
331   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
332   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
334   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
335   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
336 { 0 }\r
337 };\r
338 \r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
340 static int lastChecked;\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
342 extern int tinyLayout;\r
343 extern char * menuBarText[][10];\r
344 \r
345 void\r
346 LoadLanguageFile(char *name)\r
347 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
348     FILE *f;\r
349     int i=0, j=0, n=0, k;\r
350     char buf[MSG_SIZ];\r
351 \r
352     if(!name || name[0] == NULLCHAR) return;\r
353       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
354     appData.language = oldLanguage;\r
355     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
356     if((f = fopen(buf, "r")) == NULL) return;\r
357     while((k = fgetc(f)) != EOF) {\r
358         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
359         languageBuf[i] = k;\r
360         if(k == '\n') {\r
361             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
362                 char *p;\r
363                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
364                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
365                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
366                         english[j] = languageBuf + n + 1; *p = 0;\r
367                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
369                     }\r
370                 }\r
371             }\r
372             n = i + 1;\r
373         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
374             switch(k) {\r
375               case 'n': k = '\n'; break;\r
376               case 'r': k = '\r'; break;\r
377               case 't': k = '\t'; break;\r
378             }\r
379             languageBuf[--i] = k;\r
380         }\r
381         i++;\r
382     }\r
383     fclose(f);\r
384     barbaric = (j != 0);\r
385     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
386 }\r
387 \r
388 char *\r
389 T_(char *s)\r
390 {   // return the translation of the given string\r
391     // efficiency can be improved a lot...\r
392     int i=0;\r
393     static char buf[MSG_SIZ];\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
395     if(!barbaric) return s;\r
396     if(!s) return ""; // sanity\r
397     while(english[i]) {\r
398         if(!strcmp(s, english[i])) return foreign[i];\r
399         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
400             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
401             return buf;\r
402         }\r
403         i++;\r
404     }\r
405     return s;\r
406 }\r
407 \r
408 void\r
409 Translate(HWND hDlg, int dialogID)\r
410 {   // translate all text items in the given dialog\r
411     int i=0, j, k;\r
412     char buf[MSG_SIZ], *s;\r
413     if(!barbaric) return;\r
414     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
415     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
416     GetWindowText( hDlg, buf, MSG_SIZ );\r
417     s = T_(buf);\r
418     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
419     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
420         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
421         if(strlen(buf) == 0) continue;\r
422         s = T_(buf);\r
423         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
424     }\r
425 }\r
426 \r
427 HMENU\r
428 TranslateOneMenu(int i, HMENU subMenu)\r
429 {\r
430     int j;\r
431     static MENUITEMINFO info;\r
432 \r
433     info.cbSize = sizeof(MENUITEMINFO);\r
434     info.fMask = MIIM_STATE | MIIM_TYPE;\r
435           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
436             char buf[MSG_SIZ];\r
437             info.dwTypeData = buf;\r
438             info.cch = sizeof(buf);\r
439             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
440             if(i < 10) {\r
441                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
442                 else menuText[i][j] = strdup(buf); // remember original on first change\r
443             }\r
444             if(buf[0] == NULLCHAR) continue;\r
445             info.dwTypeData = T_(buf);\r
446             info.cch = strlen(buf)+1;\r
447             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
448           }\r
449     return subMenu;\r
450 }\r
451 \r
452 void\r
453 TranslateMenus(int addLanguage)\r
454 {\r
455     int i;\r
456     WIN32_FIND_DATA fileData;\r
457     HANDLE hFind;\r
458 #define IDM_English 1970\r
459     if(1) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
462           HMENU subMenu = GetSubMenu(mainMenu, i);\r
463           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
464                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
465           TranslateOneMenu(i, subMenu);\r
466         }\r
467         DrawMenuBar(hwndMain);\r
468     }\r
469 \r
470     if(!addLanguage) return;\r
471     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
472         HMENU mainMenu = GetMenu(hwndMain);\r
473         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
474         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
475         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
476         i = 0; lastChecked = IDM_English;\r
477         do {\r
478             char *p, *q = fileData.cFileName;\r
479             int checkFlag = MF_UNCHECKED;\r
480             languageFile[i] = strdup(q);\r
481             if(barbaric && !strcmp(oldLanguage, q)) {\r
482                 checkFlag = MF_CHECKED;\r
483                 lastChecked = IDM_English + i + 1;\r
484                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
485             }\r
486             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
487             p = strstr(fileData.cFileName, ".lng");\r
488             if(p) *p = 0;\r
489             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
490         } while(FindNextFile(hFind, &fileData));\r
491         FindClose(hFind);\r
492     }\r
493 }\r
494 \r
495 #endif\r
496 \r
497 #define IDM_RecentEngines 3000\r
498 \r
499 void\r
500 RecentEngineMenu (char *s)\r
501 {\r
502     if(appData.icsActive) return;\r
503     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
504         HMENU mainMenu = GetMenu(hwndMain);\r
505         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
506         int i=IDM_RecentEngines;\r
507         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
508         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
509         while(*s) {\r
510           char *p = strchr(s, '\n');\r
511           if(p == NULL) return; // malformed!\r
512           *p = NULLCHAR;\r
513           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
514           *p = '\n';\r
515           s = p+1;\r
516         }\r
517     }\r
518 }\r
519 \r
520 \r
521 typedef struct {\r
522   char *name;\r
523   int squareSize;\r
524   int lineGap;\r
525   int smallLayout;\r
526   int tinyLayout;\r
527   int cliWidth, cliHeight;\r
528 } SizeInfo;\r
529 \r
530 SizeInfo sizeInfo[] = \r
531 {\r
532   { "tiny",     21, 0, 1, 1, 0, 0 },\r
533   { "teeny",    25, 1, 1, 1, 0, 0 },\r
534   { "dinky",    29, 1, 1, 1, 0, 0 },\r
535   { "petite",   33, 1, 1, 1, 0, 0 },\r
536   { "slim",     37, 2, 1, 0, 0, 0 },\r
537   { "small",    40, 2, 1, 0, 0, 0 },\r
538   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
539   { "middling", 49, 2, 0, 0, 0, 0 },\r
540   { "average",  54, 2, 0, 0, 0, 0 },\r
541   { "moderate", 58, 3, 0, 0, 0, 0 },\r
542   { "medium",   64, 3, 0, 0, 0, 0 },\r
543   { "bulky",    72, 3, 0, 0, 0, 0 },\r
544   { "large",    80, 3, 0, 0, 0, 0 },\r
545   { "big",      87, 3, 0, 0, 0, 0 },\r
546   { "huge",     95, 3, 0, 0, 0, 0 },\r
547   { "giant",    108, 3, 0, 0, 0, 0 },\r
548   { "colossal", 116, 4, 0, 0, 0, 0 },\r
549   { "titanic",  129, 4, 0, 0, 0, 0 },\r
550   { NULL, 0, 0, 0, 0, 0, 0 }\r
551 };\r
552 \r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
555 {\r
556   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
557   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
558   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
559   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
560   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
561   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
574 };\r
575 \r
576 MyFont *font[NUM_SIZES][NUM_FONTS];\r
577 \r
578 typedef struct {\r
579   char *label;\r
580   int id;\r
581   HWND hwnd;\r
582   WNDPROC wndproc;\r
583 } MyButtonDesc;\r
584 \r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
586 #define N_BUTTONS 5\r
587 \r
588 MyButtonDesc buttonDesc[N_BUTTONS] =\r
589 {\r
590   {"<<", IDM_ToStart, NULL, NULL},\r
591   {"<", IDM_Backward, NULL, NULL},\r
592   {"P", IDM_Pause, NULL, NULL},\r
593   {">", IDM_Forward, NULL, NULL},\r
594   {">>", IDM_ToEnd, NULL, NULL},\r
595 };\r
596 \r
597 int tinyLayout = 0, smallLayout = 0;\r
598 #define MENU_BAR_ITEMS 9\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
600   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
601   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
602 };\r
603 \r
604 \r
605 MySound sounds[(int)NSoundClasses];\r
606 MyTextAttribs textAttribs[(int)NColorClasses];\r
607 \r
608 MyColorizeAttribs colorizeAttribs[] = {\r
609   { (COLORREF)0, 0, N_("Shout Text") },\r
610   { (COLORREF)0, 0, N_("SShout/CShout") },\r
611   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
612   { (COLORREF)0, 0, N_("Channel Text") },\r
613   { (COLORREF)0, 0, N_("Kibitz Text") },\r
614   { (COLORREF)0, 0, N_("Tell Text") },\r
615   { (COLORREF)0, 0, N_("Challenge Text") },\r
616   { (COLORREF)0, 0, N_("Request Text") },\r
617   { (COLORREF)0, 0, N_("Seek Text") },\r
618   { (COLORREF)0, 0, N_("Normal Text") },\r
619   { (COLORREF)0, 0, N_("None") }\r
620 };\r
621 \r
622 \r
623 \r
624 static char *commentTitle;\r
625 static char *commentText;\r
626 static int commentIndex;\r
627 static Boolean editComment = FALSE;\r
628 \r
629 \r
630 char errorTitle[MSG_SIZ];\r
631 char errorMessage[2*MSG_SIZ];\r
632 HWND errorDialog = NULL;\r
633 BOOLEAN moveErrorMessageUp = FALSE;\r
634 BOOLEAN consoleEcho = TRUE;\r
635 CHARFORMAT consoleCF;\r
636 COLORREF consoleBackgroundColor;\r
637 \r
638 char *programVersion;\r
639 \r
640 #define CPReal 1\r
641 #define CPComm 2\r
642 #define CPSock 3\r
643 #define CPRcmd 4\r
644 typedef int CPKind;\r
645 \r
646 typedef struct {\r
647   CPKind kind;\r
648   HANDLE hProcess;\r
649   DWORD pid;\r
650   HANDLE hTo;\r
651   HANDLE hFrom;\r
652   SOCKET sock;\r
653   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
654 } ChildProc;\r
655 \r
656 #define INPUT_SOURCE_BUF_SIZE 4096\r
657 \r
658 typedef struct _InputSource {\r
659   CPKind kind;\r
660   HANDLE hFile;\r
661   SOCKET sock;\r
662   int lineByLine;\r
663   HANDLE hThread;\r
664   DWORD id;\r
665   char buf[INPUT_SOURCE_BUF_SIZE];\r
666   char *next;\r
667   DWORD count;\r
668   int error;\r
669   InputCallback func;\r
670   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
671   VOIDSTAR closure;\r
672 } InputSource;\r
673 \r
674 InputSource *consoleInputSource;\r
675 \r
676 DCB dcb;\r
677 \r
678 /* forward */\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
680 VOID ConsoleCreate();\r
681 LRESULT CALLBACK\r
682   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);\r
686 LRESULT CALLBACK\r
687   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
689 void ParseIcsTextMenu(char *icsTextMenuString);\r
690 VOID PopUpNameDialog(char firstchar);\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
692 \r
693 /* [AS] */\r
694 int NewGameFRC();\r
695 int GameListOptions();\r
696 \r
697 int dummy; // [HGM] for obsolete args\r
698 \r
699 HWND hwndMain = NULL;        /* root window*/\r
700 HWND hwndConsole = NULL;\r
701 HWND commentDialog = NULL;\r
702 HWND moveHistoryDialog = NULL;\r
703 HWND evalGraphDialog = NULL;\r
704 HWND engineOutputDialog = NULL;\r
705 HWND gameListDialog = NULL;\r
706 HWND editTagsDialog = NULL;\r
707 \r
708 int commentUp = FALSE;\r
709 \r
710 WindowPlacement wpMain;\r
711 WindowPlacement wpConsole;\r
712 WindowPlacement wpComment;\r
713 WindowPlacement wpMoveHistory;\r
714 WindowPlacement wpEvalGraph;\r
715 WindowPlacement wpEngineOutput;\r
716 WindowPlacement wpGameList;\r
717 WindowPlacement wpTags;\r
718 \r
719 VOID EngineOptionsPopup(); // [HGM] settings\r
720 \r
721 VOID GothicPopUp(char *title, VariantClass variant);\r
722 /*\r
723  * Setting "frozen" should disable all user input other than deleting\r
724  * the window.  We do this while engines are initializing themselves.\r
725  */\r
726 static int frozen = 0;\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];\r
728 void FreezeUI()\r
729 {\r
730   HMENU hmenu;\r
731   int i;\r
732 \r
733   if (frozen) return;\r
734   frozen = 1;\r
735   hmenu = GetMenu(hwndMain);\r
736   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
737     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
738   }\r
739   DrawMenuBar(hwndMain);\r
740 }\r
741 \r
742 /* Undo a FreezeUI */\r
743 void ThawUI()\r
744 {\r
745   HMENU hmenu;\r
746   int i;\r
747 \r
748   if (!frozen) return;\r
749   frozen = 0;\r
750   hmenu = GetMenu(hwndMain);\r
751   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
752     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
753   }\r
754   DrawMenuBar(hwndMain);\r
755 }\r
756 \r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
758 \r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
760 #ifdef JAWS\r
761 #include "jaws.c"\r
762 #else\r
763 #define JAWS_INIT\r
764 #define JAWS_ARGS\r
765 #define JAWS_ALT_INTERCEPT\r
766 #define JAWS_KBUP_NAVIGATION\r
767 #define JAWS_KBDOWN_NAVIGATION\r
768 #define JAWS_MENU_ITEMS\r
769 #define JAWS_SILENCE\r
770 #define JAWS_REPLAY\r
771 #define JAWS_ACCEL\r
772 #define JAWS_COPYRIGHT\r
773 #define JAWS_DELETE(X) X\r
774 #define SAYMACHINEMOVE()\r
775 #define SAY(X)\r
776 #endif\r
777 \r
778 /*---------------------------------------------------------------------------*\\r
779  *\r
780  * WinMain\r
781  *\r
782 \*---------------------------------------------------------------------------*/\r
783 \r
784 int APIENTRY\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
786         LPSTR lpCmdLine, int nCmdShow)\r
787 {\r
788   MSG msg;\r
789   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
790 //  INITCOMMONCONTROLSEX ex;\r
791 \r
792   debugFP = stderr;\r
793 \r
794   LoadLibrary("RICHED32.DLL");\r
795   consoleCF.cbSize = sizeof(CHARFORMAT);\r
796 \r
797   if (!InitApplication(hInstance)) {\r
798     return (FALSE);\r
799   }\r
800   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
801     return (FALSE);\r
802   }\r
803 \r
804   JAWS_INIT\r
805   TranslateMenus(1);\r
806 \r
807 //  InitCommonControlsEx(&ex);\r
808   InitCommonControls();\r
809 \r
810   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
811   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
812   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
813 \r
814   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
815 \r
816   while (GetMessage(&msg, /* message structure */\r
817                     NULL, /* handle of window receiving the message */\r
818                     0,    /* lowest message to examine */\r
819                     0))   /* highest message to examine */\r
820     {\r
821 \r
822       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
823         // [HGM] navigate: switch between all windows with tab\r
824         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
825         int i, currentElement = 0;\r
826 \r
827         // first determine what element of the chain we come from (if any)\r
828         if(appData.icsActive) {\r
829             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
830             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
831         }\r
832         if(engineOutputDialog && EngineOutputIsUp()) {\r
833             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
834             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
835         }\r
836         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
837             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
838         }\r
839         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
840         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
841         if(msg.hwnd == e1)                 currentElement = 2; else\r
842         if(msg.hwnd == e2)                 currentElement = 3; else\r
843         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
844         if(msg.hwnd == mh)                currentElement = 4; else\r
845         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
846         if(msg.hwnd == hText)  currentElement = 5; else\r
847         if(msg.hwnd == hInput) currentElement = 6; else\r
848         for (i = 0; i < N_BUTTONS; i++) {\r
849             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
850         }\r
851 \r
852         // determine where to go to\r
853         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
854           do {\r
855             currentElement = (currentElement + direction) % 7;\r
856             switch(currentElement) {\r
857                 case 0:\r
858                   h = hwndMain; break; // passing this case always makes the loop exit\r
859                 case 1:\r
860                   h = buttonDesc[0].hwnd; break; // could be NULL\r
861                 case 2:\r
862                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
863                   h = e1; break;\r
864                 case 3:\r
865                   if(!EngineOutputIsUp()) continue;\r
866                   h = e2; break;\r
867                 case 4:\r
868                   if(!MoveHistoryIsUp()) continue;\r
869                   h = mh; break;\r
870 //              case 6: // input to eval graph does not seem to get here!\r
871 //                if(!EvalGraphIsUp()) continue;\r
872 //                h = evalGraphDialog; break;\r
873                 case 5:\r
874                   if(!appData.icsActive) continue;\r
875                   SAY("display");\r
876                   h = hText; break;\r
877                 case 6:\r
878                   if(!appData.icsActive) continue;\r
879                   SAY("input");\r
880                   h = hInput; break;\r
881             }\r
882           } while(h == 0);\r
883 \r
884           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
885           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
886           SetFocus(h);\r
887 \r
888           continue; // this message now has been processed\r
889         }\r
890       }\r
891 \r
892       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
893           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
894           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
895           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
896           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
897           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
898           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
899           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
900           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
901           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
902         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
903         for(i=0; i<MAX_CHAT; i++) \r
904             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
905                 done = 1; break;\r
906         }\r
907         if(done) continue; // [HGM] chat: end patch\r
908         TranslateMessage(&msg); /* Translates virtual key codes */\r
909         DispatchMessage(&msg);  /* Dispatches message to window */\r
910       }\r
911     }\r
912 \r
913 \r
914   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
915 }\r
916 \r
917 /*---------------------------------------------------------------------------*\\r
918  *\r
919  * Initialization functions\r
920  *\r
921 \*---------------------------------------------------------------------------*/\r
922 \r
923 void\r
924 SetUserLogo()\r
925 {   // update user logo if necessary\r
926     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
927 \r
928     if(appData.autoLogo) {\r
929           curName = UserName();\r
930           if(strcmp(curName, oldUserName)) {\r
931                 GetCurrentDirectory(MSG_SIZ, dir);\r
932                 SetCurrentDirectory(installDir);\r
933                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
934                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
935                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
936                 if(userLogo == NULL)\r
937                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
938                 SetCurrentDirectory(dir); /* return to prev directory */\r
939           }\r
940     }\r
941 }\r
942 \r
943 BOOL\r
944 InitApplication(HINSTANCE hInstance)\r
945 {\r
946   WNDCLASS wc;\r
947 \r
948   /* Fill in window class structure with parameters that describe the */\r
949   /* main window. */\r
950 \r
951   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
952   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
953   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
954   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
955   wc.hInstance     = hInstance;         /* Owner of this class */\r
956   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
957   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
958   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
959   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
960   wc.lpszClassName = szAppName;                 /* Name to register as */\r
961 \r
962   /* Register the window class and return success/failure code. */\r
963   if (!RegisterClass(&wc)) return FALSE;\r
964 \r
965   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
966   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
967   wc.cbClsExtra    = 0;\r
968   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
969   wc.hInstance     = hInstance;\r
970   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
971   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
972   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
973   wc.lpszMenuName  = NULL;\r
974   wc.lpszClassName = szConsoleName;\r
975 \r
976   if (!RegisterClass(&wc)) return FALSE;\r
977   return TRUE;\r
978 }\r
979 \r
980 \r
981 /* Set by InitInstance, used by EnsureOnScreen */\r
982 int screenHeight, screenWidth;\r
983 \r
984 void\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
986 {\r
987 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
988   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
989   if (*x > screenWidth - 32) *x = 0;\r
990   if (*y > screenHeight - 32) *y = 0;\r
991   if (*x < minX) *x = minX;\r
992   if (*y < minY) *y = minY;\r
993 }\r
994 \r
995 VOID\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
997 {\r
998   char buf[MSG_SIZ], dir[MSG_SIZ];\r
999   GetCurrentDirectory(MSG_SIZ, dir);\r
1000   SetCurrentDirectory(installDir);\r
1001   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1002       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1003 \r
1004       if (cps->programLogo == NULL && appData.debugMode) {\r
1005           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1006       }\r
1007   } else if(appData.autoLogo) {\r
1008       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1009         char *opponent = "";\r
1010         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1011         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1012         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1013         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1014             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1015             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1016         }\r
1017       } else\r
1018       if(appData.directory[n] && appData.directory[n][0]) {\r
1019         SetCurrentDirectory(appData.directory[n]);\r
1020         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1021       }\r
1022   }\r
1023   SetCurrentDirectory(dir); /* return to prev directory */\r
1024 }\r
1025 \r
1026 VOID\r
1027 InitTextures()\r
1028 {\r
1029   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1030   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1031   \r
1032   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1033       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1034       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1035       liteBackTextureMode = appData.liteBackTextureMode;\r
1036 \r
1037       if (liteBackTexture == NULL && appData.debugMode) {\r
1038           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1039       }\r
1040   }\r
1041   \r
1042   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1043       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1044       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1045       darkBackTextureMode = appData.darkBackTextureMode;\r
1046 \r
1047       if (darkBackTexture == NULL && appData.debugMode) {\r
1048           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1049       }\r
1050   }\r
1051 }\r
1052 \r
1053 BOOL\r
1054 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1055 {\r
1056   HWND hwnd; /* Main window handle. */\r
1057   int ibs;\r
1058   WINDOWPLACEMENT wp;\r
1059   char *filepart;\r
1060 \r
1061   hInst = hInstance;    /* Store instance handle in our global variable */\r
1062   programName = szAppName;\r
1063 \r
1064   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1065     *filepart = NULLCHAR;\r
1066   } else {\r
1067     GetCurrentDirectory(MSG_SIZ, installDir);\r
1068   }\r
1069   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1070   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1071   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1072   /* xboard, and older WinBoards, controlled the move sound with the\r
1073      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1074      always turn the option on (so that the backend will call us),\r
1075      then let the user turn the sound off by setting it to silence if\r
1076      desired.  To accommodate old winboard.ini files saved by old\r
1077      versions of WinBoard, we also turn off the sound if the option\r
1078      was initially set to false. [HGM] taken out of InitAppData */\r
1079   if (!appData.ringBellAfterMoves) {\r
1080     sounds[(int)SoundMove].name = strdup("");\r
1081     appData.ringBellAfterMoves = TRUE;\r
1082   }\r
1083   if (appData.debugMode) {\r
1084     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1085     setbuf(debugFP, NULL);\r
1086   }\r
1087 \r
1088   LoadLanguageFile(appData.language);\r
1089 \r
1090   InitBackEnd1();\r
1091 \r
1092 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1093 //  InitEngineUCI( installDir, &second );\r
1094 \r
1095   /* Create a main window for this application instance. */\r
1096   hwnd = CreateWindow(szAppName, szTitle,\r
1097                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1098                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1099                       NULL, NULL, hInstance, NULL);\r
1100   hwndMain = hwnd;\r
1101 \r
1102   /* If window could not be created, return "failure" */\r
1103   if (!hwnd) {\r
1104     return (FALSE);\r
1105   }\r
1106 \r
1107   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1108   LoadLogo(&first, 0, FALSE);\r
1109   LoadLogo(&second, 1, appData.icsActive);\r
1110 \r
1111   SetUserLogo();\r
1112 \r
1113   iconWhite = LoadIcon(hInstance, "icon_white");\r
1114   iconBlack = LoadIcon(hInstance, "icon_black");\r
1115   iconCurrent = iconWhite;\r
1116   InitDrawingColors();\r
1117   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1118   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1119   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1120     /* Compute window size for each board size, and use the largest\r
1121        size that fits on this screen as the default. */\r
1122     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1123     if (boardSize == (BoardSize)-1 &&\r
1124         winH <= screenHeight\r
1125            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1126         && winW <= screenWidth) {\r
1127       boardSize = (BoardSize)ibs;\r
1128     }\r
1129   }\r
1130 \r
1131   InitDrawingSizes(boardSize, 0);\r
1132   RecentEngineMenu(appData.recentEngineList);\r
1133   InitMenuChecks();\r
1134   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1135 \r
1136   /* [AS] Load textures if specified */\r
1137   InitTextures();\r
1138 \r
1139   mysrandom( (unsigned) time(NULL) );\r
1140 \r
1141   /* [AS] Restore layout */\r
1142   if( wpMoveHistory.visible ) {\r
1143       MoveHistoryPopUp();\r
1144   }\r
1145 \r
1146   if( wpEvalGraph.visible ) {\r
1147       EvalGraphPopUp();\r
1148   }\r
1149 \r
1150   if( wpEngineOutput.visible ) {\r
1151       EngineOutputPopUp();\r
1152   }\r
1153 \r
1154   /* Make the window visible; update its client area; and return "success" */\r
1155   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1156   wp.length = sizeof(WINDOWPLACEMENT);\r
1157   wp.flags = 0;\r
1158   wp.showCmd = nCmdShow;\r
1159   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1160   wp.rcNormalPosition.left = wpMain.x;\r
1161   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1162   wp.rcNormalPosition.top = wpMain.y;\r
1163   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1164   SetWindowPlacement(hwndMain, &wp);\r
1165 \r
1166   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1167 \r
1168   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1169                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1170 \r
1171   if (hwndConsole) {\r
1172 #if AOT_CONSOLE\r
1173     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1174                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1175 #endif\r
1176     ShowWindow(hwndConsole, nCmdShow);\r
1177     SetActiveWindow(hwndConsole);\r
1178   }\r
1179   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1180   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1181 \r
1182   return TRUE;\r
1183 \r
1184 }\r
1185 \r
1186 VOID\r
1187 InitMenuChecks()\r
1188 {\r
1189   HMENU hmenu = GetMenu(hwndMain);\r
1190 \r
1191   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1192                         MF_BYCOMMAND|((appData.icsActive &&\r
1193                                        *appData.icsCommPort != NULLCHAR) ?\r
1194                                       MF_ENABLED : MF_GRAYED));\r
1195   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1196                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1197                                      MF_CHECKED : MF_UNCHECKED));\r
1198 }\r
1199 \r
1200 //---------------------------------------------------------------------------------------------------------\r
1201 \r
1202 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1203 #define XBOARD FALSE\r
1204 \r
1205 #define OPTCHAR "/"\r
1206 #define SEPCHAR "="\r
1207 #define TOPLEVEL 0\r
1208 \r
1209 #include "args.h"\r
1210 \r
1211 // front-end part of option handling\r
1212 \r
1213 VOID\r
1214 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1215 {\r
1216   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1217   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1218   DeleteDC(hdc);\r
1219   lf->lfWidth = 0;\r
1220   lf->lfEscapement = 0;\r
1221   lf->lfOrientation = 0;\r
1222   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1223   lf->lfItalic = mfp->italic;\r
1224   lf->lfUnderline = mfp->underline;\r
1225   lf->lfStrikeOut = mfp->strikeout;\r
1226   lf->lfCharSet = mfp->charset;\r
1227   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1228   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1229   lf->lfQuality = DEFAULT_QUALITY;\r
1230   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1231     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1232 }\r
1233 \r
1234 void\r
1235 CreateFontInMF(MyFont *mf)\r
1236\r
1237   LFfromMFP(&mf->lf, &mf->mfp);\r
1238   if (mf->hf) DeleteObject(mf->hf);\r
1239   mf->hf = CreateFontIndirect(&mf->lf);\r
1240 }\r
1241 \r
1242 // [HGM] This platform-dependent table provides the location for storing the color info\r
1243 void *\r
1244 colorVariable[] = {\r
1245   &whitePieceColor, \r
1246   &blackPieceColor, \r
1247   &lightSquareColor,\r
1248   &darkSquareColor, \r
1249   &highlightSquareColor,\r
1250   &premoveHighlightColor,\r
1251   NULL,\r
1252   &consoleBackgroundColor,\r
1253   &appData.fontForeColorWhite,\r
1254   &appData.fontBackColorWhite,\r
1255   &appData.fontForeColorBlack,\r
1256   &appData.fontBackColorBlack,\r
1257   &appData.evalHistColorWhite,\r
1258   &appData.evalHistColorBlack,\r
1259   &appData.highlightArrowColor,\r
1260 };\r
1261 \r
1262 /* Command line font name parser.  NULL name means do nothing.\r
1263    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1264    For backward compatibility, syntax without the colon is also\r
1265    accepted, but font names with digits in them won't work in that case.\r
1266 */\r
1267 VOID\r
1268 ParseFontName(char *name, MyFontParams *mfp)\r
1269 {\r
1270   char *p, *q;\r
1271   if (name == NULL) return;\r
1272   p = name;\r
1273   q = strchr(p, ':');\r
1274   if (q) {\r
1275     if (q - p >= sizeof(mfp->faceName))\r
1276       ExitArgError(_("Font name too long:"), name, TRUE);\r
1277     memcpy(mfp->faceName, p, q - p);\r
1278     mfp->faceName[q - p] = NULLCHAR;\r
1279     p = q + 1;\r
1280   } else {\r
1281     q = mfp->faceName;\r
1282 \r
1283     while (*p && !isdigit(*p)) {\r
1284       *q++ = *p++;\r
1285       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1286         ExitArgError(_("Font name too long:"), name, TRUE);\r
1287     }\r
1288     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1289     *q = NULLCHAR;\r
1290   }\r
1291   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1292   mfp->pointSize = (float) atof(p);\r
1293   mfp->bold = (strchr(p, 'b') != NULL);\r
1294   mfp->italic = (strchr(p, 'i') != NULL);\r
1295   mfp->underline = (strchr(p, 'u') != NULL);\r
1296   mfp->strikeout = (strchr(p, 's') != NULL);\r
1297   mfp->charset = DEFAULT_CHARSET;\r
1298   q = strchr(p, 'c');\r
1299   if (q)\r
1300     mfp->charset = (BYTE) atoi(q+1);\r
1301 }\r
1302 \r
1303 void\r
1304 ParseFont(char *name, int number)\r
1305 { // wrapper to shield back-end from 'font'\r
1306   ParseFontName(name, &font[boardSize][number]->mfp);\r
1307 }\r
1308 \r
1309 void\r
1310 SetFontDefaults()\r
1311 { // in WB  we have a 2D array of fonts; this initializes their description\r
1312   int i, j;\r
1313   /* Point font array elements to structures and\r
1314      parse default font names */\r
1315   for (i=0; i<NUM_FONTS; i++) {\r
1316     for (j=0; j<NUM_SIZES; j++) {\r
1317       font[j][i] = &fontRec[j][i];\r
1318       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1319     }\r
1320   }\r
1321 }\r
1322 \r
1323 void\r
1324 CreateFonts()\r
1325 { // here we create the actual fonts from the selected descriptions\r
1326   int i, j;\r
1327   for (i=0; i<NUM_FONTS; i++) {\r
1328     for (j=0; j<NUM_SIZES; j++) {\r
1329       CreateFontInMF(font[j][i]);\r
1330     }\r
1331   }\r
1332 }\r
1333 /* Color name parser.\r
1334    X version accepts X color names, but this one\r
1335    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1336 COLORREF\r
1337 ParseColorName(char *name)\r
1338 {\r
1339   int red, green, blue, count;\r
1340   char buf[MSG_SIZ];\r
1341 \r
1342   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1343   if (count != 3) {\r
1344     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1345       &red, &green, &blue);\r
1346   }\r
1347   if (count != 3) {\r
1348     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1349     DisplayError(buf, 0);\r
1350     return RGB(0, 0, 0);\r
1351   }\r
1352   return PALETTERGB(red, green, blue);\r
1353 }\r
1354 \r
1355 void\r
1356 ParseColor(int n, char *name)\r
1357 { // for WinBoard the color is an int, which needs to be derived from the string\r
1358   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1359 }\r
1360 \r
1361 void\r
1362 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1363 {\r
1364   char *e = argValue;\r
1365   int eff = 0;\r
1366 \r
1367   while (*e) {\r
1368     if (*e == 'b')      eff |= CFE_BOLD;\r
1369     else if (*e == 'i') eff |= CFE_ITALIC;\r
1370     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1371     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1372     else if (*e == '#' || isdigit(*e)) break;\r
1373     e++;\r
1374   }\r
1375   *effects = eff;\r
1376   *color   = ParseColorName(e);\r
1377 }\r
1378 \r
1379 void\r
1380 ParseTextAttribs(ColorClass cc, char *s)\r
1381 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1382     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1383     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1384 }\r
1385 \r
1386 void\r
1387 ParseBoardSize(void *addr, char *name)\r
1388 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1389   BoardSize bs = SizeTiny;\r
1390   while (sizeInfo[bs].name != NULL) {\r
1391     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1392         *(BoardSize *)addr = bs;\r
1393         return;\r
1394     }\r
1395     bs++;\r
1396   }\r
1397   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1398 }\r
1399 \r
1400 void\r
1401 LoadAllSounds()\r
1402 { // [HGM] import name from appData first\r
1403   ColorClass cc;\r
1404   SoundClass sc;\r
1405   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1406     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1407     textAttribs[cc].sound.data = NULL;\r
1408     MyLoadSound(&textAttribs[cc].sound);\r
1409   }\r
1410   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1411     textAttribs[cc].sound.name = strdup("");\r
1412     textAttribs[cc].sound.data = NULL;\r
1413   }\r
1414   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1415     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1416     sounds[sc].data = NULL;\r
1417     MyLoadSound(&sounds[sc]);\r
1418   }\r
1419 }\r
1420 \r
1421 void\r
1422 SetCommPortDefaults()\r
1423 {\r
1424    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1425   dcb.DCBlength = sizeof(DCB);\r
1426   dcb.BaudRate = 9600;\r
1427   dcb.fBinary = TRUE;\r
1428   dcb.fParity = FALSE;\r
1429   dcb.fOutxCtsFlow = FALSE;\r
1430   dcb.fOutxDsrFlow = FALSE;\r
1431   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1432   dcb.fDsrSensitivity = FALSE;\r
1433   dcb.fTXContinueOnXoff = TRUE;\r
1434   dcb.fOutX = FALSE;\r
1435   dcb.fInX = FALSE;\r
1436   dcb.fNull = FALSE;\r
1437   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1438   dcb.fAbortOnError = FALSE;\r
1439   dcb.ByteSize = 7;\r
1440   dcb.Parity = SPACEPARITY;\r
1441   dcb.StopBits = ONESTOPBIT;\r
1442 }\r
1443 \r
1444 // [HGM] args: these three cases taken out to stay in front-end\r
1445 void\r
1446 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1447 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1448         // while the curent board size determines the element. This system should be ported to XBoard.\r
1449         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1450         int bs;\r
1451         for (bs=0; bs<NUM_SIZES; bs++) {\r
1452           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1453           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1454           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1455             ad->argName, mfp->faceName, mfp->pointSize,\r
1456             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1457             mfp->bold ? "b" : "",\r
1458             mfp->italic ? "i" : "",\r
1459             mfp->underline ? "u" : "",\r
1460             mfp->strikeout ? "s" : "",\r
1461             (int)mfp->charset);\r
1462         }\r
1463       }\r
1464 \r
1465 void\r
1466 ExportSounds()\r
1467 { // [HGM] copy the names from the internal WB variables to appData\r
1468   ColorClass cc;\r
1469   SoundClass sc;\r
1470   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1471     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1472   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1473     (&appData.soundMove)[sc] = sounds[sc].name;\r
1474 }\r
1475 \r
1476 void\r
1477 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1478 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1479         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1480         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1481           (ta->effects & CFE_BOLD) ? "b" : "",\r
1482           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1483           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1484           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1485           (ta->effects) ? " " : "",\r
1486           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1487       }\r
1488 \r
1489 void\r
1490 SaveColor(FILE *f, ArgDescriptor *ad)\r
1491 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1492         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1493         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1494           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1495 }\r
1496 \r
1497 void\r
1498 SaveBoardSize(FILE *f, char *name, void *addr)\r
1499 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1500   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1501 }\r
1502 \r
1503 void\r
1504 ParseCommPortSettings(char *s)\r
1505 { // wrapper to keep dcb from back-end\r
1506   ParseCommSettings(s, &dcb);\r
1507 }\r
1508 \r
1509 void\r
1510 GetWindowCoords()\r
1511 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1512   GetActualPlacement(hwndMain, &wpMain);\r
1513   GetActualPlacement(hwndConsole, &wpConsole);\r
1514   GetActualPlacement(commentDialog, &wpComment);\r
1515   GetActualPlacement(editTagsDialog, &wpTags);\r
1516   GetActualPlacement(gameListDialog, &wpGameList);\r
1517   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1518   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1519   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1520 }\r
1521 \r
1522 void\r
1523 PrintCommPortSettings(FILE *f, char *name)\r
1524 { // wrapper to shield back-end from DCB\r
1525       PrintCommSettings(f, name, &dcb);\r
1526 }\r
1527 \r
1528 int\r
1529 MySearchPath(char *installDir, char *name, char *fullname)\r
1530 {\r
1531   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1532   if(name[0]== '%') {\r
1533     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1534     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1535       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1536       *strchr(buf, '%') = 0;\r
1537       strcat(fullname, getenv(buf));\r
1538       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1539     }\r
1540     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1541     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1542     return (int) strlen(fullname);\r
1543   }\r
1544   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1545 }\r
1546 \r
1547 int\r
1548 MyGetFullPathName(char *name, char *fullname)\r
1549 {\r
1550   char *dummy;\r
1551   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1552 }\r
1553 \r
1554 int\r
1555 MainWindowUp()\r
1556 { // [HGM] args: allows testing if main window is realized from back-end\r
1557   return hwndMain != NULL;\r
1558 }\r
1559 \r
1560 void\r
1561 PopUpStartupDialog()\r
1562 {\r
1563     FARPROC lpProc;\r
1564     \r
1565     LoadLanguageFile(appData.language);\r
1566     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1567     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1568     FreeProcInstance(lpProc);\r
1569 }\r
1570 \r
1571 /*---------------------------------------------------------------------------*\\r
1572  *\r
1573  * GDI board drawing routines\r
1574  *\r
1575 \*---------------------------------------------------------------------------*/\r
1576 \r
1577 /* [AS] Draw square using background texture */\r
1578 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1579 {\r
1580     XFORM   x;\r
1581 \r
1582     if( mode == 0 ) {\r
1583         return; /* Should never happen! */\r
1584     }\r
1585 \r
1586     SetGraphicsMode( dst, GM_ADVANCED );\r
1587 \r
1588     switch( mode ) {\r
1589     case 1:\r
1590         /* Identity */\r
1591         break;\r
1592     case 2:\r
1593         /* X reflection */\r
1594         x.eM11 = -1.0;\r
1595         x.eM12 = 0;\r
1596         x.eM21 = 0;\r
1597         x.eM22 = 1.0;\r
1598         x.eDx = (FLOAT) dw + dx - 1;\r
1599         x.eDy = 0;\r
1600         dx = 0;\r
1601         SetWorldTransform( dst, &x );\r
1602         break;\r
1603     case 3:\r
1604         /* Y reflection */\r
1605         x.eM11 = 1.0;\r
1606         x.eM12 = 0;\r
1607         x.eM21 = 0;\r
1608         x.eM22 = -1.0;\r
1609         x.eDx = 0;\r
1610         x.eDy = (FLOAT) dh + dy - 1;\r
1611         dy = 0;\r
1612         SetWorldTransform( dst, &x );\r
1613         break;\r
1614     case 4:\r
1615         /* X/Y flip */\r
1616         x.eM11 = 0;\r
1617         x.eM12 = 1.0;\r
1618         x.eM21 = 1.0;\r
1619         x.eM22 = 0;\r
1620         x.eDx = (FLOAT) dx;\r
1621         x.eDy = (FLOAT) dy;\r
1622         dx = 0;\r
1623         dy = 0;\r
1624         SetWorldTransform( dst, &x );\r
1625         break;\r
1626     }\r
1627 \r
1628     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1629 \r
1630     x.eM11 = 1.0;\r
1631     x.eM12 = 0;\r
1632     x.eM21 = 0;\r
1633     x.eM22 = 1.0;\r
1634     x.eDx = 0;\r
1635     x.eDy = 0;\r
1636     SetWorldTransform( dst, &x );\r
1637 \r
1638     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1639 }\r
1640 \r
1641 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1642 enum {\r
1643     PM_WP = (int) WhitePawn, \r
1644     PM_WN = (int) WhiteKnight, \r
1645     PM_WB = (int) WhiteBishop, \r
1646     PM_WR = (int) WhiteRook, \r
1647     PM_WQ = (int) WhiteQueen, \r
1648     PM_WF = (int) WhiteFerz, \r
1649     PM_WW = (int) WhiteWazir, \r
1650     PM_WE = (int) WhiteAlfil, \r
1651     PM_WM = (int) WhiteMan, \r
1652     PM_WO = (int) WhiteCannon, \r
1653     PM_WU = (int) WhiteUnicorn, \r
1654     PM_WH = (int) WhiteNightrider, \r
1655     PM_WA = (int) WhiteAngel, \r
1656     PM_WC = (int) WhiteMarshall, \r
1657     PM_WAB = (int) WhiteCardinal, \r
1658     PM_WD = (int) WhiteDragon, \r
1659     PM_WL = (int) WhiteLance, \r
1660     PM_WS = (int) WhiteCobra, \r
1661     PM_WV = (int) WhiteFalcon, \r
1662     PM_WSG = (int) WhiteSilver, \r
1663     PM_WG = (int) WhiteGrasshopper, \r
1664     PM_WK = (int) WhiteKing,\r
1665     PM_BP = (int) BlackPawn, \r
1666     PM_BN = (int) BlackKnight, \r
1667     PM_BB = (int) BlackBishop, \r
1668     PM_BR = (int) BlackRook, \r
1669     PM_BQ = (int) BlackQueen, \r
1670     PM_BF = (int) BlackFerz, \r
1671     PM_BW = (int) BlackWazir, \r
1672     PM_BE = (int) BlackAlfil, \r
1673     PM_BM = (int) BlackMan,\r
1674     PM_BO = (int) BlackCannon, \r
1675     PM_BU = (int) BlackUnicorn, \r
1676     PM_BH = (int) BlackNightrider, \r
1677     PM_BA = (int) BlackAngel, \r
1678     PM_BC = (int) BlackMarshall, \r
1679     PM_BG = (int) BlackGrasshopper, \r
1680     PM_BAB = (int) BlackCardinal,\r
1681     PM_BD = (int) BlackDragon,\r
1682     PM_BL = (int) BlackLance,\r
1683     PM_BS = (int) BlackCobra,\r
1684     PM_BV = (int) BlackFalcon,\r
1685     PM_BSG = (int) BlackSilver,\r
1686     PM_BK = (int) BlackKing\r
1687 };\r
1688 \r
1689 static HFONT hPieceFont = NULL;\r
1690 static HBITMAP hPieceMask[(int) EmptySquare];\r
1691 static HBITMAP hPieceFace[(int) EmptySquare];\r
1692 static int fontBitmapSquareSize = 0;\r
1693 static char pieceToFontChar[(int) EmptySquare] =\r
1694                               { 'p', 'n', 'b', 'r', 'q', \r
1695                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1696                       'k', 'o', 'm', 'v', 't', 'w', \r
1697                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1698                                                               'l' };\r
1699 \r
1700 extern BOOL SetCharTable( char *table, const char * map );\r
1701 /* [HGM] moved to backend.c */\r
1702 \r
1703 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1704 {\r
1705     HBRUSH hbrush;\r
1706     BYTE r1 = GetRValue( color );\r
1707     BYTE g1 = GetGValue( color );\r
1708     BYTE b1 = GetBValue( color );\r
1709     BYTE r2 = r1 / 2;\r
1710     BYTE g2 = g1 / 2;\r
1711     BYTE b2 = b1 / 2;\r
1712     RECT rc;\r
1713 \r
1714     /* Create a uniform background first */\r
1715     hbrush = CreateSolidBrush( color );\r
1716     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1717     FillRect( hdc, &rc, hbrush );\r
1718     DeleteObject( hbrush );\r
1719     \r
1720     if( mode == 1 ) {\r
1721         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1722         int steps = squareSize / 2;\r
1723         int i;\r
1724 \r
1725         for( i=0; i<steps; i++ ) {\r
1726             BYTE r = r1 - (r1-r2) * i / steps;\r
1727             BYTE g = g1 - (g1-g2) * i / steps;\r
1728             BYTE b = b1 - (b1-b2) * i / steps;\r
1729 \r
1730             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1731             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1732             FillRect( hdc, &rc, hbrush );\r
1733             DeleteObject(hbrush);\r
1734         }\r
1735     }\r
1736     else if( mode == 2 ) {\r
1737         /* Diagonal gradient, good more or less for every piece */\r
1738         POINT triangle[3];\r
1739         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1740         HBRUSH hbrush_old;\r
1741         int steps = squareSize;\r
1742         int i;\r
1743 \r
1744         triangle[0].x = squareSize - steps;\r
1745         triangle[0].y = squareSize;\r
1746         triangle[1].x = squareSize;\r
1747         triangle[1].y = squareSize;\r
1748         triangle[2].x = squareSize;\r
1749         triangle[2].y = squareSize - steps;\r
1750 \r
1751         for( i=0; i<steps; i++ ) {\r
1752             BYTE r = r1 - (r1-r2) * i / steps;\r
1753             BYTE g = g1 - (g1-g2) * i / steps;\r
1754             BYTE b = b1 - (b1-b2) * i / steps;\r
1755 \r
1756             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1757             hbrush_old = SelectObject( hdc, hbrush );\r
1758             Polygon( hdc, triangle, 3 );\r
1759             SelectObject( hdc, hbrush_old );\r
1760             DeleteObject(hbrush);\r
1761             triangle[0].x++;\r
1762             triangle[2].y++;\r
1763         }\r
1764 \r
1765         SelectObject( hdc, hpen );\r
1766     }\r
1767 }\r
1768 \r
1769 /*\r
1770     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1771     seems to work ok. The main problem here is to find the "inside" of a chess\r
1772     piece: follow the steps as explained below.\r
1773 */\r
1774 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1775 {\r
1776     HBITMAP hbm;\r
1777     HBITMAP hbm_old;\r
1778     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1779     RECT rc;\r
1780     SIZE sz;\r
1781     POINT pt;\r
1782     int backColor = whitePieceColor; \r
1783     int foreColor = blackPieceColor;\r
1784     \r
1785     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1786         backColor = appData.fontBackColorWhite;\r
1787         foreColor = appData.fontForeColorWhite;\r
1788     }\r
1789     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1790         backColor = appData.fontBackColorBlack;\r
1791         foreColor = appData.fontForeColorBlack;\r
1792     }\r
1793 \r
1794     /* Mask */\r
1795     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1796 \r
1797     hbm_old = SelectObject( hdc, hbm );\r
1798 \r
1799     rc.left = 0;\r
1800     rc.top = 0;\r
1801     rc.right = squareSize;\r
1802     rc.bottom = squareSize;\r
1803 \r
1804     /* Step 1: background is now black */\r
1805     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1806 \r
1807     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1808 \r
1809     pt.x = (squareSize - sz.cx) / 2;\r
1810     pt.y = (squareSize - sz.cy) / 2;\r
1811 \r
1812     SetBkMode( hdc, TRANSPARENT );\r
1813     SetTextColor( hdc, chroma );\r
1814     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1815     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1816 \r
1817     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1818     /* Step 3: the area outside the piece is filled with white */\r
1819 //    FloodFill( hdc, 0, 0, chroma );\r
1820     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1821     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1822     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1823     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1824     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1825     /* \r
1826         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1827         but if the start point is not inside the piece we're lost!\r
1828         There should be a better way to do this... if we could create a region or path\r
1829         from the fill operation we would be fine for example.\r
1830     */\r
1831 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1832     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1833 \r
1834     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1835         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1836         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1837 \r
1838         SelectObject( dc2, bm2 );\r
1839         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1840         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1841         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1842         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1843         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1844 \r
1845         DeleteDC( dc2 );\r
1846         DeleteObject( bm2 );\r
1847     }\r
1848 \r
1849     SetTextColor( hdc, 0 );\r
1850     /* \r
1851         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1852         draw the piece again in black for safety.\r
1853     */\r
1854     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1855 \r
1856     SelectObject( hdc, hbm_old );\r
1857 \r
1858     if( hPieceMask[index] != NULL ) {\r
1859         DeleteObject( hPieceMask[index] );\r
1860     }\r
1861 \r
1862     hPieceMask[index] = hbm;\r
1863 \r
1864     /* Face */\r
1865     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1866 \r
1867     SelectObject( hdc, hbm );\r
1868 \r
1869     {\r
1870         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1871         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1872         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1873 \r
1874         SelectObject( dc1, hPieceMask[index] );\r
1875         SelectObject( dc2, bm2 );\r
1876         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1877         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1878         \r
1879         /* \r
1880             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1881             the piece background and deletes (makes transparent) the rest.\r
1882             Thanks to that mask, we are free to paint the background with the greates\r
1883             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1884             We use this, to make gradients and give the pieces a "roundish" look.\r
1885         */\r
1886         SetPieceBackground( hdc, backColor, 2 );\r
1887         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1888 \r
1889         DeleteDC( dc2 );\r
1890         DeleteDC( dc1 );\r
1891         DeleteObject( bm2 );\r
1892     }\r
1893 \r
1894     SetTextColor( hdc, foreColor );\r
1895     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1896 \r
1897     SelectObject( hdc, hbm_old );\r
1898 \r
1899     if( hPieceFace[index] != NULL ) {\r
1900         DeleteObject( hPieceFace[index] );\r
1901     }\r
1902 \r
1903     hPieceFace[index] = hbm;\r
1904 }\r
1905 \r
1906 static int TranslatePieceToFontPiece( int piece )\r
1907 {\r
1908     switch( piece ) {\r
1909     case BlackPawn:\r
1910         return PM_BP;\r
1911     case BlackKnight:\r
1912         return PM_BN;\r
1913     case BlackBishop:\r
1914         return PM_BB;\r
1915     case BlackRook:\r
1916         return PM_BR;\r
1917     case BlackQueen:\r
1918         return PM_BQ;\r
1919     case BlackKing:\r
1920         return PM_BK;\r
1921     case WhitePawn:\r
1922         return PM_WP;\r
1923     case WhiteKnight:\r
1924         return PM_WN;\r
1925     case WhiteBishop:\r
1926         return PM_WB;\r
1927     case WhiteRook:\r
1928         return PM_WR;\r
1929     case WhiteQueen:\r
1930         return PM_WQ;\r
1931     case WhiteKing:\r
1932         return PM_WK;\r
1933 \r
1934     case BlackAngel:\r
1935         return PM_BA;\r
1936     case BlackMarshall:\r
1937         return PM_BC;\r
1938     case BlackFerz:\r
1939         return PM_BF;\r
1940     case BlackNightrider:\r
1941         return PM_BH;\r
1942     case BlackAlfil:\r
1943         return PM_BE;\r
1944     case BlackWazir:\r
1945         return PM_BW;\r
1946     case BlackUnicorn:\r
1947         return PM_BU;\r
1948     case BlackCannon:\r
1949         return PM_BO;\r
1950     case BlackGrasshopper:\r
1951         return PM_BG;\r
1952     case BlackMan:\r
1953         return PM_BM;\r
1954     case BlackSilver:\r
1955         return PM_BSG;\r
1956     case BlackLance:\r
1957         return PM_BL;\r
1958     case BlackFalcon:\r
1959         return PM_BV;\r
1960     case BlackCobra:\r
1961         return PM_BS;\r
1962     case BlackCardinal:\r
1963         return PM_BAB;\r
1964     case BlackDragon:\r
1965         return PM_BD;\r
1966 \r
1967     case WhiteAngel:\r
1968         return PM_WA;\r
1969     case WhiteMarshall:\r
1970         return PM_WC;\r
1971     case WhiteFerz:\r
1972         return PM_WF;\r
1973     case WhiteNightrider:\r
1974         return PM_WH;\r
1975     case WhiteAlfil:\r
1976         return PM_WE;\r
1977     case WhiteWazir:\r
1978         return PM_WW;\r
1979     case WhiteUnicorn:\r
1980         return PM_WU;\r
1981     case WhiteCannon:\r
1982         return PM_WO;\r
1983     case WhiteGrasshopper:\r
1984         return PM_WG;\r
1985     case WhiteMan:\r
1986         return PM_WM;\r
1987     case WhiteSilver:\r
1988         return PM_WSG;\r
1989     case WhiteLance:\r
1990         return PM_WL;\r
1991     case WhiteFalcon:\r
1992         return PM_WV;\r
1993     case WhiteCobra:\r
1994         return PM_WS;\r
1995     case WhiteCardinal:\r
1996         return PM_WAB;\r
1997     case WhiteDragon:\r
1998         return PM_WD;\r
1999     }\r
2000 \r
2001     return 0;\r
2002 }\r
2003 \r
2004 void CreatePiecesFromFont()\r
2005 {\r
2006     LOGFONT lf;\r
2007     HDC hdc_window = NULL;\r
2008     HDC hdc = NULL;\r
2009     HFONT hfont_old;\r
2010     int fontHeight;\r
2011     int i;\r
2012 \r
2013     if( fontBitmapSquareSize < 0 ) {\r
2014         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2015         return;\r
2016     }\r
2017 \r
2018     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2019             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2020         fontBitmapSquareSize = -1;\r
2021         return;\r
2022     }\r
2023 \r
2024     if( fontBitmapSquareSize != squareSize ) {\r
2025         hdc_window = GetDC( hwndMain );\r
2026         hdc = CreateCompatibleDC( hdc_window );\r
2027 \r
2028         if( hPieceFont != NULL ) {\r
2029             DeleteObject( hPieceFont );\r
2030         }\r
2031         else {\r
2032             for( i=0; i<=(int)BlackKing; i++ ) {\r
2033                 hPieceMask[i] = NULL;\r
2034                 hPieceFace[i] = NULL;\r
2035             }\r
2036         }\r
2037 \r
2038         fontHeight = 75;\r
2039 \r
2040         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2041             fontHeight = appData.fontPieceSize;\r
2042         }\r
2043 \r
2044         fontHeight = (fontHeight * squareSize) / 100;\r
2045 \r
2046         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2047         lf.lfWidth = 0;\r
2048         lf.lfEscapement = 0;\r
2049         lf.lfOrientation = 0;\r
2050         lf.lfWeight = FW_NORMAL;\r
2051         lf.lfItalic = 0;\r
2052         lf.lfUnderline = 0;\r
2053         lf.lfStrikeOut = 0;\r
2054         lf.lfCharSet = DEFAULT_CHARSET;\r
2055         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2056         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2057         lf.lfQuality = PROOF_QUALITY;\r
2058         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2059         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2060         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2061 \r
2062         hPieceFont = CreateFontIndirect( &lf );\r
2063 \r
2064         if( hPieceFont == NULL ) {\r
2065             fontBitmapSquareSize = -2;\r
2066         }\r
2067         else {\r
2068             /* Setup font-to-piece character table */\r
2069             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2070                 /* No (or wrong) global settings, try to detect the font */\r
2071                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2072                     /* Alpha */\r
2073                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2074                 }\r
2075                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2076                     /* DiagramTT* family */\r
2077                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2078                 }\r
2079                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2080                     /* Fairy symbols */\r
2081                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2082                 }\r
2083                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2084                     /* Good Companion (Some characters get warped as literal :-( */\r
2085                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2086                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2087                     SetCharTable(pieceToFontChar, s);\r
2088                 }\r
2089                 else {\r
2090                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2091                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2092                 }\r
2093             }\r
2094 \r
2095             /* Create bitmaps */\r
2096             hfont_old = SelectObject( hdc, hPieceFont );\r
2097             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2098                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2099                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2100 \r
2101             SelectObject( hdc, hfont_old );\r
2102 \r
2103             fontBitmapSquareSize = squareSize;\r
2104         }\r
2105     }\r
2106 \r
2107     if( hdc != NULL ) {\r
2108         DeleteDC( hdc );\r
2109     }\r
2110 \r
2111     if( hdc_window != NULL ) {\r
2112         ReleaseDC( hwndMain, hdc_window );\r
2113     }\r
2114 }\r
2115 \r
2116 HBITMAP\r
2117 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2118 {\r
2119   char name[128], buf[MSG_SIZ];\r
2120 \r
2121     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2122   if(appData.pieceDirectory[0]) {\r
2123     HBITMAP res;\r
2124     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2125     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2126     if(res) return res;\r
2127   }\r
2128   if (gameInfo.event &&\r
2129       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2130       strcmp(name, "k80s") == 0) {\r
2131     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2132   }\r
2133   return LoadBitmap(hinst, name);\r
2134 }\r
2135 \r
2136 \r
2137 /* Insert a color into the program's logical palette\r
2138    structure.  This code assumes the given color is\r
2139    the result of the RGB or PALETTERGB macro, and it\r
2140    knows how those macros work (which is documented).\r
2141 */\r
2142 VOID\r
2143 InsertInPalette(COLORREF color)\r
2144 {\r
2145   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2146 \r
2147   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2148     DisplayFatalError(_("Too many colors"), 0, 1);\r
2149     pLogPal->palNumEntries--;\r
2150     return;\r
2151   }\r
2152 \r
2153   pe->peFlags = (char) 0;\r
2154   pe->peRed = (char) (0xFF & color);\r
2155   pe->peGreen = (char) (0xFF & (color >> 8));\r
2156   pe->peBlue = (char) (0xFF & (color >> 16));\r
2157   return;\r
2158 }\r
2159 \r
2160 \r
2161 VOID\r
2162 InitDrawingColors()\r
2163 {\r
2164   if (pLogPal == NULL) {\r
2165     /* Allocate enough memory for a logical palette with\r
2166      * PALETTESIZE entries and set the size and version fields\r
2167      * of the logical palette structure.\r
2168      */\r
2169     pLogPal = (NPLOGPALETTE)\r
2170       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2171                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2172     pLogPal->palVersion    = 0x300;\r
2173   }\r
2174   pLogPal->palNumEntries = 0;\r
2175 \r
2176   InsertInPalette(lightSquareColor);\r
2177   InsertInPalette(darkSquareColor);\r
2178   InsertInPalette(whitePieceColor);\r
2179   InsertInPalette(blackPieceColor);\r
2180   InsertInPalette(highlightSquareColor);\r
2181   InsertInPalette(premoveHighlightColor);\r
2182 \r
2183   /*  create a logical color palette according the information\r
2184    *  in the LOGPALETTE structure.\r
2185    */\r
2186   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2187 \r
2188   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2189   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2190   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2191   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2192   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2193   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2194   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2195   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2196   /* [AS] Force rendering of the font-based pieces */\r
2197   if( fontBitmapSquareSize > 0 ) {\r
2198     fontBitmapSquareSize = 0;\r
2199   }\r
2200 }\r
2201 \r
2202 \r
2203 int\r
2204 BoardWidth(int boardSize, int n)\r
2205 { /* [HGM] argument n added to allow different width and height */\r
2206   int lineGap = sizeInfo[boardSize].lineGap;\r
2207 \r
2208   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2209       lineGap = appData.overrideLineGap;\r
2210   }\r
2211 \r
2212   return (n + 1) * lineGap +\r
2213           n * sizeInfo[boardSize].squareSize;\r
2214 }\r
2215 \r
2216 /* Respond to board resize by dragging edge */\r
2217 VOID\r
2218 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2219 {\r
2220   BoardSize newSize = NUM_SIZES - 1;\r
2221   static int recurse = 0;\r
2222   if (IsIconic(hwndMain)) return;\r
2223   if (recurse > 0) return;\r
2224   recurse++;\r
2225   while (newSize > 0) {\r
2226         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2227         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2228            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2229     newSize--;\r
2230   } \r
2231   boardSize = newSize;\r
2232   InitDrawingSizes(boardSize, flags);\r
2233   recurse--;\r
2234 }\r
2235 \r
2236 \r
2237 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2238 \r
2239 VOID\r
2240 InitDrawingSizes(BoardSize boardSize, int flags)\r
2241 {\r
2242   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2243   ChessSquare piece;\r
2244   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2245   HDC hdc;\r
2246   SIZE clockSize, messageSize;\r
2247   HFONT oldFont;\r
2248   char buf[MSG_SIZ];\r
2249   char *str;\r
2250   HMENU hmenu = GetMenu(hwndMain);\r
2251   RECT crect, wrect, oldRect;\r
2252   int offby;\r
2253   LOGBRUSH logbrush;\r
2254   VariantClass v = gameInfo.variant;\r
2255 \r
2256   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2257   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2258 \r
2259   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2260   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2261   oldBoardSize = boardSize;\r
2262 \r
2263   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2264   { // correct board size to one where built-in pieces exist\r
2265     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2266        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2267       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2268       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2269       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2270       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2271       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2272                                    boardSize = SizeMiddling;\r
2273     }\r
2274   }\r
2275   if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2276 \r
2277   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2278   oldRect.top = wpMain.y;\r
2279   oldRect.right = wpMain.x + wpMain.width;\r
2280   oldRect.bottom = wpMain.y + wpMain.height;\r
2281 \r
2282   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2283   smallLayout = sizeInfo[boardSize].smallLayout;\r
2284   squareSize = sizeInfo[boardSize].squareSize;\r
2285   lineGap = sizeInfo[boardSize].lineGap;\r
2286   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2287   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2288 \r
2289   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2290       lineGap = appData.overrideLineGap;\r
2291   }\r
2292 \r
2293   if (tinyLayout != oldTinyLayout) {\r
2294     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2295     if (tinyLayout) {\r
2296       style &= ~WS_SYSMENU;\r
2297       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2298                  "&Minimize\tCtrl+F4");\r
2299     } else {\r
2300       style |= WS_SYSMENU;\r
2301       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2302     }\r
2303     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2304 \r
2305     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2306       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2307         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2308     }\r
2309     DrawMenuBar(hwndMain);\r
2310   }\r
2311 \r
2312   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2313   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2314 \r
2315   /* Get text area sizes */\r
2316   hdc = GetDC(hwndMain);\r
2317   if (appData.clockMode) {\r
2318     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2319   } else {\r
2320     snprintf(buf, MSG_SIZ, _("White"));\r
2321   }\r
2322   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2323   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2324   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2325   str = _("We only care about the height here");\r
2326   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2327   SelectObject(hdc, oldFont);\r
2328   ReleaseDC(hwndMain, hdc);\r
2329 \r
2330   /* Compute where everything goes */\r
2331   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2332         /* [HGM] logo: if either logo is on, reserve space for it */\r
2333         logoHeight =  2*clockSize.cy;\r
2334         leftLogoRect.left   = OUTER_MARGIN;\r
2335         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2336         leftLogoRect.top    = OUTER_MARGIN;\r
2337         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2338 \r
2339         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2340         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2341         rightLogoRect.top    = OUTER_MARGIN;\r
2342         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2343 \r
2344 \r
2345     whiteRect.left = leftLogoRect.right;\r
2346     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2347     whiteRect.top = OUTER_MARGIN;\r
2348     whiteRect.bottom = whiteRect.top + logoHeight;\r
2349 \r
2350     blackRect.right = rightLogoRect.left;\r
2351     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2352     blackRect.top = whiteRect.top;\r
2353     blackRect.bottom = whiteRect.bottom;\r
2354   } else {\r
2355     whiteRect.left = OUTER_MARGIN;\r
2356     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2357     whiteRect.top = OUTER_MARGIN;\r
2358     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2359 \r
2360     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2361     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2362     blackRect.top = whiteRect.top;\r
2363     blackRect.bottom = whiteRect.bottom;\r
2364 \r
2365     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2366   }\r
2367 \r
2368   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2369   if (appData.showButtonBar) {\r
2370     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2371       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2372   } else {\r
2373     messageRect.right = OUTER_MARGIN + boardWidth;\r
2374   }\r
2375   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2376   messageRect.bottom = messageRect.top + messageSize.cy;\r
2377 \r
2378   boardRect.left = OUTER_MARGIN;\r
2379   boardRect.right = boardRect.left + boardWidth;\r
2380   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2381   boardRect.bottom = boardRect.top + boardHeight;\r
2382 \r
2383   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2384   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2385   oldTinyLayout = tinyLayout;\r
2386   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2387   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2388     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2389   winW *= 1 + twoBoards;\r
2390   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2391   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2392   wpMain.height = winH; //       without disturbing window attachments\r
2393   GetWindowRect(hwndMain, &wrect);\r
2394   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2395                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2396 \r
2397   // [HGM] placement: let attached windows follow size change.\r
2398   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2399   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2400   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2401   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2402   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2403 \r
2404   /* compensate if menu bar wrapped */\r
2405   GetClientRect(hwndMain, &crect);\r
2406   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2407   wpMain.height += offby;\r
2408   switch (flags) {\r
2409   case WMSZ_TOPLEFT:\r
2410     SetWindowPos(hwndMain, NULL, \r
2411                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2412                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2413     break;\r
2414 \r
2415   case WMSZ_TOPRIGHT:\r
2416   case WMSZ_TOP:\r
2417     SetWindowPos(hwndMain, NULL, \r
2418                  wrect.left, wrect.bottom - wpMain.height, \r
2419                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2420     break;\r
2421 \r
2422   case WMSZ_BOTTOMLEFT:\r
2423   case WMSZ_LEFT:\r
2424     SetWindowPos(hwndMain, NULL, \r
2425                  wrect.right - wpMain.width, wrect.top, \r
2426                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2427     break;\r
2428 \r
2429   case WMSZ_BOTTOMRIGHT:\r
2430   case WMSZ_BOTTOM:\r
2431   case WMSZ_RIGHT:\r
2432   default:\r
2433     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2434                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2435     break;\r
2436   }\r
2437 \r
2438   hwndPause = NULL;\r
2439   for (i = 0; i < N_BUTTONS; i++) {\r
2440     if (buttonDesc[i].hwnd != NULL) {\r
2441       DestroyWindow(buttonDesc[i].hwnd);\r
2442       buttonDesc[i].hwnd = NULL;\r
2443     }\r
2444     if (appData.showButtonBar) {\r
2445       buttonDesc[i].hwnd =\r
2446         CreateWindow("BUTTON", buttonDesc[i].label,\r
2447                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2448                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2449                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2450                      (HMENU) buttonDesc[i].id,\r
2451                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2452       if (tinyLayout) {\r
2453         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2454                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2455                     MAKELPARAM(FALSE, 0));\r
2456       }\r
2457       if (buttonDesc[i].id == IDM_Pause)\r
2458         hwndPause = buttonDesc[i].hwnd;\r
2459       buttonDesc[i].wndproc = (WNDPROC)\r
2460         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2461     }\r
2462   }\r
2463   if (gridPen != NULL) DeleteObject(gridPen);\r
2464   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2465   if (premovePen != NULL) DeleteObject(premovePen);\r
2466   if (lineGap != 0) {\r
2467     logbrush.lbStyle = BS_SOLID;\r
2468     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2469     gridPen =\r
2470       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2471                    lineGap, &logbrush, 0, NULL);\r
2472     logbrush.lbColor = highlightSquareColor;\r
2473     highlightPen =\r
2474       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2475                    lineGap, &logbrush, 0, NULL);\r
2476 \r
2477     logbrush.lbColor = premoveHighlightColor; \r
2478     premovePen =\r
2479       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2480                    lineGap, &logbrush, 0, NULL);\r
2481 \r
2482     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2483     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2484       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2485       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2486         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2487       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2488         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2489       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2490     }\r
2491     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2492       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2493       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2494         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2495         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2496       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2497         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2498       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2499     }\r
2500   }\r
2501 \r
2502   /* [HGM] Licensing requirement */\r
2503 #ifdef GOTHIC\r
2504   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2505 #endif\r
2506 #ifdef FALCON\r
2507   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2508 #endif\r
2509   GothicPopUp( "", VariantNormal);\r
2510 \r
2511 \r
2512 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2513 \r
2514   /* Load piece bitmaps for this board size */\r
2515   for (i=0; i<=2; i++) {\r
2516     for (piece = WhitePawn;\r
2517          (int) piece < (int) BlackPawn;\r
2518          piece = (ChessSquare) ((int) piece + 1)) {\r
2519       if (pieceBitmap[i][piece] != NULL)\r
2520         DeleteObject(pieceBitmap[i][piece]);\r
2521     }\r
2522   }\r
2523 \r
2524   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2525   // Orthodox Chess pieces\r
2526   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2527   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2528   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2529   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2530   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2531   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2532   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2533   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2534   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2535   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2536   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2537   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2538   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2539   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2540   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2541   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2542     // in Shogi, Hijack the unused Queen for Lance\r
2543     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2544     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2545     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2546   } else {\r
2547     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2548     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2549     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2550   }\r
2551 \r
2552   if(squareSize <= 72 && squareSize >= 33) { \r
2553     /* A & C are available in most sizes now */\r
2554     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2555       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2556       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2557       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2558       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2559       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2560       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2561       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2562       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2563       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2564       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2565       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2566       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2567     } else { // Smirf-like\r
2568       if(gameInfo.variant == VariantSChess) {\r
2569         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2570         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2571         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2572       } else {\r
2573         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2574         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2575         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2576       }\r
2577     }\r
2578     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2579       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2580       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2581       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2582     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2583       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2584       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2585       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2586     } else { // WinBoard standard\r
2587       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2588       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2589       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2590     }\r
2591   }\r
2592 \r
2593 \r
2594   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2595     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2596     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2597     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2598     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2599     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2600     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2601     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2602     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2603     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2604     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2605     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2606     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2607     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2608     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2609     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2610     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2611     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2612     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2613     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2614     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2615     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2616     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2617     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2618     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2619     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2620     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2621     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2622     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2623     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2624     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2625 \r
2626     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2627       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2628       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2629       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2630       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2631       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2632       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2633       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2634       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2635       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2636       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2637       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2638       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2639     } else {\r
2640       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2641       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2642       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2643       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2644       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2645       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2646       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2647       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2648       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2649       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2650       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2651       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2652     }\r
2653 \r
2654   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2655     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2656     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2657     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2658     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2659     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2660     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2661     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2662     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2663     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2664     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2665     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2666     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2667     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2668     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2669   }\r
2670 \r
2671 \r
2672   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2673   /* special Shogi support in this size */\r
2674   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2675       for (piece = WhitePawn;\r
2676            (int) piece < (int) BlackPawn;\r
2677            piece = (ChessSquare) ((int) piece + 1)) {\r
2678         if (pieceBitmap[i][piece] != NULL)\r
2679           DeleteObject(pieceBitmap[i][piece]);\r
2680       }\r
2681     }\r
2682   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2683   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2684   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2685   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2686   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2687   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2688   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2689   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2690   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2691   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2692   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2693   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2694   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2695   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2696   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2697   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2698   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2699   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2700   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2701   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2702   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2703   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2704   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2705   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2706   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2707   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2708   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2709   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2710   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2711   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2712   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2713   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2714   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2715   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2716   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2717   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2718   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2719   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2720   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2721   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2722   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2723   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2724   minorSize = 0;\r
2725   }\r
2726 }\r
2727 \r
2728 HBITMAP\r
2729 PieceBitmap(ChessSquare p, int kind)\r
2730 {\r
2731   if ((int) p >= (int) BlackPawn)\r
2732     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2733 \r
2734   return pieceBitmap[kind][(int) p];\r
2735 }\r
2736 \r
2737 /***************************************************************/\r
2738 \r
2739 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2740 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2741 /*\r
2742 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2743 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2744 */\r
2745 \r
2746 VOID\r
2747 SquareToPos(int row, int column, int * x, int * y)\r
2748 {\r
2749   if (flipView) {\r
2750     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2751     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2752   } else {\r
2753     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2754     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2755   }\r
2756 }\r
2757 \r
2758 VOID\r
2759 DrawCoordsOnDC(HDC hdc)\r
2760 {\r
2761   static char files[] = "0123456789012345678901221098765432109876543210";\r
2762   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2763   char str[2] = { NULLCHAR, NULLCHAR };\r
2764   int oldMode, oldAlign, x, y, start, i;\r
2765   HFONT oldFont;\r
2766   HBRUSH oldBrush;\r
2767 \r
2768   if (!appData.showCoords)\r
2769     return;\r
2770 \r
2771   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2772 \r
2773   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2774   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2775   oldAlign = GetTextAlign(hdc);\r
2776   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2777 \r
2778   y = boardRect.top + lineGap;\r
2779   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2780 \r
2781   if(border) {\r
2782     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2783     x += border - lineGap - 4; y += squareSize - 6;\r
2784   } else\r
2785   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2786   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2787     str[0] = files[start + i];\r
2788     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2789     y += squareSize + lineGap;\r
2790   }\r
2791 \r
2792   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2793 \r
2794   if(border) {\r
2795     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2796     x += -border + 4; y += border - squareSize + 6;\r
2797   } else\r
2798   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2799   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2800     str[0] = ranks[start + i];\r
2801     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2802     x += squareSize + lineGap;\r
2803   }    \r
2804 \r
2805   SelectObject(hdc, oldBrush);\r
2806   SetBkMode(hdc, oldMode);\r
2807   SetTextAlign(hdc, oldAlign);\r
2808   SelectObject(hdc, oldFont);\r
2809 }\r
2810 \r
2811 VOID\r
2812 DrawGridOnDC(HDC hdc)\r
2813 {\r
2814   HPEN oldPen;\r
2815  \r
2816   if (lineGap != 0) {\r
2817     oldPen = SelectObject(hdc, gridPen);\r
2818     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2819     SelectObject(hdc, oldPen);\r
2820   }\r
2821 }\r
2822 \r
2823 #define HIGHLIGHT_PEN 0\r
2824 #define PREMOVE_PEN   1\r
2825 \r
2826 VOID\r
2827 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2828 {\r
2829   int x1, y1;\r
2830   HPEN oldPen, hPen;\r
2831   if (lineGap == 0) return;\r
2832   if (flipView) {\r
2833     x1 = boardRect.left +\r
2834       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2835     y1 = boardRect.top +\r
2836       lineGap/2 + y * (squareSize + lineGap) + border;\r
2837   } else {\r
2838     x1 = boardRect.left +\r
2839       lineGap/2 + x * (squareSize + lineGap) + border;\r
2840     y1 = boardRect.top +\r
2841       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2842   }\r
2843   hPen = pen ? premovePen : highlightPen;\r
2844   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2845   MoveToEx(hdc, x1, y1, NULL);\r
2846   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2847   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2848   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2849   LineTo(hdc, x1, y1);\r
2850   SelectObject(hdc, oldPen);\r
2851 }\r
2852 \r
2853 VOID\r
2854 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2855 {\r
2856   int i;\r
2857   for (i=0; i<2; i++) {\r
2858     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2859       DrawHighlightOnDC(hdc, TRUE,\r
2860                         h->sq[i].x, h->sq[i].y,\r
2861                         pen);\r
2862   }\r
2863 }\r
2864 \r
2865 /* Note: sqcolor is used only in monoMode */\r
2866 /* Note that this code is largely duplicated in woptions.c,\r
2867    function DrawSampleSquare, so that needs to be updated too */\r
2868 VOID\r
2869 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2870 {\r
2871   HBITMAP oldBitmap;\r
2872   HBRUSH oldBrush;\r
2873   int tmpSize;\r
2874 \r
2875   if (appData.blindfold) return;\r
2876 \r
2877   /* [AS] Use font-based pieces if needed */\r
2878   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2879     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2880     CreatePiecesFromFont();\r
2881 \r
2882     if( fontBitmapSquareSize == squareSize ) {\r
2883         int index = TranslatePieceToFontPiece(piece);\r
2884 \r
2885         SelectObject( tmphdc, hPieceMask[ index ] );\r
2886 \r
2887       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2888         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2889       else\r
2890         BitBlt( hdc,\r
2891             x, y,\r
2892             squareSize, squareSize,\r
2893             tmphdc,\r
2894             0, 0,\r
2895             SRCAND );\r
2896 \r
2897         SelectObject( tmphdc, hPieceFace[ index ] );\r
2898 \r
2899       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2900         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2901       else\r
2902         BitBlt( hdc,\r
2903             x, y,\r
2904             squareSize, squareSize,\r
2905             tmphdc,\r
2906             0, 0,\r
2907             SRCPAINT );\r
2908 \r
2909         return;\r
2910     }\r
2911   }\r
2912 \r
2913   if (appData.monoMode) {\r
2914     SelectObject(tmphdc, PieceBitmap(piece, \r
2915       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2916     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2917            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2918   } else {\r
2919     HBRUSH xBrush = whitePieceBrush;\r
2920     tmpSize = squareSize;\r
2921     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2922     if(minorSize &&\r
2923         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2924          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2925       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2926       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2927       x += (squareSize - minorSize)>>1;\r
2928       y += squareSize - minorSize - 2;\r
2929       tmpSize = minorSize;\r
2930     }\r
2931     if (color || appData.allWhite ) {\r
2932       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2933       if( color )\r
2934               oldBrush = SelectObject(hdc, xBrush);\r
2935       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2936       if(appData.upsideDown && color==flipView)\r
2937         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2938       else\r
2939         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2940       /* Use black for outline of white pieces */\r
2941       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2942       if(appData.upsideDown && color==flipView)\r
2943         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2944       else\r
2945         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2946     } else if(appData.pieceDirectory[0]) {\r
2947       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2948       oldBrush = SelectObject(hdc, xBrush);\r
2949       if(appData.upsideDown && color==flipView)\r
2950         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2951       else\r
2952         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2953       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2954       if(appData.upsideDown && color==flipView)\r
2955         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2956       else\r
2957         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2958     } else {\r
2959       /* Use square color for details of black pieces */\r
2960       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2961       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2962       if(appData.upsideDown && !flipView)\r
2963         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2964       else\r
2965         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2966     }\r
2967     SelectObject(hdc, oldBrush);\r
2968     SelectObject(tmphdc, oldBitmap);\r
2969   }\r
2970 }\r
2971 \r
2972 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2973 int GetBackTextureMode( int algo )\r
2974 {\r
2975     int result = BACK_TEXTURE_MODE_DISABLED;\r
2976 \r
2977     switch( algo ) \r
2978     {\r
2979         case BACK_TEXTURE_MODE_PLAIN:\r
2980             result = 1; /* Always use identity map */\r
2981             break;\r
2982         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2983             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2984             break;\r
2985     }\r
2986 \r
2987     return result;\r
2988 }\r
2989 \r
2990 /* \r
2991     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2992     to handle redraws cleanly (as random numbers would always be different).\r
2993 */\r
2994 VOID RebuildTextureSquareInfo()\r
2995 {\r
2996     BITMAP bi;\r
2997     int lite_w = 0;\r
2998     int lite_h = 0;\r
2999     int dark_w = 0;\r
3000     int dark_h = 0;\r
3001     int row;\r
3002     int col;\r
3003 \r
3004     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3005 \r
3006     if( liteBackTexture != NULL ) {\r
3007         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3008             lite_w = bi.bmWidth;\r
3009             lite_h = bi.bmHeight;\r
3010         }\r
3011     }\r
3012 \r
3013     if( darkBackTexture != NULL ) {\r
3014         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3015             dark_w = bi.bmWidth;\r
3016             dark_h = bi.bmHeight;\r
3017         }\r
3018     }\r
3019 \r
3020     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3021         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3022             if( (col + row) & 1 ) {\r
3023                 /* Lite square */\r
3024                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3025                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3026                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3027                   else\r
3028                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3029                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3030                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3031                   else\r
3032                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3033                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3034                 }\r
3035             }\r
3036             else {\r
3037                 /* Dark square */\r
3038                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3039                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3040                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3041                   else\r
3042                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3043                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3044                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3045                   else\r
3046                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3047                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3048                 }\r
3049             }\r
3050         }\r
3051     }\r
3052 }\r
3053 \r
3054 /* [AS] Arrow highlighting support */\r
3055 \r
3056 static double A_WIDTH = 5; /* Width of arrow body */\r
3057 \r
3058 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3059 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3060 \r
3061 static double Sqr( double x )\r
3062 {\r
3063     return x*x;\r
3064 }\r
3065 \r
3066 static int Round( double x )\r
3067 {\r
3068     return (int) (x + 0.5);\r
3069 }\r
3070 \r
3071 /* Draw an arrow between two points using current settings */\r
3072 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3073 {\r
3074     POINT arrow[7];\r
3075     double dx, dy, j, k, x, y;\r
3076 \r
3077     if( d_x == s_x ) {\r
3078         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3079 \r
3080         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3081         arrow[0].y = s_y;\r
3082 \r
3083         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3084         arrow[1].y = d_y - h;\r
3085 \r
3086         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3087         arrow[2].y = d_y - h;\r
3088 \r
3089         arrow[3].x = d_x;\r
3090         arrow[3].y = d_y;\r
3091 \r
3092         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3093         arrow[5].y = d_y - h;\r
3094 \r
3095         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3096         arrow[4].y = d_y - h;\r
3097 \r
3098         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3099         arrow[6].y = s_y;\r
3100     }\r
3101     else if( d_y == s_y ) {\r
3102         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3103 \r
3104         arrow[0].x = s_x;\r
3105         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3106 \r
3107         arrow[1].x = d_x - w;\r
3108         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3109 \r
3110         arrow[2].x = d_x - w;\r
3111         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3112 \r
3113         arrow[3].x = d_x;\r
3114         arrow[3].y = d_y;\r
3115 \r
3116         arrow[5].x = d_x - w;\r
3117         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3118 \r
3119         arrow[4].x = d_x - w;\r
3120         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3121 \r
3122         arrow[6].x = s_x;\r
3123         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3124     }\r
3125     else {\r
3126         /* [AS] Needed a lot of paper for this! :-) */\r
3127         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3128         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3129   \r
3130         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3131 \r
3132         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3133 \r
3134         x = s_x;\r
3135         y = s_y;\r
3136 \r
3137         arrow[0].x = Round(x - j);\r
3138         arrow[0].y = Round(y + j*dx);\r
3139 \r
3140         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3141         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3142 \r
3143         if( d_x > s_x ) {\r
3144             x = (double) d_x - k;\r
3145             y = (double) d_y - k*dy;\r
3146         }\r
3147         else {\r
3148             x = (double) d_x + k;\r
3149             y = (double) d_y + k*dy;\r
3150         }\r
3151 \r
3152         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3153 \r
3154         arrow[6].x = Round(x - j);\r
3155         arrow[6].y = Round(y + j*dx);\r
3156 \r
3157         arrow[2].x = Round(arrow[6].x + 2*j);\r
3158         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3159 \r
3160         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3161         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3162 \r
3163         arrow[4].x = d_x;\r
3164         arrow[4].y = d_y;\r
3165 \r
3166         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3167         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3168     }\r
3169 \r
3170     Polygon( hdc, arrow, 7 );\r
3171 }\r
3172 \r
3173 /* [AS] Draw an arrow between two squares */\r
3174 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3175 {\r
3176     int s_x, s_y, d_x, d_y;\r
3177     HPEN hpen;\r
3178     HPEN holdpen;\r
3179     HBRUSH hbrush;\r
3180     HBRUSH holdbrush;\r
3181     LOGBRUSH stLB;\r
3182 \r
3183     if( s_col == d_col && s_row == d_row ) {\r
3184         return;\r
3185     }\r
3186 \r
3187     /* Get source and destination points */\r
3188     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3189     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3190 \r
3191     if( d_y > s_y ) {\r
3192         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3193     }\r
3194     else if( d_y < s_y ) {\r
3195         d_y += squareSize / 2 + squareSize / 4;\r
3196     }\r
3197     else {\r
3198         d_y += squareSize / 2;\r
3199     }\r
3200 \r
3201     if( d_x > s_x ) {\r
3202         d_x += squareSize / 2 - squareSize / 4;\r
3203     }\r
3204     else if( d_x < s_x ) {\r
3205         d_x += squareSize / 2 + squareSize / 4;\r
3206     }\r
3207     else {\r
3208         d_x += squareSize / 2;\r
3209     }\r
3210 \r
3211     s_x += squareSize / 2;\r
3212     s_y += squareSize / 2;\r
3213 \r
3214     /* Adjust width */\r
3215     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3216 \r
3217     /* Draw */\r
3218     stLB.lbStyle = BS_SOLID;\r
3219     stLB.lbColor = appData.highlightArrowColor;\r
3220     stLB.lbHatch = 0;\r
3221 \r
3222     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3223     holdpen = SelectObject( hdc, hpen );\r
3224     hbrush = CreateBrushIndirect( &stLB );\r
3225     holdbrush = SelectObject( hdc, hbrush );\r
3226 \r
3227     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3228 \r
3229     SelectObject( hdc, holdpen );\r
3230     SelectObject( hdc, holdbrush );\r
3231     DeleteObject( hpen );\r
3232     DeleteObject( hbrush );\r
3233 }\r
3234 \r
3235 BOOL HasHighlightInfo()\r
3236 {\r
3237     BOOL result = FALSE;\r
3238 \r
3239     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3240         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3241     {\r
3242         result = TRUE;\r
3243     }\r
3244 \r
3245     return result;\r
3246 }\r
3247 \r
3248 BOOL IsDrawArrowEnabled()\r
3249 {\r
3250     BOOL result = FALSE;\r
3251 \r
3252     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3253         result = TRUE;\r
3254     }\r
3255 \r
3256     return result;\r
3257 }\r
3258 \r
3259 VOID DrawArrowHighlight( HDC hdc )\r
3260 {\r
3261     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3262         DrawArrowBetweenSquares( hdc,\r
3263             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3264             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3265     }\r
3266 }\r
3267 \r
3268 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3269 {\r
3270     HRGN result = NULL;\r
3271 \r
3272     if( HasHighlightInfo() ) {\r
3273         int x1, y1, x2, y2;\r
3274         int sx, sy, dx, dy;\r
3275 \r
3276         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3277         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3278 \r
3279         sx = MIN( x1, x2 );\r
3280         sy = MIN( y1, y2 );\r
3281         dx = MAX( x1, x2 ) + squareSize;\r
3282         dy = MAX( y1, y2 ) + squareSize;\r
3283 \r
3284         result = CreateRectRgn( sx, sy, dx, dy );\r
3285     }\r
3286 \r
3287     return result;\r
3288 }\r
3289 \r
3290 /*\r
3291     Warning: this function modifies the behavior of several other functions. \r
3292     \r
3293     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3294     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3295     repaint is scattered all over the place, which is not good for features such as\r
3296     "arrow highlighting" that require a full repaint of the board.\r
3297 \r
3298     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3299     user interaction, when speed is not so important) but especially to avoid errors\r
3300     in the displayed graphics.\r
3301 \r
3302     In such patched places, I always try refer to this function so there is a single\r
3303     place to maintain knowledge.\r
3304     \r
3305     To restore the original behavior, just return FALSE unconditionally.\r
3306 */\r
3307 BOOL IsFullRepaintPreferrable()\r
3308 {\r
3309     BOOL result = FALSE;\r
3310 \r
3311     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3312         /* Arrow may appear on the board */\r
3313         result = TRUE;\r
3314     }\r
3315 \r
3316     return result;\r
3317 }\r
3318 \r
3319 /* \r
3320     This function is called by DrawPosition to know whether a full repaint must\r
3321     be forced or not.\r
3322 \r
3323     Only DrawPosition may directly call this function, which makes use of \r
3324     some state information. Other function should call DrawPosition specifying \r
3325     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3326 */\r
3327 BOOL DrawPositionNeedsFullRepaint()\r
3328 {\r
3329     BOOL result = FALSE;\r
3330 \r
3331     /* \r
3332         Probably a slightly better policy would be to trigger a full repaint\r
3333         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3334         but animation is fast enough that it's difficult to notice.\r
3335     */\r
3336     if( animInfo.piece == EmptySquare ) {\r
3337         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3338             result = TRUE;\r
3339         }\r
3340     }\r
3341 \r
3342     return result;\r
3343 }\r
3344 \r
3345 static HBITMAP borderBitmap;\r
3346 \r
3347 VOID\r
3348 DrawBackgroundOnDC(HDC hdc)\r
3349 {\r
3350   \r
3351   BITMAP bi;\r
3352   HDC tmphdc;\r
3353   HBITMAP hbm;\r
3354   static char oldBorder[MSG_SIZ];\r
3355   int w = 600, h = 600, mode;\r
3356 \r
3357   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3358     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3359     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3360   }\r
3361   if(borderBitmap == NULL) { // loading failed, use white\r
3362     FillRect( hdc, &boardRect, whitePieceBrush );\r
3363     return;\r
3364   }\r
3365   tmphdc = CreateCompatibleDC(hdc);\r
3366   hbm = SelectObject(tmphdc, borderBitmap);\r
3367   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3368             w = bi.bmWidth;\r
3369             h = bi.bmHeight;\r
3370   }\r
3371   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3372   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3373                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3374   SetStretchBltMode(hdc, mode);\r
3375   SelectObject(tmphdc, hbm);\r
3376   DeleteDC(tmphdc);\r
3377 }\r
3378 \r
3379 VOID\r
3380 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3381 {\r
3382   int row, column, x, y, square_color, piece_color;\r
3383   ChessSquare piece;\r
3384   HBRUSH oldBrush;\r
3385   HDC texture_hdc = NULL;\r
3386 \r
3387   /* [AS] Initialize background textures if needed */\r
3388   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3389       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3390       if( backTextureSquareSize != squareSize \r
3391        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3392           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3393           backTextureSquareSize = squareSize;\r
3394           RebuildTextureSquareInfo();\r
3395       }\r
3396 \r
3397       texture_hdc = CreateCompatibleDC( hdc );\r
3398   }\r
3399 \r
3400   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3401     for (column = 0; column < BOARD_WIDTH; column++) {\r
3402   \r
3403       SquareToPos(row, column, &x, &y);\r
3404 \r
3405       piece = board[row][column];\r
3406 \r
3407       square_color = ((column + row) % 2) == 1;\r
3408       if( gameInfo.variant == VariantXiangqi ) {\r
3409           square_color = !InPalace(row, column);\r
3410           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3411           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3412       }\r
3413       piece_color = (int) piece < (int) BlackPawn;\r
3414 \r
3415 \r
3416       /* [HGM] holdings file: light square or black */\r
3417       if(column == BOARD_LEFT-2) {\r
3418             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3419                 square_color = 1;\r
3420             else {\r
3421                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3422                 continue;\r
3423             }\r
3424       } else\r
3425       if(column == BOARD_RGHT + 1 ) {\r
3426             if( row < gameInfo.holdingsSize )\r
3427                 square_color = 1;\r
3428             else {\r
3429                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3430                 continue;\r
3431             }\r
3432       }\r
3433       if(column == BOARD_LEFT-1 ) /* left align */\r
3434             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3435       else if( column == BOARD_RGHT) /* right align */\r
3436             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3437       else\r
3438       if (appData.monoMode) {\r
3439         if (piece == EmptySquare) {\r
3440           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3441                  square_color ? WHITENESS : BLACKNESS);\r
3442         } else {\r
3443           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3444         }\r
3445       } \r
3446       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3447           /* [AS] Draw the square using a texture bitmap */\r
3448           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3449           int r = row, c = column; // [HGM] do not flip board in flipView\r
3450           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3451 \r
3452           DrawTile( x, y, \r
3453               squareSize, squareSize, \r
3454               hdc, \r
3455               texture_hdc,\r
3456               backTextureSquareInfo[r][c].mode,\r
3457               backTextureSquareInfo[r][c].x,\r
3458               backTextureSquareInfo[r][c].y );\r
3459 \r
3460           SelectObject( texture_hdc, hbm );\r
3461 \r
3462           if (piece != EmptySquare) {\r
3463               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3464           }\r
3465       }\r
3466       else {\r
3467         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3468 \r
3469         oldBrush = SelectObject(hdc, brush );\r
3470         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3471         SelectObject(hdc, oldBrush);\r
3472         if (piece != EmptySquare)\r
3473           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3474       }\r
3475     }\r
3476   }\r
3477 \r
3478   if( texture_hdc != NULL ) {\r
3479     DeleteDC( texture_hdc );\r
3480   }\r
3481 }\r
3482 \r
3483 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3484 void fputDW(FILE *f, int x)\r
3485 {\r
3486         fputc(x     & 255, f);\r
3487         fputc(x>>8  & 255, f);\r
3488         fputc(x>>16 & 255, f);\r
3489         fputc(x>>24 & 255, f);\r
3490 }\r
3491 \r
3492 #define MAX_CLIPS 200   /* more than enough */\r
3493 \r
3494 VOID\r
3495 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3496 {\r
3497 //  HBITMAP bufferBitmap;\r
3498   BITMAP bi;\r
3499 //  RECT Rect;\r
3500   HDC tmphdc;\r
3501   HBITMAP hbm;\r
3502   int w = 100, h = 50;\r
3503 \r
3504   if(logo == NULL) {\r
3505     if(!logoHeight) return;\r
3506     FillRect( hdc, &logoRect, whitePieceBrush );\r
3507   }\r
3508 //  GetClientRect(hwndMain, &Rect);\r
3509 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3510 //                                      Rect.bottom-Rect.top+1);\r
3511   tmphdc = CreateCompatibleDC(hdc);\r
3512   hbm = SelectObject(tmphdc, logo);\r
3513   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3514             w = bi.bmWidth;\r
3515             h = bi.bmHeight;\r
3516   }\r
3517   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3518                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3519   SelectObject(tmphdc, hbm);\r
3520   DeleteDC(tmphdc);\r
3521 }\r
3522 \r
3523 VOID\r
3524 DisplayLogos()\r
3525 {\r
3526   if(logoHeight) {\r
3527         HDC hdc = GetDC(hwndMain);\r
3528         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3529         if(appData.autoLogo) {\r
3530           \r
3531           switch(gameMode) { // pick logos based on game mode\r
3532             case IcsObserving:\r
3533                 whiteLogo = second.programLogo; // ICS logo\r
3534                 blackLogo = second.programLogo;\r
3535             default:\r
3536                 break;\r
3537             case IcsPlayingWhite:\r
3538                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3539                 blackLogo = second.programLogo; // ICS logo\r
3540                 break;\r
3541             case IcsPlayingBlack:\r
3542                 whiteLogo = second.programLogo; // ICS logo\r
3543                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3544                 break;\r
3545             case TwoMachinesPlay:\r
3546                 if(first.twoMachinesColor[0] == 'b') {\r
3547                     whiteLogo = second.programLogo;\r
3548                     blackLogo = first.programLogo;\r
3549                 }\r
3550                 break;\r
3551             case MachinePlaysWhite:\r
3552                 blackLogo = userLogo;\r
3553                 break;\r
3554             case MachinePlaysBlack:\r
3555                 whiteLogo = userLogo;\r
3556                 blackLogo = first.programLogo;\r
3557           }\r
3558         }\r
3559         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3560         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3561         ReleaseDC(hwndMain, hdc);\r
3562   }\r
3563 }\r
3564 \r
3565 void\r
3566 UpdateLogos(int display)\r
3567 { // called after loading new engine(s), in tourney or from menu\r
3568   LoadLogo(&first, 0, FALSE);\r
3569   LoadLogo(&second, 1, appData.icsActive);\r
3570   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3571   if(display) DisplayLogos();\r
3572 }\r
3573 \r
3574 static HDC hdcSeek;\r
3575 \r
3576 // [HGM] seekgraph\r
3577 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3578 {\r
3579     POINT stPt;\r
3580     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3581     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3582     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3583     SelectObject( hdcSeek, hp );\r
3584 }\r
3585 \r
3586 // front-end wrapper for drawing functions to do rectangles\r
3587 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3588 {\r
3589     HPEN hp;\r
3590     RECT rc;\r
3591 \r
3592     if (hdcSeek == NULL) {\r
3593     hdcSeek = GetDC(hwndMain);\r
3594       if (!appData.monoMode) {\r
3595         SelectPalette(hdcSeek, hPal, FALSE);\r
3596         RealizePalette(hdcSeek);\r
3597       }\r
3598     }\r
3599     hp = SelectObject( hdcSeek, gridPen );\r
3600     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3601     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3602     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3603     SelectObject( hdcSeek, hp );\r
3604 }\r
3605 \r
3606 // front-end wrapper for putting text in graph\r
3607 void DrawSeekText(char *buf, int x, int y)\r
3608 {\r
3609         SIZE stSize;\r
3610         SetBkMode( hdcSeek, TRANSPARENT );\r
3611         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3612         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3613 }\r
3614 \r
3615 void DrawSeekDot(int x, int y, int color)\r
3616 {\r
3617         int square = color & 0x80;\r
3618         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3619                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3620         color &= 0x7F;\r
3621         if(square)\r
3622             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3623                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3624         else\r
3625             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3626                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3627             SelectObject(hdcSeek, oldBrush);\r
3628 }\r
3629 \r
3630 void DrawSeekOpen()\r
3631 {\r
3632 }\r
3633 \r
3634 void DrawSeekClose()\r
3635 {\r
3636 }\r
3637 \r
3638 VOID\r
3639 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3640 {\r
3641   static Board lastReq[2], lastDrawn[2];\r
3642   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3643   static int lastDrawnFlipView = 0;\r
3644   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3645   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3646   HDC tmphdc;\r
3647   HDC hdcmem;\r
3648   HBITMAP bufferBitmap;\r
3649   HBITMAP oldBitmap;\r
3650   RECT Rect;\r
3651   HRGN clips[MAX_CLIPS];\r
3652   ChessSquare dragged_piece = EmptySquare;\r
3653   int nr = twoBoards*partnerUp;\r
3654 \r
3655   /* I'm undecided on this - this function figures out whether a full\r
3656    * repaint is necessary on its own, so there's no real reason to have the\r
3657    * caller tell it that.  I think this can safely be set to FALSE - but\r
3658    * if we trust the callers not to request full repaints unnessesarily, then\r
3659    * we could skip some clipping work.  In other words, only request a full\r
3660    * redraw when the majority of pieces have changed positions (ie. flip, \r
3661    * gamestart and similar)  --Hawk\r
3662    */\r
3663   Boolean fullrepaint = repaint;\r
3664 \r
3665   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3666 \r
3667   if( DrawPositionNeedsFullRepaint() ) {\r
3668       fullrepaint = TRUE;\r
3669   }\r
3670 \r
3671   if (board == NULL) {\r
3672     if (!lastReqValid[nr]) {\r
3673       return;\r
3674     }\r
3675     board = lastReq[nr];\r
3676   } else {\r
3677     CopyBoard(lastReq[nr], board);\r
3678     lastReqValid[nr] = 1;\r
3679   }\r
3680 \r
3681   if (doingSizing) {\r
3682     return;\r
3683   }\r
3684 \r
3685   if (IsIconic(hwndMain)) {\r
3686     return;\r
3687   }\r
3688 \r
3689   if (hdc == NULL) {\r
3690     hdc = GetDC(hwndMain);\r
3691     if (!appData.monoMode) {\r
3692       SelectPalette(hdc, hPal, FALSE);\r
3693       RealizePalette(hdc);\r
3694     }\r
3695     releaseDC = TRUE;\r
3696   } else {\r
3697     releaseDC = FALSE;\r
3698   }\r
3699 \r
3700   /* Create some work-DCs */\r
3701   hdcmem = CreateCompatibleDC(hdc);\r
3702   tmphdc = CreateCompatibleDC(hdc);\r
3703 \r
3704   /* If dragging is in progress, we temporarely remove the piece */\r
3705   /* [HGM] or temporarily decrease count if stacked              */\r
3706   /*       !! Moved to before board compare !!                   */\r
3707   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3708     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3709     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3710             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3711         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3712     } else \r
3713     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3714             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3715         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3716     } else \r
3717         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3718   }\r
3719 \r
3720   /* Figure out which squares need updating by comparing the \r
3721    * newest board with the last drawn board and checking if\r
3722    * flipping has changed.\r
3723    */\r
3724   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3725     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3726       for (column = 0; column < BOARD_WIDTH; column++) {\r
3727         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3728           SquareToPos(row, column, &x, &y);\r
3729           clips[num_clips++] =\r
3730             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3731         }\r
3732       }\r
3733     }\r
3734    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3735     for (i=0; i<2; i++) {\r
3736       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3737           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3738         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3739             lastDrawnHighlight.sq[i].y >= 0) {\r
3740           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3741                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3742           clips[num_clips++] =\r
3743             CreateRectRgn(x - lineGap, y - lineGap, \r
3744                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3745         }\r
3746         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3747           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3748           clips[num_clips++] =\r
3749             CreateRectRgn(x - lineGap, y - lineGap, \r
3750                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3751         }\r
3752       }\r
3753     }\r
3754     for (i=0; i<2; i++) {\r
3755       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3756           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3757         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3758             lastDrawnPremove.sq[i].y >= 0) {\r
3759           SquareToPos(lastDrawnPremove.sq[i].y,\r
3760                       lastDrawnPremove.sq[i].x, &x, &y);\r
3761           clips[num_clips++] =\r
3762             CreateRectRgn(x - lineGap, y - lineGap, \r
3763                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3764         }\r
3765         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3766             premoveHighlightInfo.sq[i].y >= 0) {\r
3767           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3768                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3769           clips[num_clips++] =\r
3770             CreateRectRgn(x - lineGap, y - lineGap, \r
3771                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3772         }\r
3773       }\r
3774     }\r
3775    } else { // nr == 1\r
3776         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3777         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3778         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3779         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3780       for (i=0; i<2; i++) {\r
3781         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3782             partnerHighlightInfo.sq[i].y >= 0) {\r
3783           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3784                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3785           clips[num_clips++] =\r
3786             CreateRectRgn(x - lineGap, y - lineGap, \r
3787                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3788         }\r
3789         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3790             oldPartnerHighlight.sq[i].y >= 0) {\r
3791           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3792                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3793           clips[num_clips++] =\r
3794             CreateRectRgn(x - lineGap, y - lineGap, \r
3795                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3796         }\r
3797       }\r
3798    }\r
3799   } else {\r
3800     fullrepaint = TRUE;\r
3801   }\r
3802 \r
3803   /* Create a buffer bitmap - this is the actual bitmap\r
3804    * being written to.  When all the work is done, we can\r
3805    * copy it to the real DC (the screen).  This avoids\r
3806    * the problems with flickering.\r
3807    */\r
3808   GetClientRect(hwndMain, &Rect);\r
3809   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3810                                         Rect.bottom-Rect.top+1);\r
3811   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3812   if (!appData.monoMode) {\r
3813     SelectPalette(hdcmem, hPal, FALSE);\r
3814   }\r
3815 \r
3816   /* Create clips for dragging */\r
3817   if (!fullrepaint) {\r
3818     if (dragInfo.from.x >= 0) {\r
3819       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3820       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3821     }\r
3822     if (dragInfo.start.x >= 0) {\r
3823       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3824       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3825     }\r
3826     if (dragInfo.pos.x >= 0) {\r
3827       x = dragInfo.pos.x - squareSize / 2;\r
3828       y = dragInfo.pos.y - squareSize / 2;\r
3829       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3830     }\r
3831     if (dragInfo.lastpos.x >= 0) {\r
3832       x = dragInfo.lastpos.x - squareSize / 2;\r
3833       y = dragInfo.lastpos.y - squareSize / 2;\r
3834       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3835     }\r
3836   }\r
3837 \r
3838   /* Are we animating a move?  \r
3839    * If so, \r
3840    *   - remove the piece from the board (temporarely)\r
3841    *   - calculate the clipping region\r
3842    */\r
3843   if (!fullrepaint) {\r
3844     if (animInfo.piece != EmptySquare) {\r
3845       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3846       x = boardRect.left + animInfo.lastpos.x;\r
3847       y = boardRect.top + animInfo.lastpos.y;\r
3848       x2 = boardRect.left + animInfo.pos.x;\r
3849       y2 = boardRect.top + animInfo.pos.y;\r
3850       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3851       /* Slight kludge.  The real problem is that after AnimateMove is\r
3852          done, the position on the screen does not match lastDrawn.\r
3853          This currently causes trouble only on e.p. captures in\r
3854          atomic, where the piece moves to an empty square and then\r
3855          explodes.  The old and new positions both had an empty square\r
3856          at the destination, but animation has drawn a piece there and\r
3857          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3858       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3859     }\r
3860   }\r
3861 \r
3862   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3863   if (num_clips == 0)\r
3864     fullrepaint = TRUE;\r
3865 \r
3866   /* Set clipping on the memory DC */\r
3867   if (!fullrepaint) {\r
3868     SelectClipRgn(hdcmem, clips[0]);\r
3869     for (x = 1; x < num_clips; x++) {\r
3870       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3871         abort();  // this should never ever happen!\r
3872     }\r
3873   }\r
3874 \r
3875   /* Do all the drawing to the memory DC */\r
3876   if(explodeInfo.radius) { // [HGM] atomic\r
3877         HBRUSH oldBrush;\r
3878         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3879         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3880         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3881         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3882         x += squareSize/2;\r
3883         y += squareSize/2;\r
3884         if(!fullrepaint) {\r
3885           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3886           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3887         }\r
3888         DrawGridOnDC(hdcmem);\r
3889         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3890         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3891         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3892         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3893         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3894         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3895         SelectObject(hdcmem, oldBrush);\r
3896   } else {\r
3897     if(border) DrawBackgroundOnDC(hdcmem);\r
3898     DrawGridOnDC(hdcmem);\r
3899     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3900         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3901         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3902     } else {\r
3903         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3904         oldPartnerHighlight = partnerHighlightInfo;\r
3905     }\r
3906     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3907   }\r
3908   if(nr == 0) // [HGM] dual: markers only on left board\r
3909   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3910     for (column = 0; column < BOARD_WIDTH; column++) {\r
3911         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3912             HBRUSH oldBrush = SelectObject(hdcmem, \r
3913                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3914             SquareToPos(row, column, &x, &y);\r
3915             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3916                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3917             SelectObject(hdcmem, oldBrush);\r
3918         }\r
3919     }\r
3920   }\r
3921 \r
3922   if( appData.highlightMoveWithArrow ) {\r
3923     DrawArrowHighlight(hdcmem);\r
3924   }\r
3925 \r
3926   DrawCoordsOnDC(hdcmem);\r
3927 \r
3928   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3929                  /* to make sure lastDrawn contains what is actually drawn */\r
3930 \r
3931   /* Put the dragged piece back into place and draw it (out of place!) */\r
3932     if (dragged_piece != EmptySquare) {\r
3933     /* [HGM] or restack */\r
3934     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3935                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3936     else\r
3937     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3938                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3939     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3940     x = dragInfo.pos.x - squareSize / 2;\r
3941     y = dragInfo.pos.y - squareSize / 2;\r
3942     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3943                   ((int) dragInfo.piece < (int) BlackPawn), \r
3944                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3945   }   \r
3946   \r
3947   /* Put the animated piece back into place and draw it */\r
3948   if (animInfo.piece != EmptySquare) {\r
3949     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3950     x = boardRect.left + animInfo.pos.x;\r
3951     y = boardRect.top + animInfo.pos.y;\r
3952     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3953                   ((int) animInfo.piece < (int) BlackPawn),\r
3954                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3955   }\r
3956 \r
3957   /* Release the bufferBitmap by selecting in the old bitmap \r
3958    * and delete the memory DC\r
3959    */\r
3960   SelectObject(hdcmem, oldBitmap);\r
3961   DeleteDC(hdcmem);\r
3962 \r
3963   /* Set clipping on the target DC */\r
3964   if (!fullrepaint) {\r
3965     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3966         RECT rect;\r
3967         GetRgnBox(clips[x], &rect);\r
3968         DeleteObject(clips[x]);\r
3969         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3970                           rect.right + wpMain.width/2, rect.bottom);\r
3971     }\r
3972     SelectClipRgn(hdc, clips[0]);\r
3973     for (x = 1; x < num_clips; x++) {\r
3974       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3975         abort();   // this should never ever happen!\r
3976     } \r
3977   }\r
3978 \r
3979   /* Copy the new bitmap onto the screen in one go.\r
3980    * This way we avoid any flickering\r
3981    */\r
3982   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3983   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3984          boardRect.right - boardRect.left,\r
3985          boardRect.bottom - boardRect.top,\r
3986          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3987   if(saveDiagFlag) { \r
3988     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3989     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3990 \r
3991     GetObject(bufferBitmap, sizeof(b), &b);\r
3992     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3993         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3994         bih.biWidth = b.bmWidth;\r
3995         bih.biHeight = b.bmHeight;\r
3996         bih.biPlanes = 1;\r
3997         bih.biBitCount = b.bmBitsPixel;\r
3998         bih.biCompression = 0;\r
3999         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4000         bih.biXPelsPerMeter = 0;\r
4001         bih.biYPelsPerMeter = 0;\r
4002         bih.biClrUsed = 0;\r
4003         bih.biClrImportant = 0;\r
4004 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4005 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4006         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4007 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4008 \r
4009         wb = b.bmWidthBytes;\r
4010         // count colors\r
4011         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4012                 int k = ((int*) pData)[i];\r
4013                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4014                 if(j >= 16) break;\r
4015                 color[j] = k;\r
4016                 if(j >= nrColors) nrColors = j+1;\r
4017         }\r
4018         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4019                 INT p = 0;\r
4020                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4021                     for(w=0; w<(wb>>2); w+=2) {\r
4022                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4023                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4024                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4025                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4026                         pData[p++] = m | j<<4;\r
4027                     }\r
4028                     while(p&3) pData[p++] = 0;\r
4029                 }\r
4030                 fac = 3;\r
4031                 wb = ((wb+31)>>5)<<2;\r
4032         }\r
4033         // write BITMAPFILEHEADER\r
4034         fprintf(diagFile, "BM");\r
4035         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4036         fputDW(diagFile, 0);\r
4037         fputDW(diagFile, 0x36 + (fac?64:0));\r
4038         // write BITMAPINFOHEADER\r
4039         fputDW(diagFile, 40);\r
4040         fputDW(diagFile, b.bmWidth);\r
4041         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4042         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4043         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4044         fputDW(diagFile, 0);\r
4045         fputDW(diagFile, 0);\r
4046         fputDW(diagFile, 0);\r
4047         fputDW(diagFile, 0);\r
4048         fputDW(diagFile, 0);\r
4049         fputDW(diagFile, 0);\r
4050         // write color table\r
4051         if(fac)\r
4052         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4053         // write bitmap data\r
4054         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4055                 fputc(pData[i], diagFile);\r
4056         free(pData);\r
4057      }\r
4058   }\r
4059 \r
4060   SelectObject(tmphdc, oldBitmap);\r
4061 \r
4062   /* Massive cleanup */\r
4063   for (x = 0; x < num_clips; x++)\r
4064     DeleteObject(clips[x]);\r
4065 \r
4066   DeleteDC(tmphdc);\r
4067   DeleteObject(bufferBitmap);\r
4068 \r
4069   if (releaseDC) \r
4070     ReleaseDC(hwndMain, hdc);\r
4071   \r
4072   if (lastDrawnFlipView != flipView && nr == 0) {\r
4073     if (flipView)\r
4074       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4075     else\r
4076       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4077   }\r
4078 \r
4079 /*  CopyBoard(lastDrawn, board);*/\r
4080   lastDrawnHighlight = highlightInfo;\r
4081   lastDrawnPremove   = premoveHighlightInfo;\r
4082   lastDrawnFlipView = flipView;\r
4083   lastDrawnValid[nr] = 1;\r
4084 }\r
4085 \r
4086 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4087 int\r
4088 SaveDiagram(f)\r
4089      FILE *f;\r
4090 {\r
4091     saveDiagFlag = 1; diagFile = f;\r
4092     HDCDrawPosition(NULL, TRUE, NULL);\r
4093     saveDiagFlag = 0;\r
4094 \r
4095     fclose(f);\r
4096     return TRUE;\r
4097 }\r
4098 \r
4099 \r
4100 /*---------------------------------------------------------------------------*\\r
4101 | CLIENT PAINT PROCEDURE\r
4102 |   This is the main event-handler for the WM_PAINT message.\r
4103 |\r
4104 \*---------------------------------------------------------------------------*/\r
4105 VOID\r
4106 PaintProc(HWND hwnd)\r
4107 {\r
4108   HDC         hdc;\r
4109   PAINTSTRUCT ps;\r
4110   HFONT       oldFont;\r
4111 \r
4112   if((hdc = BeginPaint(hwnd, &ps))) {\r
4113     if (IsIconic(hwnd)) {\r
4114       DrawIcon(hdc, 2, 2, iconCurrent);\r
4115     } else {\r
4116       if (!appData.monoMode) {\r
4117         SelectPalette(hdc, hPal, FALSE);\r
4118         RealizePalette(hdc);\r
4119       }\r
4120       HDCDrawPosition(hdc, 1, NULL);\r
4121       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4122         flipView = !flipView; partnerUp = !partnerUp;\r
4123         HDCDrawPosition(hdc, 1, NULL);\r
4124         flipView = !flipView; partnerUp = !partnerUp;\r
4125       }\r
4126       oldFont =\r
4127         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4128       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4129                  ETO_CLIPPED|ETO_OPAQUE,\r
4130                  &messageRect, messageText, strlen(messageText), NULL);\r
4131       SelectObject(hdc, oldFont);\r
4132       DisplayBothClocks();\r
4133       DisplayLogos();\r
4134     }\r
4135     EndPaint(hwnd,&ps);\r
4136   }\r
4137 \r
4138   return;\r
4139 }\r
4140 \r
4141 \r
4142 /*\r
4143  * If the user selects on a border boundary, return -1; if off the board,\r
4144  *   return -2.  Otherwise map the event coordinate to the square.\r
4145  * The offset boardRect.left or boardRect.top must already have been\r
4146  *   subtracted from x.\r
4147  */\r
4148 int EventToSquare(x, limit)\r
4149      int x, limit;\r
4150 {\r
4151   if (x <= border)\r
4152     return -2;\r
4153   if (x < lineGap + border)\r
4154     return -1;\r
4155   x -= lineGap + border;\r
4156   if ((x % (squareSize + lineGap)) >= squareSize)\r
4157     return -1;\r
4158   x /= (squareSize + lineGap);\r
4159     if (x >= limit)\r
4160     return -2;\r
4161   return x;\r
4162 }\r
4163 \r
4164 typedef struct {\r
4165   char piece;\r
4166   int command;\r
4167   char* name;\r
4168 } DropEnable;\r
4169 \r
4170 DropEnable dropEnables[] = {\r
4171   { 'P', DP_Pawn, N_("Pawn") },\r
4172   { 'N', DP_Knight, N_("Knight") },\r
4173   { 'B', DP_Bishop, N_("Bishop") },\r
4174   { 'R', DP_Rook, N_("Rook") },\r
4175   { 'Q', DP_Queen, N_("Queen") },\r
4176 };\r
4177 \r
4178 VOID\r
4179 SetupDropMenu(HMENU hmenu)\r
4180 {\r
4181   int i, count, enable;\r
4182   char *p;\r
4183   extern char white_holding[], black_holding[];\r
4184   char item[MSG_SIZ];\r
4185 \r
4186   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4187     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4188                dropEnables[i].piece);\r
4189     count = 0;\r
4190     while (p && *p++ == dropEnables[i].piece) count++;\r
4191       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4192     enable = count > 0 || !appData.testLegality\r
4193       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4194                       && !appData.icsActive);\r
4195     ModifyMenu(hmenu, dropEnables[i].command,\r
4196                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4197                dropEnables[i].command, item);\r
4198   }\r
4199 }\r
4200 \r
4201 void DragPieceBegin(int x, int y, Boolean instantly)\r
4202 {\r
4203       dragInfo.lastpos.x = boardRect.left + x;\r
4204       dragInfo.lastpos.y = boardRect.top + y;\r
4205       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4206       dragInfo.from.x = fromX;\r
4207       dragInfo.from.y = fromY;\r
4208       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4209       dragInfo.start = dragInfo.from;\r
4210       SetCapture(hwndMain);\r
4211 }\r
4212 \r
4213 void DragPieceEnd(int x, int y)\r
4214 {\r
4215     ReleaseCapture();\r
4216     dragInfo.start.x = dragInfo.start.y = -1;\r
4217     dragInfo.from = dragInfo.start;\r
4218     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4219 }\r
4220 \r
4221 void ChangeDragPiece(ChessSquare piece)\r
4222 {\r
4223     dragInfo.piece = piece;\r
4224 }\r
4225 \r
4226 /* Event handler for mouse messages */\r
4227 VOID\r
4228 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4229 {\r
4230   int x, y, menuNr;\r
4231   POINT pt;\r
4232   static int recursive = 0;\r
4233   HMENU hmenu;\r
4234   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4235 \r
4236   if (recursive) {\r
4237     if (message == WM_MBUTTONUP) {\r
4238       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4239          to the middle button: we simulate pressing the left button too!\r
4240          */\r
4241       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4242       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4243     }\r
4244     return;\r
4245   }\r
4246   recursive++;\r
4247   \r
4248   pt.x = LOWORD(lParam);\r
4249   pt.y = HIWORD(lParam);\r
4250   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4251   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4252   if (!flipView && y >= 0) {\r
4253     y = BOARD_HEIGHT - 1 - y;\r
4254   }\r
4255   if (flipView && x >= 0) {\r
4256     x = BOARD_WIDTH - 1 - x;\r
4257   }\r
4258 \r
4259   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4260 \r
4261   switch (message) {\r
4262   case WM_LBUTTONDOWN:\r
4263       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4264         ClockClick(flipClock); break;\r
4265       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4266         ClockClick(!flipClock); break;\r
4267       }\r
4268       dragInfo.start.x = dragInfo.start.y = -1;\r
4269       dragInfo.from = dragInfo.start;\r
4270     if(fromX == -1 && frozen) { // not sure where this is for\r
4271                 fromX = fromY = -1; \r
4272       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4273       break;\r
4274     }\r
4275       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4276       DrawPosition(TRUE, NULL);\r
4277     break;\r
4278 \r
4279   case WM_LBUTTONUP:\r
4280       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4281       DrawPosition(TRUE, NULL);\r
4282     break;\r
4283 \r
4284   case WM_MOUSEMOVE:\r
4285     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4286     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4287     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4288     if ((appData.animateDragging || appData.highlightDragging)\r
4289         && (wParam & MK_LBUTTON)\r
4290         && dragInfo.from.x >= 0) \r
4291     {\r
4292       BOOL full_repaint = FALSE;\r
4293 \r
4294       if (appData.animateDragging) {\r
4295         dragInfo.pos = pt;\r
4296       }\r
4297       if (appData.highlightDragging) {\r
4298         SetHighlights(fromX, fromY, x, y);\r
4299         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4300             full_repaint = TRUE;\r
4301         }\r
4302       }\r
4303       \r
4304       DrawPosition( full_repaint, NULL);\r
4305       \r
4306       dragInfo.lastpos = dragInfo.pos;\r
4307     }\r
4308     break;\r
4309 \r
4310   case WM_MOUSEWHEEL: // [DM]\r
4311     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4312        /* Mouse Wheel is being rolled forward\r
4313         * Play moves forward\r
4314         */\r
4315        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4316                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4317        /* Mouse Wheel is being rolled backward\r
4318         * Play moves backward\r
4319         */\r
4320        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4321                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4322     }\r
4323     break;\r
4324 \r
4325   case WM_MBUTTONUP:\r
4326   case WM_RBUTTONUP:\r
4327     ReleaseCapture();\r
4328     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4329     break;\r
4330  \r
4331   case WM_MBUTTONDOWN:\r
4332   case WM_RBUTTONDOWN:\r
4333     ErrorPopDown();\r
4334     ReleaseCapture();\r
4335     fromX = fromY = -1;\r
4336     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4337     dragInfo.start.x = dragInfo.start.y = -1;\r
4338     dragInfo.from = dragInfo.start;\r
4339     dragInfo.lastpos = dragInfo.pos;\r
4340     if (appData.highlightDragging) {\r
4341       ClearHighlights();\r
4342     }\r
4343     if(y == -2) {\r
4344       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4345       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4346           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4347       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4348           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4349       }\r
4350       break;\r
4351     }\r
4352     DrawPosition(TRUE, NULL);\r
4353 \r
4354     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4355     switch (menuNr) {\r
4356     case 0:\r
4357       if (message == WM_MBUTTONDOWN) {\r
4358         buttonCount = 3;  /* even if system didn't think so */\r
4359         if (wParam & MK_SHIFT) \r
4360           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4361         else\r
4362           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4363       } else { /* message == WM_RBUTTONDOWN */\r
4364         /* Just have one menu, on the right button.  Windows users don't\r
4365            think to try the middle one, and sometimes other software steals\r
4366            it, or it doesn't really exist. */\r
4367         if(gameInfo.variant != VariantShogi)\r
4368             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4369         else\r
4370             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4371       }\r
4372       break;\r
4373     case 2:\r
4374       SetCapture(hwndMain);\r
4375       break;\r
4376     case 1:\r
4377       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4378       SetupDropMenu(hmenu);\r
4379       MenuPopup(hwnd, pt, hmenu, -1);\r
4380     default:\r
4381       break;\r
4382     }\r
4383     break;\r
4384   }\r
4385 \r
4386   recursive--;\r
4387 }\r
4388 \r
4389 /* Preprocess messages for buttons in main window */\r
4390 LRESULT CALLBACK\r
4391 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4392 {\r
4393   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4394   int i, dir;\r
4395 \r
4396   for (i=0; i<N_BUTTONS; i++) {\r
4397     if (buttonDesc[i].id == id) break;\r
4398   }\r
4399   if (i == N_BUTTONS) return 0;\r
4400   switch (message) {\r
4401   case WM_KEYDOWN:\r
4402     switch (wParam) {\r
4403     case VK_LEFT:\r
4404     case VK_RIGHT:\r
4405       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4406       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4407       return TRUE;\r
4408     }\r
4409     break;\r
4410   case WM_CHAR:\r
4411     switch (wParam) {\r
4412     case '\r':\r
4413       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4414       return TRUE;\r
4415     default:\r
4416       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4417         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4418         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4419         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4420         SetFocus(h);\r
4421         SendMessage(h, WM_CHAR, wParam, lParam);\r
4422         return TRUE;\r
4423       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4424         TypeInEvent((char)wParam);\r
4425       }\r
4426       break;\r
4427     }\r
4428     break;\r
4429   }\r
4430   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4431 }\r
4432 \r
4433 /* Process messages for Promotion dialog box */\r
4434 LRESULT CALLBACK\r
4435 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4436 {\r
4437   char promoChar;\r
4438 \r
4439   switch (message) {\r
4440   case WM_INITDIALOG: /* message: initialize dialog box */\r
4441     /* Center the dialog over the application window */\r
4442     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4443     Translate(hDlg, DLG_PromotionKing);\r
4444     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4445       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4446        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4447        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4448                SW_SHOW : SW_HIDE);\r
4449     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4450     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4451        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4452          PieceToChar(WhiteAngel) != '~') ||\r
4453         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4454          PieceToChar(BlackAngel) != '~')   ) ?\r
4455                SW_SHOW : SW_HIDE);\r
4456     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4457        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4458          PieceToChar(WhiteMarshall) != '~') ||\r
4459         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4460          PieceToChar(BlackMarshall) != '~')   ) ?\r
4461                SW_SHOW : SW_HIDE);\r
4462     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4463     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4464        gameInfo.variant != VariantShogi ?\r
4465                SW_SHOW : SW_HIDE);\r
4466     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4467        gameInfo.variant != VariantShogi ?\r
4468                SW_SHOW : SW_HIDE);\r
4469     if(gameInfo.variant == VariantShogi) {\r
4470         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4471         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4472         SetWindowText(hDlg, "Promote?");\r
4473     }\r
4474     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4475        gameInfo.variant == VariantSuper ?\r
4476                SW_SHOW : SW_HIDE);\r
4477     return TRUE;\r
4478 \r
4479   case WM_COMMAND: /* message: received a command */\r
4480     switch (LOWORD(wParam)) {\r
4481     case IDCANCEL:\r
4482       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4483       ClearHighlights();\r
4484       DrawPosition(FALSE, NULL);\r
4485       return TRUE;\r
4486     case PB_King:\r
4487       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4488       break;\r
4489     case PB_Queen:\r
4490       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4491       break;\r
4492     case PB_Rook:\r
4493       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4494       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4495       break;\r
4496     case PB_Bishop:\r
4497       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4498       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4499       break;\r
4500     case PB_Chancellor:\r
4501       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4502       break;\r
4503     case PB_Archbishop:\r
4504       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4505       break;\r
4506     case PB_Knight:\r
4507       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4508       break;\r
4509     default:\r
4510       return FALSE;\r
4511     }\r
4512     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4513     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4514     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4515     fromX = fromY = -1;\r
4516     if (!appData.highlightLastMove) {\r
4517       ClearHighlights();\r
4518       DrawPosition(FALSE, NULL);\r
4519     }\r
4520     return TRUE;\r
4521   }\r
4522   return FALSE;\r
4523 }\r
4524 \r
4525 /* Pop up promotion dialog */\r
4526 VOID\r
4527 PromotionPopup(HWND hwnd)\r
4528 {\r
4529   FARPROC lpProc;\r
4530 \r
4531   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4532   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4533     hwnd, (DLGPROC)lpProc);\r
4534   FreeProcInstance(lpProc);\r
4535 }\r
4536 \r
4537 void\r
4538 PromotionPopUp()\r
4539 {\r
4540   DrawPosition(TRUE, NULL);\r
4541   PromotionPopup(hwndMain);\r
4542 }\r
4543 \r
4544 VOID\r
4545 LoadGameDialog(HWND hwnd, char* title)\r
4546 {\r
4547   UINT number = 0;\r
4548   FILE *f;\r
4549   char fileTitle[MSG_SIZ];\r
4550   f = OpenFileDialog(hwnd, "rb", "",\r
4551                      appData.oldSaveStyle ? "gam" : "pgn",\r
4552                      GAME_FILT,\r
4553                      title, &number, fileTitle, NULL);\r
4554   if (f != NULL) {\r
4555     cmailMsgLoaded = FALSE;\r
4556     if (number == 0) {\r
4557       int error = GameListBuild(f);\r
4558       if (error) {\r
4559         DisplayError(_("Cannot build game list"), error);\r
4560       } else if (!ListEmpty(&gameList) &&\r
4561                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4562         GameListPopUp(f, fileTitle);\r
4563         return;\r
4564       }\r
4565       GameListDestroy();\r
4566       number = 1;\r
4567     }\r
4568     LoadGame(f, number, fileTitle, FALSE);\r
4569   }\r
4570 }\r
4571 \r
4572 int get_term_width()\r
4573 {\r
4574     HDC hdc;\r
4575     TEXTMETRIC tm;\r
4576     RECT rc;\r
4577     HFONT hfont, hold_font;\r
4578     LOGFONT lf;\r
4579     HWND hText;\r
4580 \r
4581     if (hwndConsole)\r
4582         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4583     else\r
4584         return 79;\r
4585 \r
4586     // get the text metrics\r
4587     hdc = GetDC(hText);\r
4588     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4589     if (consoleCF.dwEffects & CFE_BOLD)\r
4590         lf.lfWeight = FW_BOLD;\r
4591     if (consoleCF.dwEffects & CFE_ITALIC)\r
4592         lf.lfItalic = TRUE;\r
4593     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4594         lf.lfStrikeOut = TRUE;\r
4595     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4596         lf.lfUnderline = TRUE;\r
4597     hfont = CreateFontIndirect(&lf);\r
4598     hold_font = SelectObject(hdc, hfont);\r
4599     GetTextMetrics(hdc, &tm);\r
4600     SelectObject(hdc, hold_font);\r
4601     DeleteObject(hfont);\r
4602     ReleaseDC(hText, hdc);\r
4603 \r
4604     // get the rectangle\r
4605     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4606 \r
4607     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4608 }\r
4609 \r
4610 void UpdateICSWidth(HWND hText)\r
4611 {\r
4612     LONG old_width, new_width;\r
4613 \r
4614     new_width = get_term_width(hText, FALSE);\r
4615     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4616     if (new_width != old_width)\r
4617     {\r
4618         ics_update_width(new_width);\r
4619         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4620     }\r
4621 }\r
4622 \r
4623 VOID\r
4624 ChangedConsoleFont()\r
4625 {\r
4626   CHARFORMAT cfmt;\r
4627   CHARRANGE tmpsel, sel;\r
4628   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4629   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4630   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4631   PARAFORMAT paraf;\r
4632 \r
4633   cfmt.cbSize = sizeof(CHARFORMAT);\r
4634   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4635     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4636                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4637   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4638    * size.  This was undocumented in the version of MSVC++ that I had\r
4639    * when I wrote the code, but is apparently documented now.\r
4640    */\r
4641   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4642   cfmt.bCharSet = f->lf.lfCharSet;\r
4643   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4644   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4645   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4646   /* Why are the following seemingly needed too? */\r
4647   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4648   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4649   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4650   tmpsel.cpMin = 0;\r
4651   tmpsel.cpMax = -1; /*999999?*/\r
4652   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4653   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4654   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4655    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4656    */\r
4657   paraf.cbSize = sizeof(paraf);\r
4658   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4659   paraf.dxStartIndent = 0;\r
4660   paraf.dxOffset = WRAP_INDENT;\r
4661   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4662   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4663   UpdateICSWidth(hText);\r
4664 }\r
4665 \r
4666 /*---------------------------------------------------------------------------*\\r
4667  *\r
4668  * Window Proc for main window\r
4669  *\r
4670 \*---------------------------------------------------------------------------*/\r
4671 \r
4672 /* Process messages for main window, etc. */\r
4673 LRESULT CALLBACK\r
4674 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4675 {\r
4676   FARPROC lpProc;\r
4677   int wmId, wmEvent;\r
4678   char *defName;\r
4679   FILE *f;\r
4680   UINT number;\r
4681   char fileTitle[MSG_SIZ];\r
4682   static SnapData sd;\r
4683   static int peek=0;\r
4684 \r
4685   switch (message) {\r
4686 \r
4687   case WM_PAINT: /* message: repaint portion of window */\r
4688     PaintProc(hwnd);\r
4689     break;\r
4690 \r
4691   case WM_ERASEBKGND:\r
4692     if (IsIconic(hwnd)) {\r
4693       /* Cheat; change the message */\r
4694       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4695     } else {\r
4696       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4697     }\r
4698     break;\r
4699 \r
4700   case WM_LBUTTONDOWN:\r
4701   case WM_MBUTTONDOWN:\r
4702   case WM_RBUTTONDOWN:\r
4703   case WM_LBUTTONUP:\r
4704   case WM_MBUTTONUP:\r
4705   case WM_RBUTTONUP:\r
4706   case WM_MOUSEMOVE:\r
4707   case WM_MOUSEWHEEL:\r
4708     MouseEvent(hwnd, message, wParam, lParam);\r
4709     break;\r
4710 \r
4711   case WM_KEYUP:\r
4712     if((char)wParam == '\b') {\r
4713       ForwardEvent(); peek = 0;\r
4714     }\r
4715 \r
4716     JAWS_KBUP_NAVIGATION\r
4717 \r
4718     break;\r
4719 \r
4720   case WM_KEYDOWN:\r
4721     if((char)wParam == '\b') {\r
4722       if(!peek) BackwardEvent(), peek = 1;\r
4723     }\r
4724 \r
4725     JAWS_KBDOWN_NAVIGATION\r
4726 \r
4727     break;\r
4728 \r
4729   case WM_CHAR:\r
4730     \r
4731     JAWS_ALT_INTERCEPT\r
4732 \r
4733     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4734         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4735         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4736         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4737         SetFocus(h);\r
4738         SendMessage(h, message, wParam, lParam);\r
4739     } else if(lParam != KF_REPEAT) {\r
4740         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4741                 TypeInEvent((char)wParam);\r
4742         } else if((char)wParam == 003) CopyGameToClipboard();\r
4743          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4744     }\r
4745 \r
4746     break;\r
4747 \r
4748   case WM_PALETTECHANGED:\r
4749     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4750       int nnew;\r
4751       HDC hdc = GetDC(hwndMain);\r
4752       SelectPalette(hdc, hPal, TRUE);\r
4753       nnew = RealizePalette(hdc);\r
4754       if (nnew > 0) {\r
4755         paletteChanged = TRUE;\r
4756         InvalidateRect(hwnd, &boardRect, FALSE);\r
4757       }\r
4758       ReleaseDC(hwnd, hdc);\r
4759     }\r
4760     break;\r
4761 \r
4762   case WM_QUERYNEWPALETTE:\r
4763     if (!appData.monoMode /*&& paletteChanged*/) {\r
4764       int nnew;\r
4765       HDC hdc = GetDC(hwndMain);\r
4766       paletteChanged = FALSE;\r
4767       SelectPalette(hdc, hPal, FALSE);\r
4768       nnew = RealizePalette(hdc);\r
4769       if (nnew > 0) {\r
4770         InvalidateRect(hwnd, &boardRect, FALSE);\r
4771       }\r
4772       ReleaseDC(hwnd, hdc);\r
4773       return TRUE;\r
4774     }\r
4775     return FALSE;\r
4776 \r
4777   case WM_COMMAND: /* message: command from application menu */\r
4778     wmId    = LOWORD(wParam);\r
4779     wmEvent = HIWORD(wParam);\r
4780 \r
4781     switch (wmId) {\r
4782     case IDM_NewGame:\r
4783       ResetGameEvent();\r
4784       SAY("new game enter a move to play against the computer with white");\r
4785       break;\r
4786 \r
4787     case IDM_NewGameFRC:\r
4788       if( NewGameFRC() == 0 ) {\r
4789         ResetGameEvent();\r
4790       }\r
4791       break;\r
4792 \r
4793     case IDM_NewVariant:\r
4794       NewVariantPopup(hwnd);\r
4795       break;\r
4796 \r
4797     case IDM_LoadGame:\r
4798       LoadGameDialog(hwnd, _("Load Game from File"));\r
4799       break;\r
4800 \r
4801     case IDM_LoadNextGame:\r
4802       ReloadGame(1);\r
4803       break;\r
4804 \r
4805     case IDM_LoadPrevGame:\r
4806       ReloadGame(-1);\r
4807       break;\r
4808 \r
4809     case IDM_ReloadGame:\r
4810       ReloadGame(0);\r
4811       break;\r
4812 \r
4813     case IDM_LoadPosition:\r
4814       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4815         Reset(FALSE, TRUE);\r
4816       }\r
4817       number = 1;\r
4818       f = OpenFileDialog(hwnd, "rb", "",\r
4819                          appData.oldSaveStyle ? "pos" : "fen",\r
4820                          POSITION_FILT,\r
4821                          _("Load Position from File"), &number, fileTitle, NULL);\r
4822       if (f != NULL) {\r
4823         LoadPosition(f, number, fileTitle);\r
4824       }\r
4825       break;\r
4826 \r
4827     case IDM_LoadNextPosition:\r
4828       ReloadPosition(1);\r
4829       break;\r
4830 \r
4831     case IDM_LoadPrevPosition:\r
4832       ReloadPosition(-1);\r
4833       break;\r
4834 \r
4835     case IDM_ReloadPosition:\r
4836       ReloadPosition(0);\r
4837       break;\r
4838 \r
4839     case IDM_SaveGame:\r
4840       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4841       f = OpenFileDialog(hwnd, "a", defName,\r
4842                          appData.oldSaveStyle ? "gam" : "pgn",\r
4843                          GAME_FILT,\r
4844                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4845       if (f != NULL) {\r
4846         SaveGame(f, 0, "");\r
4847       }\r
4848       break;\r
4849 \r
4850     case IDM_SavePosition:\r
4851       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4852       f = OpenFileDialog(hwnd, "a", defName,\r
4853                          appData.oldSaveStyle ? "pos" : "fen",\r
4854                          POSITION_FILT,\r
4855                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4856       if (f != NULL) {\r
4857         SavePosition(f, 0, "");\r
4858       }\r
4859       break;\r
4860 \r
4861     case IDM_SaveDiagram:\r
4862       defName = "diagram";\r
4863       f = OpenFileDialog(hwnd, "wb", defName,\r
4864                          "bmp",\r
4865                          DIAGRAM_FILT,\r
4866                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4867       if (f != NULL) {\r
4868         SaveDiagram(f);\r
4869       }\r
4870       break;\r
4871 \r
4872     case IDM_CreateBook:\r
4873       CreateBookEvent();\r
4874       break;\r
4875 \r
4876     case IDM_CopyGame:\r
4877       CopyGameToClipboard();\r
4878       break;\r
4879 \r
4880     case IDM_PasteGame:\r
4881       PasteGameFromClipboard();\r
4882       break;\r
4883 \r
4884     case IDM_CopyGameListToClipboard:\r
4885       CopyGameListToClipboard();\r
4886       break;\r
4887 \r
4888     /* [AS] Autodetect FEN or PGN data */\r
4889     case IDM_PasteAny:\r
4890       PasteGameOrFENFromClipboard();\r
4891       break;\r
4892 \r
4893     /* [AS] Move history */\r
4894     case IDM_ShowMoveHistory:\r
4895         if( MoveHistoryIsUp() ) {\r
4896             MoveHistoryPopDown();\r
4897         }\r
4898         else {\r
4899             MoveHistoryPopUp();\r
4900         }\r
4901         break;\r
4902 \r
4903     /* [AS] Eval graph */\r
4904     case IDM_ShowEvalGraph:\r
4905         if( EvalGraphIsUp() ) {\r
4906             EvalGraphPopDown();\r
4907         }\r
4908         else {\r
4909             EvalGraphPopUp();\r
4910             SetFocus(hwndMain);\r
4911         }\r
4912         break;\r
4913 \r
4914     /* [AS] Engine output */\r
4915     case IDM_ShowEngineOutput:\r
4916         if( EngineOutputIsUp() ) {\r
4917             EngineOutputPopDown();\r
4918         }\r
4919         else {\r
4920             EngineOutputPopUp();\r
4921         }\r
4922         break;\r
4923 \r
4924     /* [AS] User adjudication */\r
4925     case IDM_UserAdjudication_White:\r
4926         UserAdjudicationEvent( +1 );\r
4927         break;\r
4928 \r
4929     case IDM_UserAdjudication_Black:\r
4930         UserAdjudicationEvent( -1 );\r
4931         break;\r
4932 \r
4933     case IDM_UserAdjudication_Draw:\r
4934         UserAdjudicationEvent( 0 );\r
4935         break;\r
4936 \r
4937     /* [AS] Game list options dialog */\r
4938     case IDM_GameListOptions:\r
4939       GameListOptions();\r
4940       break;\r
4941 \r
4942     case IDM_NewChat:\r
4943       ChatPopUp(NULL);\r
4944       break;\r
4945 \r
4946     case IDM_CopyPosition:\r
4947       CopyFENToClipboard();\r
4948       break;\r
4949 \r
4950     case IDM_PastePosition:\r
4951       PasteFENFromClipboard();\r
4952       break;\r
4953 \r
4954     case IDM_MailMove:\r
4955       MailMoveEvent();\r
4956       break;\r
4957 \r
4958     case IDM_ReloadCMailMsg:\r
4959       Reset(TRUE, TRUE);\r
4960       ReloadCmailMsgEvent(FALSE);\r
4961       break;\r
4962 \r
4963     case IDM_Minimize:\r
4964       ShowWindow(hwnd, SW_MINIMIZE);\r
4965       break;\r
4966 \r
4967     case IDM_Exit:\r
4968       ExitEvent(0);\r
4969       break;\r
4970 \r
4971     case IDM_MachineWhite:\r
4972       MachineWhiteEvent();\r
4973       /*\r
4974        * refresh the tags dialog only if it's visible\r
4975        */\r
4976       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4977           char *tags;\r
4978           tags = PGNTags(&gameInfo);\r
4979           TagsPopUp(tags, CmailMsg());\r
4980           free(tags);\r
4981       }\r
4982       SAY("computer starts playing white");\r
4983       break;\r
4984 \r
4985     case IDM_MachineBlack:\r
4986       MachineBlackEvent();\r
4987       /*\r
4988        * refresh the tags dialog only if it's visible\r
4989        */\r
4990       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4991           char *tags;\r
4992           tags = PGNTags(&gameInfo);\r
4993           TagsPopUp(tags, CmailMsg());\r
4994           free(tags);\r
4995       }\r
4996       SAY("computer starts playing black");\r
4997       break;\r
4998 \r
4999     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5000       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5001       break;\r
5002 \r
5003     case IDM_TwoMachines:\r
5004       TwoMachinesEvent();\r
5005       /*\r
5006        * refresh the tags dialog only if it's visible\r
5007        */\r
5008       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5009           char *tags;\r
5010           tags = PGNTags(&gameInfo);\r
5011           TagsPopUp(tags, CmailMsg());\r
5012           free(tags);\r
5013       }\r
5014       SAY("computer starts playing both sides");\r
5015       break;\r
5016 \r
5017     case IDM_AnalysisMode:\r
5018       if(AnalyzeModeEvent()) {\r
5019         SAY("analyzing current position");\r
5020       }\r
5021       break;\r
5022 \r
5023     case IDM_AnalyzeFile:\r
5024       AnalyzeFileEvent();\r
5025       break;\r
5026 \r
5027     case IDM_IcsClient:\r
5028       IcsClientEvent();\r
5029       break;\r
5030 \r
5031     case IDM_EditGame:\r
5032     case IDM_EditGame2:\r
5033       EditGameEvent();\r
5034       SAY("edit game");\r
5035       break;\r
5036 \r
5037     case IDM_EditPosition:\r
5038     case IDM_EditPosition2:\r
5039       EditPositionEvent();\r
5040       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5041       break;\r
5042 \r
5043     case IDM_Training:\r
5044       TrainingEvent();\r
5045       break;\r
5046 \r
5047     case IDM_ShowGameList:\r
5048       ShowGameListProc();\r
5049       break;\r
5050 \r
5051     case IDM_EditProgs1:\r
5052       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5053       break;\r
5054 \r
5055     case IDM_LoadProg1:\r
5056      LoadEnginePopUp(hwndMain, 0);\r
5057       break;\r
5058 \r
5059     case IDM_LoadProg2:\r
5060      LoadEnginePopUp(hwndMain, 1);\r
5061       break;\r
5062 \r
5063     case IDM_EditServers:\r
5064       EditTagsPopUp(icsNames, &icsNames);\r
5065       break;\r
5066 \r
5067     case IDM_EditTags:\r
5068     case IDM_Tags:\r
5069       EditTagsProc();\r
5070       break;\r
5071 \r
5072     case IDM_EditBook:\r
5073       EditBookEvent();\r
5074       break;\r
5075 \r
5076     case IDM_EditComment:\r
5077     case IDM_Comment:\r
5078       if (commentUp && editComment) {\r
5079         CommentPopDown();\r
5080       } else {\r
5081         EditCommentEvent();\r
5082       }\r
5083       break;\r
5084 \r
5085     case IDM_Pause:\r
5086       PauseEvent();\r
5087       break;\r
5088 \r
5089     case IDM_Accept:\r
5090       AcceptEvent();\r
5091       break;\r
5092 \r
5093     case IDM_Decline:\r
5094       DeclineEvent();\r
5095       break;\r
5096 \r
5097     case IDM_Rematch:\r
5098       RematchEvent();\r
5099       break;\r
5100 \r
5101     case IDM_CallFlag:\r
5102       CallFlagEvent();\r
5103       break;\r
5104 \r
5105     case IDM_Draw:\r
5106       DrawEvent();\r
5107       break;\r
5108 \r
5109     case IDM_Adjourn:\r
5110       AdjournEvent();\r
5111       break;\r
5112 \r
5113     case IDM_Abort:\r
5114       AbortEvent();\r
5115       break;\r
5116 \r
5117     case IDM_Resign:\r
5118       ResignEvent();\r
5119       break;\r
5120 \r
5121     case IDM_StopObserving:\r
5122       StopObservingEvent();\r
5123       break;\r
5124 \r
5125     case IDM_StopExamining:\r
5126       StopExaminingEvent();\r
5127       break;\r
5128 \r
5129     case IDM_Upload:\r
5130       UploadGameEvent();\r
5131       break;\r
5132 \r
5133     case IDM_TypeInMove:\r
5134       TypeInEvent('\000');\r
5135       break;\r
5136 \r
5137     case IDM_TypeInName:\r
5138       PopUpNameDialog('\000');\r
5139       break;\r
5140 \r
5141     case IDM_Backward:\r
5142       BackwardEvent();\r
5143       SetFocus(hwndMain);\r
5144       break;\r
5145 \r
5146     JAWS_MENU_ITEMS\r
5147 \r
5148     case IDM_Forward:\r
5149       ForwardEvent();\r
5150       SetFocus(hwndMain);\r
5151       break;\r
5152 \r
5153     case IDM_ToStart:\r
5154       ToStartEvent();\r
5155       SetFocus(hwndMain);\r
5156       break;\r
5157 \r
5158     case IDM_ToEnd:\r
5159       ToEndEvent();\r
5160       SetFocus(hwndMain);\r
5161       break;\r
5162 \r
5163     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5164     case OPT_GameListPrev:\r
5165       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5166       break;\r
5167 \r
5168     case IDM_Revert:\r
5169       RevertEvent(FALSE);\r
5170       break;\r
5171 \r
5172     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5173       RevertEvent(TRUE);\r
5174       break;\r
5175 \r
5176     case IDM_TruncateGame:\r
5177       TruncateGameEvent();\r
5178       break;\r
5179 \r
5180     case IDM_MoveNow:\r
5181       MoveNowEvent();\r
5182       break;\r
5183 \r
5184     case IDM_RetractMove:\r
5185       RetractMoveEvent();\r
5186       break;\r
5187 \r
5188     case IDM_FlipView:\r
5189       flipView = !flipView;\r
5190       DrawPosition(FALSE, NULL);\r
5191       break;\r
5192 \r
5193     case IDM_FlipClock:\r
5194       flipClock = !flipClock;\r
5195       DisplayBothClocks();\r
5196       DisplayLogos();\r
5197       break;\r
5198 \r
5199     case IDM_MuteSounds:\r
5200       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5201       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5202                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5203       break;\r
5204 \r
5205     case IDM_GeneralOptions:\r
5206       GeneralOptionsPopup(hwnd);\r
5207       DrawPosition(TRUE, NULL);\r
5208       break;\r
5209 \r
5210     case IDM_BoardOptions:\r
5211       BoardOptionsPopup(hwnd);\r
5212       break;\r
5213 \r
5214     case IDM_ThemeOptions:\r
5215       ThemeOptionsPopup(hwnd);\r
5216       break;\r
5217 \r
5218     case IDM_EnginePlayOptions:\r
5219       EnginePlayOptionsPopup(hwnd);\r
5220       break;\r
5221 \r
5222     case IDM_Engine1Options:\r
5223       EngineOptionsPopup(hwnd, &first);\r
5224       break;\r
5225 \r
5226     case IDM_Engine2Options:\r
5227       savedHwnd = hwnd;\r
5228       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5229       EngineOptionsPopup(hwnd, &second);\r
5230       break;\r
5231 \r
5232     case IDM_OptionsUCI:\r
5233       UciOptionsPopup(hwnd);\r
5234       break;\r
5235 \r
5236     case IDM_Tourney:\r
5237       TourneyPopup(hwnd);\r
5238       break;\r
5239 \r
5240     case IDM_IcsOptions:\r
5241       IcsOptionsPopup(hwnd);\r
5242       break;\r
5243 \r
5244     case IDM_Fonts:\r
5245       FontsOptionsPopup(hwnd);\r
5246       break;\r
5247 \r
5248     case IDM_Sounds:\r
5249       SoundOptionsPopup(hwnd);\r
5250       break;\r
5251 \r
5252     case IDM_CommPort:\r
5253       CommPortOptionsPopup(hwnd);\r
5254       break;\r
5255 \r
5256     case IDM_LoadOptions:\r
5257       LoadOptionsPopup(hwnd);\r
5258       break;\r
5259 \r
5260     case IDM_SaveOptions:\r
5261       SaveOptionsPopup(hwnd);\r
5262       break;\r
5263 \r
5264     case IDM_TimeControl:\r
5265       TimeControlOptionsPopup(hwnd);\r
5266       break;\r
5267 \r
5268     case IDM_SaveSettings:\r
5269       SaveSettings(settingsFileName);\r
5270       break;\r
5271 \r
5272     case IDM_SaveSettingsOnExit:\r
5273       saveSettingsOnExit = !saveSettingsOnExit;\r
5274       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5275                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5276                                          MF_CHECKED : MF_UNCHECKED));\r
5277       break;\r
5278 \r
5279     case IDM_Hint:\r
5280       HintEvent();\r
5281       break;\r
5282 \r
5283     case IDM_Book:\r
5284       BookEvent();\r
5285       break;\r
5286 \r
5287     case IDM_AboutGame:\r
5288       AboutGameEvent();\r
5289       break;\r
5290 \r
5291     case IDM_Debug:\r
5292       appData.debugMode = !appData.debugMode;\r
5293       if (appData.debugMode) {\r
5294         char dir[MSG_SIZ];\r
5295         GetCurrentDirectory(MSG_SIZ, dir);\r
5296         SetCurrentDirectory(installDir);\r
5297         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5298         SetCurrentDirectory(dir);\r
5299         setbuf(debugFP, NULL);\r
5300       } else {\r
5301         fclose(debugFP);\r
5302         debugFP = NULL;\r
5303       }\r
5304       break;\r
5305 \r
5306     case IDM_HELPCONTENTS:\r
5307       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5308           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5309           MessageBox (GetFocus(),\r
5310                     _("Unable to activate help"),\r
5311                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5312       }\r
5313       break;\r
5314 \r
5315     case IDM_HELPSEARCH:\r
5316         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5317             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5318         MessageBox (GetFocus(),\r
5319                     _("Unable to activate help"),\r
5320                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5321       }\r
5322       break;\r
5323 \r
5324     case IDM_HELPHELP:\r
5325       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5326         MessageBox (GetFocus(),\r
5327                     _("Unable to activate help"),\r
5328                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5329       }\r
5330       break;\r
5331 \r
5332     case IDM_ABOUT:\r
5333       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5334       DialogBox(hInst, \r
5335         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5336         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5337       FreeProcInstance(lpProc);\r
5338       break;\r
5339 \r
5340     case IDM_DirectCommand1:\r
5341       AskQuestionEvent(_("Direct Command"),\r
5342                        _("Send to chess program:"), "", "1");\r
5343       break;\r
5344     case IDM_DirectCommand2:\r
5345       AskQuestionEvent(_("Direct Command"),\r
5346                        _("Send to second chess program:"), "", "2");\r
5347       break;\r
5348 \r
5349     case EP_WhitePawn:\r
5350       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5351       fromX = fromY = -1;\r
5352       break;\r
5353 \r
5354     case EP_WhiteKnight:\r
5355       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5356       fromX = fromY = -1;\r
5357       break;\r
5358 \r
5359     case EP_WhiteBishop:\r
5360       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5361       fromX = fromY = -1;\r
5362       break;\r
5363 \r
5364     case EP_WhiteRook:\r
5365       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5366       fromX = fromY = -1;\r
5367       break;\r
5368 \r
5369     case EP_WhiteQueen:\r
5370       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5371       fromX = fromY = -1;\r
5372       break;\r
5373 \r
5374     case EP_WhiteFerz:\r
5375       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5376       fromX = fromY = -1;\r
5377       break;\r
5378 \r
5379     case EP_WhiteWazir:\r
5380       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5381       fromX = fromY = -1;\r
5382       break;\r
5383 \r
5384     case EP_WhiteAlfil:\r
5385       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5386       fromX = fromY = -1;\r
5387       break;\r
5388 \r
5389     case EP_WhiteCannon:\r
5390       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5391       fromX = fromY = -1;\r
5392       break;\r
5393 \r
5394     case EP_WhiteCardinal:\r
5395       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5396       fromX = fromY = -1;\r
5397       break;\r
5398 \r
5399     case EP_WhiteMarshall:\r
5400       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5401       fromX = fromY = -1;\r
5402       break;\r
5403 \r
5404     case EP_WhiteKing:\r
5405       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5406       fromX = fromY = -1;\r
5407       break;\r
5408 \r
5409     case EP_BlackPawn:\r
5410       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5411       fromX = fromY = -1;\r
5412       break;\r
5413 \r
5414     case EP_BlackKnight:\r
5415       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5416       fromX = fromY = -1;\r
5417       break;\r
5418 \r
5419     case EP_BlackBishop:\r
5420       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5421       fromX = fromY = -1;\r
5422       break;\r
5423 \r
5424     case EP_BlackRook:\r
5425       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5426       fromX = fromY = -1;\r
5427       break;\r
5428 \r
5429     case EP_BlackQueen:\r
5430       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5431       fromX = fromY = -1;\r
5432       break;\r
5433 \r
5434     case EP_BlackFerz:\r
5435       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5436       fromX = fromY = -1;\r
5437       break;\r
5438 \r
5439     case EP_BlackWazir:\r
5440       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5441       fromX = fromY = -1;\r
5442       break;\r
5443 \r
5444     case EP_BlackAlfil:\r
5445       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5446       fromX = fromY = -1;\r
5447       break;\r
5448 \r
5449     case EP_BlackCannon:\r
5450       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5451       fromX = fromY = -1;\r
5452       break;\r
5453 \r
5454     case EP_BlackCardinal:\r
5455       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5456       fromX = fromY = -1;\r
5457       break;\r
5458 \r
5459     case EP_BlackMarshall:\r
5460       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5461       fromX = fromY = -1;\r
5462       break;\r
5463 \r
5464     case EP_BlackKing:\r
5465       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5466       fromX = fromY = -1;\r
5467       break;\r
5468 \r
5469     case EP_EmptySquare:\r
5470       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5471       fromX = fromY = -1;\r
5472       break;\r
5473 \r
5474     case EP_ClearBoard:\r
5475       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5476       fromX = fromY = -1;\r
5477       break;\r
5478 \r
5479     case EP_White:\r
5480       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5481       fromX = fromY = -1;\r
5482       break;\r
5483 \r
5484     case EP_Black:\r
5485       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5486       fromX = fromY = -1;\r
5487       break;\r
5488 \r
5489     case EP_Promote:\r
5490       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5491       fromX = fromY = -1;\r
5492       break;\r
5493 \r
5494     case EP_Demote:\r
5495       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5496       fromX = fromY = -1;\r
5497       break;\r
5498 \r
5499     case DP_Pawn:\r
5500       DropMenuEvent(WhitePawn, fromX, fromY);\r
5501       fromX = fromY = -1;\r
5502       break;\r
5503 \r
5504     case DP_Knight:\r
5505       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5506       fromX = fromY = -1;\r
5507       break;\r
5508 \r
5509     case DP_Bishop:\r
5510       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5511       fromX = fromY = -1;\r
5512       break;\r
5513 \r
5514     case DP_Rook:\r
5515       DropMenuEvent(WhiteRook, fromX, fromY);\r
5516       fromX = fromY = -1;\r
5517       break;\r
5518 \r
5519     case DP_Queen:\r
5520       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5521       fromX = fromY = -1;\r
5522       break;\r
5523 \r
5524     case IDM_English:\r
5525       barbaric = 0; appData.language = "";\r
5526       TranslateMenus(0);\r
5527       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5528       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5529       lastChecked = wmId;\r
5530       break;\r
5531 \r
5532     default:\r
5533       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5534           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5535       else\r
5536       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5537           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5538           TranslateMenus(0);\r
5539           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5540           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5541           lastChecked = wmId;\r
5542           break;\r
5543       }\r
5544       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5545     }\r
5546     break;\r
5547 \r
5548   case WM_TIMER:\r
5549     switch (wParam) {\r
5550     case CLOCK_TIMER_ID:\r
5551       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5552       clockTimerEvent = 0;\r
5553       DecrementClocks(); /* call into back end */\r
5554       break;\r
5555     case LOAD_GAME_TIMER_ID:\r
5556       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5557       loadGameTimerEvent = 0;\r
5558       AutoPlayGameLoop(); /* call into back end */\r
5559       break;\r
5560     case ANALYSIS_TIMER_ID:\r
5561       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5562                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5563         AnalysisPeriodicEvent(0);\r
5564       } else {\r
5565         KillTimer(hwnd, analysisTimerEvent);\r
5566         analysisTimerEvent = 0;\r
5567       }\r
5568       break;\r
5569     case DELAYED_TIMER_ID:\r
5570       KillTimer(hwnd, delayedTimerEvent);\r
5571       delayedTimerEvent = 0;\r
5572       delayedTimerCallback();\r
5573       break;\r
5574     }\r
5575     break;\r
5576 \r
5577   case WM_USER_Input:\r
5578     InputEvent(hwnd, message, wParam, lParam);\r
5579     break;\r
5580 \r
5581   /* [AS] Also move "attached" child windows */\r
5582   case WM_WINDOWPOSCHANGING:\r
5583 \r
5584     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5585         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5586 \r
5587         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5588             /* Window is moving */\r
5589             RECT rcMain;\r
5590 \r
5591 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5592             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5593             rcMain.right  = wpMain.x + wpMain.width;\r
5594             rcMain.top    = wpMain.y;\r
5595             rcMain.bottom = wpMain.y + wpMain.height;\r
5596             \r
5597             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5598             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5599             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5600             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5601             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5602             wpMain.x = lpwp->x;\r
5603             wpMain.y = lpwp->y;\r
5604         }\r
5605     }\r
5606     break;\r
5607 \r
5608   /* [AS] Snapping */\r
5609   case WM_ENTERSIZEMOVE:\r
5610     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5611     if (hwnd == hwndMain) {\r
5612       doingSizing = TRUE;\r
5613       lastSizing = 0;\r
5614     }\r
5615     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5616     break;\r
5617 \r
5618   case WM_SIZING:\r
5619     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5620     if (hwnd == hwndMain) {\r
5621       lastSizing = wParam;\r
5622     }\r
5623     break;\r
5624 \r
5625   case WM_MOVING:\r
5626     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5627       return OnMoving( &sd, hwnd, wParam, lParam );\r
5628 \r
5629   case WM_EXITSIZEMOVE:\r
5630     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5631     if (hwnd == hwndMain) {\r
5632       RECT client;\r
5633       doingSizing = FALSE;\r
5634       InvalidateRect(hwnd, &boardRect, FALSE);\r
5635       GetClientRect(hwnd, &client);\r
5636       ResizeBoard(client.right, client.bottom, lastSizing);\r
5637       lastSizing = 0;\r
5638       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5639     }\r
5640     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5641     break;\r
5642 \r
5643   case WM_DESTROY: /* message: window being destroyed */\r
5644     PostQuitMessage(0);\r
5645     break;\r
5646 \r
5647   case WM_CLOSE:\r
5648     if (hwnd == hwndMain) {\r
5649       ExitEvent(0);\r
5650     }\r
5651     break;\r
5652 \r
5653   default:      /* Passes it on if unprocessed */\r
5654     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5655   }\r
5656   return 0;\r
5657 }\r
5658 \r
5659 /*---------------------------------------------------------------------------*\\r
5660  *\r
5661  * Misc utility routines\r
5662  *\r
5663 \*---------------------------------------------------------------------------*/\r
5664 \r
5665 /*\r
5666  * Decent random number generator, at least not as bad as Windows\r
5667  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5668  */\r
5669 unsigned int randstate;\r
5670 \r
5671 int\r
5672 myrandom(void)\r
5673 {\r
5674   randstate = randstate * 1664525 + 1013904223;\r
5675   return (int) randstate & 0x7fffffff;\r
5676 }\r
5677 \r
5678 void\r
5679 mysrandom(unsigned int seed)\r
5680 {\r
5681   randstate = seed;\r
5682 }\r
5683 \r
5684 \r
5685 /* \r
5686  * returns TRUE if user selects a different color, FALSE otherwise \r
5687  */\r
5688 \r
5689 BOOL\r
5690 ChangeColor(HWND hwnd, COLORREF *which)\r
5691 {\r
5692   static BOOL firstTime = TRUE;\r
5693   static DWORD customColors[16];\r
5694   CHOOSECOLOR cc;\r
5695   COLORREF newcolor;\r
5696   int i;\r
5697   ColorClass ccl;\r
5698 \r
5699   if (firstTime) {\r
5700     /* Make initial colors in use available as custom colors */\r
5701     /* Should we put the compiled-in defaults here instead? */\r
5702     i = 0;\r
5703     customColors[i++] = lightSquareColor & 0xffffff;\r
5704     customColors[i++] = darkSquareColor & 0xffffff;\r
5705     customColors[i++] = whitePieceColor & 0xffffff;\r
5706     customColors[i++] = blackPieceColor & 0xffffff;\r
5707     customColors[i++] = highlightSquareColor & 0xffffff;\r
5708     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5709 \r
5710     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5711       customColors[i++] = textAttribs[ccl].color;\r
5712     }\r
5713     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5714     firstTime = FALSE;\r
5715   }\r
5716 \r
5717   cc.lStructSize = sizeof(cc);\r
5718   cc.hwndOwner = hwnd;\r
5719   cc.hInstance = NULL;\r
5720   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5721   cc.lpCustColors = (LPDWORD) customColors;\r
5722   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5723 \r
5724   if (!ChooseColor(&cc)) return FALSE;\r
5725 \r
5726   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5727   if (newcolor == *which) return FALSE;\r
5728   *which = newcolor;\r
5729   return TRUE;\r
5730 \r
5731   /*\r
5732   InitDrawingColors();\r
5733   InvalidateRect(hwnd, &boardRect, FALSE);\r
5734   */\r
5735 }\r
5736 \r
5737 BOOLEAN\r
5738 MyLoadSound(MySound *ms)\r
5739 {\r
5740   BOOL ok = FALSE;\r
5741   struct stat st;\r
5742   FILE *f;\r
5743 \r
5744   if (ms->data && ms->flag) free(ms->data);\r
5745   ms->data = NULL;\r
5746 \r
5747   switch (ms->name[0]) {\r
5748   case NULLCHAR:\r
5749     /* Silence */\r
5750     ok = TRUE;\r
5751     break;\r
5752   case '$':\r
5753     /* System sound from Control Panel.  Don't preload here. */\r
5754     ok = TRUE;\r
5755     break;\r
5756   case '!':\r
5757     if (ms->name[1] == NULLCHAR) {\r
5758       /* "!" alone = silence */\r
5759       ok = TRUE;\r
5760     } else {\r
5761       /* Builtin wave resource.  Error if not found. */\r
5762       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5763       if (h == NULL) break;\r
5764       ms->data = (void *)LoadResource(hInst, h);\r
5765       ms->flag = 0; // not maloced, so cannot be freed!\r
5766       if (h == NULL) break;\r
5767       ok = TRUE;\r
5768     }\r
5769     break;\r
5770   default:\r
5771     /* .wav file.  Error if not found. */\r
5772     f = fopen(ms->name, "rb");\r
5773     if (f == NULL) break;\r
5774     if (fstat(fileno(f), &st) < 0) break;\r
5775     ms->data = malloc(st.st_size);\r
5776     ms->flag = 1;\r
5777     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5778     fclose(f);\r
5779     ok = TRUE;\r
5780     break;\r
5781   }\r
5782   if (!ok) {\r
5783     char buf[MSG_SIZ];\r
5784       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5785     DisplayError(buf, GetLastError());\r
5786   }\r
5787   return ok;\r
5788 }\r
5789 \r
5790 BOOLEAN\r
5791 MyPlaySound(MySound *ms)\r
5792 {\r
5793   BOOLEAN ok = FALSE;\r
5794 \r
5795   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5796   switch (ms->name[0]) {\r
5797   case NULLCHAR:\r
5798         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5799     /* Silence */\r
5800     ok = TRUE;\r
5801     break;\r
5802   case '$':\r
5803     /* System sound from Control Panel (deprecated feature).\r
5804        "$" alone or an unset sound name gets default beep (still in use). */\r
5805     if (ms->name[1]) {\r
5806       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5807     }\r
5808     if (!ok) ok = MessageBeep(MB_OK);\r
5809     break; \r
5810   case '!':\r
5811     /* Builtin wave resource, or "!" alone for silence */\r
5812     if (ms->name[1]) {\r
5813       if (ms->data == NULL) return FALSE;\r
5814       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5815     } else {\r
5816       ok = TRUE;\r
5817     }\r
5818     break;\r
5819   default:\r
5820     /* .wav file.  Error if not found. */\r
5821     if (ms->data == NULL) return FALSE;\r
5822     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5823     break;\r
5824   }\r
5825   /* Don't print an error: this can happen innocently if the sound driver\r
5826      is busy; for instance, if another instance of WinBoard is playing\r
5827      a sound at about the same time. */\r
5828   return ok;\r
5829 }\r
5830 \r
5831 \r
5832 LRESULT CALLBACK\r
5833 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5834 {\r
5835   BOOL ok;\r
5836   OPENFILENAME *ofn;\r
5837   static UINT *number; /* gross that this is static */\r
5838 \r
5839   switch (message) {\r
5840   case WM_INITDIALOG: /* message: initialize dialog box */\r
5841     /* Center the dialog over the application window */\r
5842     ofn = (OPENFILENAME *) lParam;\r
5843     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5844       number = (UINT *) ofn->lCustData;\r
5845       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5846     } else {\r
5847       number = NULL;\r
5848     }\r
5849     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5850     Translate(hDlg, 1536);\r
5851     return FALSE;  /* Allow for further processing */\r
5852 \r
5853   case WM_COMMAND:\r
5854     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5855       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5856     }\r
5857     return FALSE;  /* Allow for further processing */\r
5858   }\r
5859   return FALSE;\r
5860 }\r
5861 \r
5862 UINT APIENTRY\r
5863 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5864 {\r
5865   static UINT *number;\r
5866   OPENFILENAME *ofname;\r
5867   OFNOTIFY *ofnot;\r
5868   switch (uiMsg) {\r
5869   case WM_INITDIALOG:\r
5870     Translate(hdlg, DLG_IndexNumber);\r
5871     ofname = (OPENFILENAME *)lParam;\r
5872     number = (UINT *)(ofname->lCustData);\r
5873     break;\r
5874   case WM_NOTIFY:\r
5875     ofnot = (OFNOTIFY *)lParam;\r
5876     if (ofnot->hdr.code == CDN_FILEOK) {\r
5877       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5878     }\r
5879     break;\r
5880   }\r
5881   return 0;\r
5882 }\r
5883 \r
5884 \r
5885 FILE *\r
5886 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5887                char *nameFilt, char *dlgTitle, UINT *number,\r
5888                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5889 {\r
5890   OPENFILENAME openFileName;\r
5891   char buf1[MSG_SIZ];\r
5892   FILE *f;\r
5893 \r
5894   if (fileName == NULL) fileName = buf1;\r
5895   if (defName == NULL) {\r
5896     safeStrCpy(fileName, "*.", 3 );\r
5897     strcat(fileName, defExt);\r
5898   } else {\r
5899     safeStrCpy(fileName, defName, MSG_SIZ );\r
5900   }\r
5901     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5902   if (number) *number = 0;\r
5903 \r
5904   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5905   openFileName.hwndOwner         = hwnd;\r
5906   openFileName.hInstance         = (HANDLE) hInst;\r
5907   openFileName.lpstrFilter       = nameFilt;\r
5908   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5909   openFileName.nMaxCustFilter    = 0L;\r
5910   openFileName.nFilterIndex      = 1L;\r
5911   openFileName.lpstrFile         = fileName;\r
5912   openFileName.nMaxFile          = MSG_SIZ;\r
5913   openFileName.lpstrFileTitle    = fileTitle;\r
5914   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5915   openFileName.lpstrInitialDir   = NULL;\r
5916   openFileName.lpstrTitle        = dlgTitle;\r
5917   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5918     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5919     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5920     | (oldDialog ? 0 : OFN_EXPLORER);\r
5921   openFileName.nFileOffset       = 0;\r
5922   openFileName.nFileExtension    = 0;\r
5923   openFileName.lpstrDefExt       = defExt;\r
5924   openFileName.lCustData         = (LONG) number;\r
5925   openFileName.lpfnHook          = oldDialog ?\r
5926     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5927   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5928 \r
5929   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5930                         GetOpenFileName(&openFileName)) {\r
5931     /* open the file */\r
5932     f = fopen(openFileName.lpstrFile, write);\r
5933     if (f == NULL) {\r
5934       MessageBox(hwnd, _("File open failed"), NULL,\r
5935                  MB_OK|MB_ICONEXCLAMATION);\r
5936       return NULL;\r
5937     }\r
5938   } else {\r
5939     int err = CommDlgExtendedError();\r
5940     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5941     return FALSE;\r
5942   }\r
5943   return f;\r
5944 }\r
5945 \r
5946 \r
5947 \r
5948 VOID APIENTRY\r
5949 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5950 {\r
5951   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5952 \r
5953   /*\r
5954    * Get the first pop-up menu in the menu template. This is the\r
5955    * menu that TrackPopupMenu displays.\r
5956    */\r
5957   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5958   TranslateOneMenu(10, hmenuTrackPopup);\r
5959 \r
5960   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5961 \r
5962   /*\r
5963    * TrackPopup uses screen coordinates, so convert the\r
5964    * coordinates of the mouse click to screen coordinates.\r
5965    */\r
5966   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5967 \r
5968   /* Draw and track the floating pop-up menu. */\r
5969   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5970                  pt.x, pt.y, 0, hwnd, NULL);\r
5971 \r
5972   /* Destroy the menu.*/\r
5973   DestroyMenu(hmenu);\r
5974 }\r
5975    \r
5976 typedef struct {\r
5977   HWND hDlg, hText;\r
5978   int sizeX, sizeY, newSizeX, newSizeY;\r
5979   HDWP hdwp;\r
5980 } ResizeEditPlusButtonsClosure;\r
5981 \r
5982 BOOL CALLBACK\r
5983 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5984 {\r
5985   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5986   RECT rect;\r
5987   POINT pt;\r
5988 \r
5989   if (hChild == cl->hText) return TRUE;\r
5990   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5991   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5992   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5993   ScreenToClient(cl->hDlg, &pt);\r
5994   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5995     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5996   return TRUE;\r
5997 }\r
5998 \r
5999 /* Resize a dialog that has a (rich) edit field filling most of\r
6000    the top, with a row of buttons below */\r
6001 VOID\r
6002 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6003 {\r
6004   RECT rectText;\r
6005   int newTextHeight, newTextWidth;\r
6006   ResizeEditPlusButtonsClosure cl;\r
6007   \r
6008   /*if (IsIconic(hDlg)) return;*/\r
6009   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6010   \r
6011   cl.hdwp = BeginDeferWindowPos(8);\r
6012 \r
6013   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6014   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6015   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6016   if (newTextHeight < 0) {\r
6017     newSizeY += -newTextHeight;\r
6018     newTextHeight = 0;\r
6019   }\r
6020   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6021     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6022 \r
6023   cl.hDlg = hDlg;\r
6024   cl.hText = hText;\r
6025   cl.sizeX = sizeX;\r
6026   cl.sizeY = sizeY;\r
6027   cl.newSizeX = newSizeX;\r
6028   cl.newSizeY = newSizeY;\r
6029   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6030 \r
6031   EndDeferWindowPos(cl.hdwp);\r
6032 }\r
6033 \r
6034 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6035 {\r
6036     RECT    rChild, rParent;\r
6037     int     wChild, hChild, wParent, hParent;\r
6038     int     wScreen, hScreen, xNew, yNew;\r
6039     HDC     hdc;\r
6040 \r
6041     /* Get the Height and Width of the child window */\r
6042     GetWindowRect (hwndChild, &rChild);\r
6043     wChild = rChild.right - rChild.left;\r
6044     hChild = rChild.bottom - rChild.top;\r
6045 \r
6046     /* Get the Height and Width of the parent window */\r
6047     GetWindowRect (hwndParent, &rParent);\r
6048     wParent = rParent.right - rParent.left;\r
6049     hParent = rParent.bottom - rParent.top;\r
6050 \r
6051     /* Get the display limits */\r
6052     hdc = GetDC (hwndChild);\r
6053     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6054     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6055     ReleaseDC(hwndChild, hdc);\r
6056 \r
6057     /* Calculate new X position, then adjust for screen */\r
6058     xNew = rParent.left + ((wParent - wChild) /2);\r
6059     if (xNew < 0) {\r
6060         xNew = 0;\r
6061     } else if ((xNew+wChild) > wScreen) {\r
6062         xNew = wScreen - wChild;\r
6063     }\r
6064 \r
6065     /* Calculate new Y position, then adjust for screen */\r
6066     if( mode == 0 ) {\r
6067         yNew = rParent.top  + ((hParent - hChild) /2);\r
6068     }\r
6069     else {\r
6070         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6071     }\r
6072 \r
6073     if (yNew < 0) {\r
6074         yNew = 0;\r
6075     } else if ((yNew+hChild) > hScreen) {\r
6076         yNew = hScreen - hChild;\r
6077     }\r
6078 \r
6079     /* Set it, and return */\r
6080     return SetWindowPos (hwndChild, NULL,\r
6081                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6082 }\r
6083 \r
6084 /* Center one window over another */\r
6085 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6086 {\r
6087     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6088 }\r
6089 \r
6090 /*---------------------------------------------------------------------------*\\r
6091  *\r
6092  * Startup Dialog functions\r
6093  *\r
6094 \*---------------------------------------------------------------------------*/\r
6095 void\r
6096 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6097 {\r
6098   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6099 \r
6100   while (*cd != NULL) {\r
6101     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6102     cd++;\r
6103   }\r
6104 }\r
6105 \r
6106 void\r
6107 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6108 {\r
6109   char buf1[MAX_ARG_LEN];\r
6110   int len;\r
6111 \r
6112   if (str[0] == '@') {\r
6113     FILE* f = fopen(str + 1, "r");\r
6114     if (f == NULL) {\r
6115       DisplayFatalError(str + 1, errno, 2);\r
6116       return;\r
6117     }\r
6118     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6119     fclose(f);\r
6120     buf1[len] = NULLCHAR;\r
6121     str = buf1;\r
6122   }\r
6123 \r
6124   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6125 \r
6126   for (;;) {\r
6127     char buf[MSG_SIZ];\r
6128     char *end = strchr(str, '\n');\r
6129     if (end == NULL) return;\r
6130     memcpy(buf, str, end - str);\r
6131     buf[end - str] = NULLCHAR;\r
6132     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6133     str = end + 1;\r
6134   }\r
6135 }\r
6136 \r
6137 void\r
6138 SetStartupDialogEnables(HWND hDlg)\r
6139 {\r
6140   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6141     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6142     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6143   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6144     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6145   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6146     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6147   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6148     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6149   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6150     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6151     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6152     IsDlgButtonChecked(hDlg, OPT_View));\r
6153 }\r
6154 \r
6155 char *\r
6156 QuoteForFilename(char *filename)\r
6157 {\r
6158   int dquote, space;\r
6159   dquote = strchr(filename, '"') != NULL;\r
6160   space = strchr(filename, ' ') != NULL;\r
6161   if (dquote || space) {\r
6162     if (dquote) {\r
6163       return "'";\r
6164     } else {\r
6165       return "\"";\r
6166     }\r
6167   } else {\r
6168     return "";\r
6169   }\r
6170 }\r
6171 \r
6172 VOID\r
6173 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6174 {\r
6175   char buf[MSG_SIZ];\r
6176   char *q;\r
6177 \r
6178   InitComboStringsFromOption(hwndCombo, nthnames);\r
6179   q = QuoteForFilename(nthcp);\r
6180     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6181   if (*nthdir != NULLCHAR) {\r
6182     q = QuoteForFilename(nthdir);\r
6183       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6184   }\r
6185   if (*nthcp == NULLCHAR) {\r
6186     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6187   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6188     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6189     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6190   }\r
6191 }\r
6192 \r
6193 LRESULT CALLBACK\r
6194 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6195 {\r
6196   char buf[MSG_SIZ];\r
6197   HANDLE hwndCombo;\r
6198   char *p;\r
6199 \r
6200   switch (message) {\r
6201   case WM_INITDIALOG:\r
6202     /* Center the dialog */\r
6203     CenterWindow (hDlg, GetDesktopWindow());\r
6204     Translate(hDlg, DLG_Startup);\r
6205     /* Initialize the dialog items */\r
6206     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6207                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6208                   firstChessProgramNames);\r
6209     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6210                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6211                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6212     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6213     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6214       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6215     if (*appData.icsHelper != NULLCHAR) {\r
6216       char *q = QuoteForFilename(appData.icsHelper);\r
6217       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6218     }\r
6219     if (*appData.icsHost == NULLCHAR) {\r
6220       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6221       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6222     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6223       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6224       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6225     }\r
6226 \r
6227     if (appData.icsActive) {\r
6228       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6229     }\r
6230     else if (appData.noChessProgram) {\r
6231       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6232     }\r
6233     else {\r
6234       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6235     }\r
6236 \r
6237     SetStartupDialogEnables(hDlg);\r
6238     return TRUE;\r
6239 \r
6240   case WM_COMMAND:\r
6241     switch (LOWORD(wParam)) {\r
6242     case IDOK:\r
6243       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6244         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6245         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6246         p = buf;\r
6247         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6248         ParseArgs(StringGet, &p);\r
6249         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6250         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6251         p = buf;\r
6252         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6253         ParseArgs(StringGet, &p);\r
6254         SwapEngines(singleList); // ... and then make it 'second'\r
6255         appData.noChessProgram = FALSE;\r
6256         appData.icsActive = FALSE;\r
6257       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6258         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6259         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6260         p = buf;\r
6261         ParseArgs(StringGet, &p);\r
6262         if (appData.zippyPlay) {\r
6263           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6264           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6265           p = buf;\r
6266           ParseArgs(StringGet, &p);\r
6267         }\r
6268       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6269         appData.noChessProgram = TRUE;\r
6270         appData.icsActive = FALSE;\r
6271       } else {\r
6272         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6273                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6274         return TRUE;\r
6275       }\r
6276       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6277         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6278         p = buf;\r
6279         ParseArgs(StringGet, &p);\r
6280       }\r
6281       EndDialog(hDlg, TRUE);\r
6282       return TRUE;\r
6283 \r
6284     case IDCANCEL:\r
6285       ExitEvent(0);\r
6286       return TRUE;\r
6287 \r
6288     case IDM_HELPCONTENTS:\r
6289       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6290         MessageBox (GetFocus(),\r
6291                     _("Unable to activate help"),\r
6292                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6293       }\r
6294       break;\r
6295 \r
6296     default:\r
6297       SetStartupDialogEnables(hDlg);\r
6298       break;\r
6299     }\r
6300     break;\r
6301   }\r
6302   return FALSE;\r
6303 }\r
6304 \r
6305 /*---------------------------------------------------------------------------*\\r
6306  *\r
6307  * About box dialog functions\r
6308  *\r
6309 \*---------------------------------------------------------------------------*/\r
6310 \r
6311 /* Process messages for "About" dialog box */\r
6312 LRESULT CALLBACK\r
6313 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6314 {\r
6315   switch (message) {\r
6316   case WM_INITDIALOG: /* message: initialize dialog box */\r
6317     /* Center the dialog over the application window */\r
6318     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6319     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6320     Translate(hDlg, ABOUTBOX);\r
6321     JAWS_COPYRIGHT\r
6322     return (TRUE);\r
6323 \r
6324   case WM_COMMAND: /* message: received a command */\r
6325     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6326         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6327       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6328       return (TRUE);\r
6329     }\r
6330     break;\r
6331   }\r
6332   return (FALSE);\r
6333 }\r
6334 \r
6335 /*---------------------------------------------------------------------------*\\r
6336  *\r
6337  * Comment Dialog functions\r
6338  *\r
6339 \*---------------------------------------------------------------------------*/\r
6340 \r
6341 LRESULT CALLBACK\r
6342 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6343 {\r
6344   static HANDLE hwndText = NULL;\r
6345   int len, newSizeX, newSizeY, flags;\r
6346   static int sizeX, sizeY;\r
6347   char *str;\r
6348   RECT rect;\r
6349   MINMAXINFO *mmi;\r
6350 \r
6351   switch (message) {\r
6352   case WM_INITDIALOG: /* message: initialize dialog box */\r
6353     /* Initialize the dialog items */\r
6354     Translate(hDlg, DLG_EditComment);\r
6355     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6356     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6357     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6358     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6359     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6360     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6361     SetWindowText(hDlg, commentTitle);\r
6362     if (editComment) {\r
6363       SetFocus(hwndText);\r
6364     } else {\r
6365       SetFocus(GetDlgItem(hDlg, IDOK));\r
6366     }\r
6367     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6368                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6369                 MAKELPARAM(FALSE, 0));\r
6370     /* Size and position the dialog */\r
6371     if (!commentDialog) {\r
6372       commentDialog = hDlg;\r
6373       flags = SWP_NOZORDER;\r
6374       GetClientRect(hDlg, &rect);\r
6375       sizeX = rect.right;\r
6376       sizeY = rect.bottom;\r
6377       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6378           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6379         WINDOWPLACEMENT wp;\r
6380         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6381         wp.length = sizeof(WINDOWPLACEMENT);\r
6382         wp.flags = 0;\r
6383         wp.showCmd = SW_SHOW;\r
6384         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6385         wp.rcNormalPosition.left = wpComment.x;\r
6386         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6387         wp.rcNormalPosition.top = wpComment.y;\r
6388         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6389         SetWindowPlacement(hDlg, &wp);\r
6390 \r
6391         GetClientRect(hDlg, &rect);\r
6392         newSizeX = rect.right;\r
6393         newSizeY = rect.bottom;\r
6394         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6395                               newSizeX, newSizeY);\r
6396         sizeX = newSizeX;\r
6397         sizeY = newSizeY;\r
6398       }\r
6399     }\r
6400     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6401     return FALSE;\r
6402 \r
6403   case WM_COMMAND: /* message: received a command */\r
6404     switch (LOWORD(wParam)) {\r
6405     case IDOK:\r
6406       if (editComment) {\r
6407         char *p, *q;\r
6408         /* Read changed options from the dialog box */\r
6409         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6410         len = GetWindowTextLength(hwndText);\r
6411         str = (char *) malloc(len + 1);\r
6412         GetWindowText(hwndText, str, len + 1);\r
6413         p = q = str;\r
6414         while (*q) {\r
6415           if (*q == '\r')\r
6416             q++;\r
6417           else\r
6418             *p++ = *q++;\r
6419         }\r
6420         *p = NULLCHAR;\r
6421         ReplaceComment(commentIndex, str);\r
6422         free(str);\r
6423       }\r
6424       CommentPopDown();\r
6425       return TRUE;\r
6426 \r
6427     case IDCANCEL:\r
6428     case OPT_CancelComment:\r
6429       CommentPopDown();\r
6430       return TRUE;\r
6431 \r
6432     case OPT_ClearComment:\r
6433       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6434       break;\r
6435 \r
6436     case OPT_EditComment:\r
6437       EditCommentEvent();\r
6438       return TRUE;\r
6439 \r
6440     default:\r
6441       break;\r
6442     }\r
6443     break;\r
6444 \r
6445   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6446         if( wParam == OPT_CommentText ) {\r
6447             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6448 \r
6449             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6450                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6451                 POINTL pt;\r
6452                 LRESULT index;\r
6453 \r
6454                 pt.x = LOWORD( lpMF->lParam );\r
6455                 pt.y = HIWORD( lpMF->lParam );\r
6456 \r
6457                 if(lpMF->msg == WM_CHAR) {\r
6458                         CHARRANGE sel;\r
6459                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6460                         index = sel.cpMin;\r
6461                 } else\r
6462                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6463 \r
6464                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6465                 len = GetWindowTextLength(hwndText);\r
6466                 str = (char *) malloc(len + 1);\r
6467                 GetWindowText(hwndText, str, len + 1);\r
6468                 ReplaceComment(commentIndex, str);\r
6469                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6470                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6471                 free(str);\r
6472 \r
6473                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6474                 lpMF->msg = WM_USER;\r
6475 \r
6476                 return TRUE;\r
6477             }\r
6478         }\r
6479         break;\r
6480 \r
6481   case WM_SIZE:\r
6482     newSizeX = LOWORD(lParam);\r
6483     newSizeY = HIWORD(lParam);\r
6484     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6485     sizeX = newSizeX;\r
6486     sizeY = newSizeY;\r
6487     break;\r
6488 \r
6489   case WM_GETMINMAXINFO:\r
6490     /* Prevent resizing window too small */\r
6491     mmi = (MINMAXINFO *) lParam;\r
6492     mmi->ptMinTrackSize.x = 100;\r
6493     mmi->ptMinTrackSize.y = 100;\r
6494     break;\r
6495   }\r
6496   return FALSE;\r
6497 }\r
6498 \r
6499 VOID\r
6500 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6501 {\r
6502   FARPROC lpProc;\r
6503   char *p, *q;\r
6504 \r
6505   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6506 \r
6507   if (str == NULL) str = "";\r
6508   p = (char *) malloc(2 * strlen(str) + 2);\r
6509   q = p;\r
6510   while (*str) {\r
6511     if (*str == '\n') *q++ = '\r';\r
6512     *q++ = *str++;\r
6513   }\r
6514   *q = NULLCHAR;\r
6515   if (commentText != NULL) free(commentText);\r
6516 \r
6517   commentIndex = index;\r
6518   commentTitle = title;\r
6519   commentText = p;\r
6520   editComment = edit;\r
6521 \r
6522   if (commentDialog) {\r
6523     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6524     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6525   } else {\r
6526     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6527     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6528                  hwndMain, (DLGPROC)lpProc);\r
6529     FreeProcInstance(lpProc);\r
6530   }\r
6531   commentUp = TRUE;\r
6532 }\r
6533 \r
6534 \r
6535 /*---------------------------------------------------------------------------*\\r
6536  *\r
6537  * Type-in move dialog functions\r
6538  * \r
6539 \*---------------------------------------------------------------------------*/\r
6540 \r
6541 LRESULT CALLBACK\r
6542 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6543 {\r
6544   char move[MSG_SIZ];\r
6545   HWND hInput;\r
6546 \r
6547   switch (message) {\r
6548   case WM_INITDIALOG:\r
6549     move[0] = (char) lParam;\r
6550     move[1] = NULLCHAR;\r
6551     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6552     Translate(hDlg, DLG_TypeInMove);\r
6553     hInput = GetDlgItem(hDlg, OPT_Move);\r
6554     SetWindowText(hInput, move);\r
6555     SetFocus(hInput);\r
6556     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6557     return FALSE;\r
6558 \r
6559   case WM_COMMAND:\r
6560     switch (LOWORD(wParam)) {\r
6561     case IDOK:\r
6562 \r
6563       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6564       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6565       TypeInDoneEvent(move);\r
6566       EndDialog(hDlg, TRUE);\r
6567       return TRUE;\r
6568     case IDCANCEL:\r
6569       EndDialog(hDlg, FALSE);\r
6570       return TRUE;\r
6571     default:\r
6572       break;\r
6573     }\r
6574     break;\r
6575   }\r
6576   return FALSE;\r
6577 }\r
6578 \r
6579 VOID\r
6580 PopUpMoveDialog(char firstchar)\r
6581 {\r
6582     FARPROC lpProc;\r
6583 \r
6584       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6585       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6586         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6587       FreeProcInstance(lpProc);\r
6588 }\r
6589 \r
6590 /*---------------------------------------------------------------------------*\\r
6591  *\r
6592  * Type-in name dialog functions\r
6593  * \r
6594 \*---------------------------------------------------------------------------*/\r
6595 \r
6596 LRESULT CALLBACK\r
6597 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6598 {\r
6599   char move[MSG_SIZ];\r
6600   HWND hInput;\r
6601 \r
6602   switch (message) {\r
6603   case WM_INITDIALOG:\r
6604     move[0] = (char) lParam;\r
6605     move[1] = NULLCHAR;\r
6606     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6607     Translate(hDlg, DLG_TypeInName);\r
6608     hInput = GetDlgItem(hDlg, OPT_Name);\r
6609     SetWindowText(hInput, move);\r
6610     SetFocus(hInput);\r
6611     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6612     return FALSE;\r
6613 \r
6614   case WM_COMMAND:\r
6615     switch (LOWORD(wParam)) {\r
6616     case IDOK:\r
6617       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6618       appData.userName = strdup(move);\r
6619       SetUserLogo();\r
6620       SetGameInfo();\r
6621       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6622         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6623         DisplayTitle(move);\r
6624       }\r
6625 \r
6626 \r
6627       EndDialog(hDlg, TRUE);\r
6628       return TRUE;\r
6629     case IDCANCEL:\r
6630       EndDialog(hDlg, FALSE);\r
6631       return TRUE;\r
6632     default:\r
6633       break;\r
6634     }\r
6635     break;\r
6636   }\r
6637   return FALSE;\r
6638 }\r
6639 \r
6640 VOID\r
6641 PopUpNameDialog(char firstchar)\r
6642 {\r
6643     FARPROC lpProc;\r
6644     \r
6645       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6646       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6647         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6648       FreeProcInstance(lpProc);\r
6649 }\r
6650 \r
6651 /*---------------------------------------------------------------------------*\\r
6652  *\r
6653  *  Error dialogs\r
6654  * \r
6655 \*---------------------------------------------------------------------------*/\r
6656 \r
6657 /* Nonmodal error box */\r
6658 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6659                              WPARAM wParam, LPARAM lParam);\r
6660 \r
6661 VOID\r
6662 ErrorPopUp(char *title, char *content)\r
6663 {\r
6664   FARPROC lpProc;\r
6665   char *p, *q;\r
6666   BOOLEAN modal = hwndMain == NULL;\r
6667 \r
6668   p = content;\r
6669   q = errorMessage;\r
6670   while (*p) {\r
6671     if (*p == '\n') {\r
6672       if (modal) {\r
6673         *q++ = ' ';\r
6674         p++;\r
6675       } else {\r
6676         *q++ = '\r';\r
6677         *q++ = *p++;\r
6678       }\r
6679     } else {\r
6680       *q++ = *p++;\r
6681     }\r
6682   }\r
6683   *q = NULLCHAR;\r
6684   strncpy(errorTitle, title, sizeof(errorTitle));\r
6685   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6686   \r
6687   if (modal) {\r
6688     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6689   } else {\r
6690     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6691     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6692                  hwndMain, (DLGPROC)lpProc);\r
6693     FreeProcInstance(lpProc);\r
6694   }\r
6695 }\r
6696 \r
6697 VOID\r
6698 ErrorPopDown()\r
6699 {\r
6700   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6701   if (errorDialog == NULL) return;\r
6702   DestroyWindow(errorDialog);\r
6703   errorDialog = NULL;\r
6704   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6705 }\r
6706 \r
6707 LRESULT CALLBACK\r
6708 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6709 {\r
6710   HANDLE hwndText;\r
6711   RECT rChild;\r
6712 \r
6713   switch (message) {\r
6714   case WM_INITDIALOG:\r
6715     GetWindowRect(hDlg, &rChild);\r
6716 \r
6717     /*\r
6718     SetWindowPos(hDlg, NULL, rChild.left,\r
6719       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6720       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6721     */\r
6722 \r
6723     /* \r
6724         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6725         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6726         and it doesn't work when you resize the dialog.\r
6727         For now, just give it a default position.\r
6728     */\r
6729     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6730     Translate(hDlg, DLG_Error);\r
6731 \r
6732     errorDialog = hDlg;\r
6733     SetWindowText(hDlg, errorTitle);\r
6734     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6735     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6736     return FALSE;\r
6737 \r
6738   case WM_COMMAND:\r
6739     switch (LOWORD(wParam)) {\r
6740     case IDOK:\r
6741     case IDCANCEL:\r
6742       if (errorDialog == hDlg) errorDialog = NULL;\r
6743       DestroyWindow(hDlg);\r
6744       return TRUE;\r
6745 \r
6746     default:\r
6747       break;\r
6748     }\r
6749     break;\r
6750   }\r
6751   return FALSE;\r
6752 }\r
6753 \r
6754 #ifdef GOTHIC\r
6755 HWND gothicDialog = NULL;\r
6756 \r
6757 LRESULT CALLBACK\r
6758 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6759 {\r
6760   HANDLE hwndText;\r
6761   RECT rChild;\r
6762   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6763 \r
6764   switch (message) {\r
6765   case WM_INITDIALOG:\r
6766     GetWindowRect(hDlg, &rChild);\r
6767 \r
6768     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6769                                                              SWP_NOZORDER);\r
6770 \r
6771     /* \r
6772         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6773         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6774         and it doesn't work when you resize the dialog.\r
6775         For now, just give it a default position.\r
6776     */\r
6777     gothicDialog = hDlg;\r
6778     SetWindowText(hDlg, errorTitle);\r
6779     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6780     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6781     return FALSE;\r
6782 \r
6783   case WM_COMMAND:\r
6784     switch (LOWORD(wParam)) {\r
6785     case IDOK:\r
6786     case IDCANCEL:\r
6787       if (errorDialog == hDlg) errorDialog = NULL;\r
6788       DestroyWindow(hDlg);\r
6789       return TRUE;\r
6790 \r
6791     default:\r
6792       break;\r
6793     }\r
6794     break;\r
6795   }\r
6796   return FALSE;\r
6797 }\r
6798 \r
6799 VOID\r
6800 GothicPopUp(char *title, VariantClass variant)\r
6801 {\r
6802   FARPROC lpProc;\r
6803   static char *lastTitle;\r
6804 \r
6805   strncpy(errorTitle, title, sizeof(errorTitle));\r
6806   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6807 \r
6808   if(lastTitle != title && gothicDialog != NULL) {\r
6809     DestroyWindow(gothicDialog);\r
6810     gothicDialog = NULL;\r
6811   }\r
6812   if(variant != VariantNormal && gothicDialog == NULL) {\r
6813     title = lastTitle;\r
6814     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6815     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6816                  hwndMain, (DLGPROC)lpProc);\r
6817     FreeProcInstance(lpProc);\r
6818   }\r
6819 }\r
6820 #endif\r
6821 \r
6822 /*---------------------------------------------------------------------------*\\r
6823  *\r
6824  *  Ics Interaction console functions\r
6825  *\r
6826 \*---------------------------------------------------------------------------*/\r
6827 \r
6828 #define HISTORY_SIZE 64\r
6829 static char *history[HISTORY_SIZE];\r
6830 int histIn = 0, histP = 0;\r
6831 \r
6832 VOID\r
6833 SaveInHistory(char *cmd)\r
6834 {\r
6835   if (history[histIn] != NULL) {\r
6836     free(history[histIn]);\r
6837     history[histIn] = NULL;\r
6838   }\r
6839   if (*cmd == NULLCHAR) return;\r
6840   history[histIn] = StrSave(cmd);\r
6841   histIn = (histIn + 1) % HISTORY_SIZE;\r
6842   if (history[histIn] != NULL) {\r
6843     free(history[histIn]);\r
6844     history[histIn] = NULL;\r
6845   }\r
6846   histP = histIn;\r
6847 }\r
6848 \r
6849 char *\r
6850 PrevInHistory(char *cmd)\r
6851 {\r
6852   int newhp;\r
6853   if (histP == histIn) {\r
6854     if (history[histIn] != NULL) free(history[histIn]);\r
6855     history[histIn] = StrSave(cmd);\r
6856   }\r
6857   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6858   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6859   histP = newhp;\r
6860   return history[histP];\r
6861 }\r
6862 \r
6863 char *\r
6864 NextInHistory()\r
6865 {\r
6866   if (histP == histIn) return NULL;\r
6867   histP = (histP + 1) % HISTORY_SIZE;\r
6868   return history[histP];   \r
6869 }\r
6870 \r
6871 HMENU\r
6872 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6873 {\r
6874   HMENU hmenu, h;\r
6875   int i = 0;\r
6876   hmenu = LoadMenu(hInst, "TextMenu");\r
6877   h = GetSubMenu(hmenu, 0);\r
6878   while (e->item) {\r
6879     if (strcmp(e->item, "-") == 0) {\r
6880       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6881     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6882       int flags = MF_STRING, j = 0;\r
6883       if (e->item[0] == '|') {\r
6884         flags |= MF_MENUBARBREAK;\r
6885         j++;\r
6886       }\r
6887       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6888       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6889     }\r
6890     e++;\r
6891     i++;\r
6892   } \r
6893   return hmenu;\r
6894 }\r
6895 \r
6896 WNDPROC consoleTextWindowProc;\r
6897 \r
6898 void\r
6899 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6900 {\r
6901   char buf[MSG_SIZ], name[MSG_SIZ];\r
6902   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6903   CHARRANGE sel;\r
6904 \r
6905   if (!getname) {\r
6906     SetWindowText(hInput, command);\r
6907     if (immediate) {\r
6908       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6909     } else {\r
6910       sel.cpMin = 999999;\r
6911       sel.cpMax = 999999;\r
6912       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6913       SetFocus(hInput);\r
6914     }\r
6915     return;\r
6916   }    \r
6917   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6918   if (sel.cpMin == sel.cpMax) {\r
6919     /* Expand to surrounding word */\r
6920     TEXTRANGE tr;\r
6921     do {\r
6922       tr.chrg.cpMax = sel.cpMin;\r
6923       tr.chrg.cpMin = --sel.cpMin;\r
6924       if (sel.cpMin < 0) break;\r
6925       tr.lpstrText = name;\r
6926       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6927     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6928     sel.cpMin++;\r
6929 \r
6930     do {\r
6931       tr.chrg.cpMin = sel.cpMax;\r
6932       tr.chrg.cpMax = ++sel.cpMax;\r
6933       tr.lpstrText = name;\r
6934       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6935     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6936     sel.cpMax--;\r
6937 \r
6938     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6939       MessageBeep(MB_ICONEXCLAMATION);\r
6940       return;\r
6941     }\r
6942     tr.chrg = sel;\r
6943     tr.lpstrText = name;\r
6944     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6945   } else {\r
6946     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6947       MessageBeep(MB_ICONEXCLAMATION);\r
6948       return;\r
6949     }\r
6950     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6951   }\r
6952   if (immediate) {\r
6953     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6954     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6955     SetWindowText(hInput, buf);\r
6956     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6957   } else {\r
6958     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6959       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6960     SetWindowText(hInput, buf);\r
6961     sel.cpMin = 999999;\r
6962     sel.cpMax = 999999;\r
6963     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6964     SetFocus(hInput);\r
6965   }\r
6966 }\r
6967 \r
6968 LRESULT CALLBACK \r
6969 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6970 {\r
6971   HWND hInput;\r
6972   CHARRANGE sel;\r
6973 \r
6974   switch (message) {\r
6975   case WM_KEYDOWN:\r
6976     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6977     if(wParam=='R') return 0;\r
6978     switch (wParam) {\r
6979     case VK_PRIOR:\r
6980       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6981       return 0;\r
6982     case VK_NEXT:\r
6983       sel.cpMin = 999999;\r
6984       sel.cpMax = 999999;\r
6985       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6986       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6987       return 0;\r
6988     }\r
6989     break;\r
6990   case WM_CHAR:\r
6991    if(wParam != '\022') {\r
6992     if (wParam == '\t') {\r
6993       if (GetKeyState(VK_SHIFT) < 0) {\r
6994         /* shifted */\r
6995         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6996         if (buttonDesc[0].hwnd) {\r
6997           SetFocus(buttonDesc[0].hwnd);\r
6998         } else {\r
6999           SetFocus(hwndMain);\r
7000         }\r
7001       } else {\r
7002         /* unshifted */\r
7003         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7004       }\r
7005     } else {\r
7006       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7007       JAWS_DELETE( SetFocus(hInput); )\r
7008       SendMessage(hInput, message, wParam, lParam);\r
7009     }\r
7010     return 0;\r
7011    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7012    lParam = -1;\r
7013   case WM_RBUTTONDOWN:\r
7014     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7015       /* Move selection here if it was empty */\r
7016       POINT pt;\r
7017       pt.x = LOWORD(lParam);\r
7018       pt.y = HIWORD(lParam);\r
7019       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7020       if (sel.cpMin == sel.cpMax) {\r
7021         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7022         sel.cpMax = sel.cpMin;\r
7023         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7024       }\r
7025       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7026 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7027       POINT pt;\r
7028       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7029       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7030       if (sel.cpMin == sel.cpMax) {\r
7031         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7032         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7033       }\r
7034       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7035         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7036       }\r
7037       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7038       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7039       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7040       MenuPopup(hwnd, pt, hmenu, -1);\r
7041 }\r
7042     }\r
7043     return 0;\r
7044   case WM_RBUTTONUP:\r
7045     if (GetKeyState(VK_SHIFT) & ~1) {\r
7046       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7047         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7048     }\r
7049     return 0;\r
7050   case WM_PASTE:\r
7051     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7052     SetFocus(hInput);\r
7053     return SendMessage(hInput, message, wParam, lParam);\r
7054   case WM_MBUTTONDOWN:\r
7055     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7056   case WM_COMMAND:\r
7057     switch (LOWORD(wParam)) {\r
7058     case IDM_QuickPaste:\r
7059       {\r
7060         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7061         if (sel.cpMin == sel.cpMax) {\r
7062           MessageBeep(MB_ICONEXCLAMATION);\r
7063           return 0;\r
7064         }\r
7065         SendMessage(hwnd, WM_COPY, 0, 0);\r
7066         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7067         SendMessage(hInput, WM_PASTE, 0, 0);\r
7068         SetFocus(hInput);\r
7069         return 0;\r
7070       }\r
7071     case IDM_Cut:\r
7072       SendMessage(hwnd, WM_CUT, 0, 0);\r
7073       return 0;\r
7074     case IDM_Paste:\r
7075       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7076       return 0;\r
7077     case IDM_Copy:\r
7078       SendMessage(hwnd, WM_COPY, 0, 0);\r
7079       return 0;\r
7080     default:\r
7081       {\r
7082         int i = LOWORD(wParam) - IDM_CommandX;\r
7083         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7084             icsTextMenuEntry[i].command != NULL) {\r
7085           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7086                    icsTextMenuEntry[i].getname,\r
7087                    icsTextMenuEntry[i].immediate);\r
7088           return 0;\r
7089         }\r
7090       }\r
7091       break;\r
7092     }\r
7093     break;\r
7094   }\r
7095   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7096 }\r
7097 \r
7098 WNDPROC consoleInputWindowProc;\r
7099 \r
7100 LRESULT CALLBACK\r
7101 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7102 {\r
7103   char buf[MSG_SIZ];\r
7104   char *p;\r
7105   static BOOL sendNextChar = FALSE;\r
7106   static BOOL quoteNextChar = FALSE;\r
7107   InputSource *is = consoleInputSource;\r
7108   CHARFORMAT cf;\r
7109   CHARRANGE sel;\r
7110 \r
7111   switch (message) {\r
7112   case WM_CHAR:\r
7113     if (!appData.localLineEditing || sendNextChar) {\r
7114       is->buf[0] = (CHAR) wParam;\r
7115       is->count = 1;\r
7116       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7117       sendNextChar = FALSE;\r
7118       return 0;\r
7119     }\r
7120     if (quoteNextChar) {\r
7121       buf[0] = (char) wParam;\r
7122       buf[1] = NULLCHAR;\r
7123       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7124       quoteNextChar = FALSE;\r
7125       return 0;\r
7126     }\r
7127     switch (wParam) {\r
7128     case '\r':   /* Enter key */\r
7129       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7130       if (consoleEcho) SaveInHistory(is->buf);\r
7131       is->buf[is->count++] = '\n';\r
7132       is->buf[is->count] = NULLCHAR;\r
7133       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7134       if (consoleEcho) {\r
7135         ConsoleOutput(is->buf, is->count, TRUE);\r
7136       } else if (appData.localLineEditing) {\r
7137         ConsoleOutput("\n", 1, TRUE);\r
7138       }\r
7139       /* fall thru */\r
7140     case '\033': /* Escape key */\r
7141       SetWindowText(hwnd, "");\r
7142       cf.cbSize = sizeof(CHARFORMAT);\r
7143       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7144       if (consoleEcho) {\r
7145         cf.crTextColor = textAttribs[ColorNormal].color;\r
7146       } else {\r
7147         cf.crTextColor = COLOR_ECHOOFF;\r
7148       }\r
7149       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7150       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7151       return 0;\r
7152     case '\t':   /* Tab key */\r
7153       if (GetKeyState(VK_SHIFT) < 0) {\r
7154         /* shifted */\r
7155         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7156       } else {\r
7157         /* unshifted */\r
7158         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7159         if (buttonDesc[0].hwnd) {\r
7160           SetFocus(buttonDesc[0].hwnd);\r
7161         } else {\r
7162           SetFocus(hwndMain);\r
7163         }\r
7164       }\r
7165       return 0;\r
7166     case '\023': /* Ctrl+S */\r
7167       sendNextChar = TRUE;\r
7168       return 0;\r
7169     case '\021': /* Ctrl+Q */\r
7170       quoteNextChar = TRUE;\r
7171       return 0;\r
7172     JAWS_REPLAY\r
7173     default:\r
7174       break;\r
7175     }\r
7176     break;\r
7177   case WM_KEYDOWN:\r
7178     switch (wParam) {\r
7179     case VK_UP:\r
7180       GetWindowText(hwnd, buf, MSG_SIZ);\r
7181       p = PrevInHistory(buf);\r
7182       if (p != NULL) {\r
7183         SetWindowText(hwnd, p);\r
7184         sel.cpMin = 999999;\r
7185         sel.cpMax = 999999;\r
7186         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7187         return 0;\r
7188       }\r
7189       break;\r
7190     case VK_DOWN:\r
7191       p = NextInHistory();\r
7192       if (p != NULL) {\r
7193         SetWindowText(hwnd, p);\r
7194         sel.cpMin = 999999;\r
7195         sel.cpMax = 999999;\r
7196         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7197         return 0;\r
7198       }\r
7199       break;\r
7200     case VK_HOME:\r
7201     case VK_END:\r
7202       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7203       /* fall thru */\r
7204     case VK_PRIOR:\r
7205     case VK_NEXT:\r
7206       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7207       return 0;\r
7208     }\r
7209     break;\r
7210   case WM_MBUTTONDOWN:\r
7211     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7212       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7213     break;\r
7214   case WM_RBUTTONUP:\r
7215     if (GetKeyState(VK_SHIFT) & ~1) {\r
7216       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7217         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7218     } else {\r
7219       POINT pt;\r
7220       HMENU hmenu;\r
7221       hmenu = LoadMenu(hInst, "InputMenu");\r
7222       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7223       if (sel.cpMin == sel.cpMax) {\r
7224         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7225         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7226       }\r
7227       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7228         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7229       }\r
7230       pt.x = LOWORD(lParam);\r
7231       pt.y = HIWORD(lParam);\r
7232       MenuPopup(hwnd, pt, hmenu, -1);\r
7233     }\r
7234     return 0;\r
7235   case WM_COMMAND:\r
7236     switch (LOWORD(wParam)) { \r
7237     case IDM_Undo:\r
7238       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7239       return 0;\r
7240     case IDM_SelectAll:\r
7241       sel.cpMin = 0;\r
7242       sel.cpMax = -1; /*999999?*/\r
7243       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7244       return 0;\r
7245     case IDM_Cut:\r
7246       SendMessage(hwnd, WM_CUT, 0, 0);\r
7247       return 0;\r
7248     case IDM_Paste:\r
7249       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7250       return 0;\r
7251     case IDM_Copy:\r
7252       SendMessage(hwnd, WM_COPY, 0, 0);\r
7253       return 0;\r
7254     }\r
7255     break;\r
7256   }\r
7257   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7258 }\r
7259 \r
7260 #define CO_MAX  100000\r
7261 #define CO_TRIM   1000\r
7262 \r
7263 LRESULT CALLBACK\r
7264 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7265 {\r
7266   static SnapData sd;\r
7267   HWND hText, hInput;\r
7268   RECT rect;\r
7269   static int sizeX, sizeY;\r
7270   int newSizeX, newSizeY;\r
7271   MINMAXINFO *mmi;\r
7272   WORD wMask;\r
7273 \r
7274   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7275   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7276 \r
7277   switch (message) {\r
7278   case WM_NOTIFY:\r
7279     if (((NMHDR*)lParam)->code == EN_LINK)\r
7280     {\r
7281       ENLINK *pLink = (ENLINK*)lParam;\r
7282       if (pLink->msg == WM_LBUTTONUP)\r
7283       {\r
7284         TEXTRANGE tr;\r
7285 \r
7286         tr.chrg = pLink->chrg;\r
7287         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7288         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7289         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7290         free(tr.lpstrText);\r
7291       }\r
7292     }\r
7293     break;\r
7294   case WM_INITDIALOG: /* message: initialize dialog box */\r
7295     hwndConsole = hDlg;\r
7296     SetFocus(hInput);\r
7297     consoleTextWindowProc = (WNDPROC)\r
7298       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7299     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7300     consoleInputWindowProc = (WNDPROC)\r
7301       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7302     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7303     Colorize(ColorNormal, TRUE);\r
7304     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7305     ChangedConsoleFont();\r
7306     GetClientRect(hDlg, &rect);\r
7307     sizeX = rect.right;\r
7308     sizeY = rect.bottom;\r
7309     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7310         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7311       WINDOWPLACEMENT wp;\r
7312       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7313       wp.length = sizeof(WINDOWPLACEMENT);\r
7314       wp.flags = 0;\r
7315       wp.showCmd = SW_SHOW;\r
7316       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7317       wp.rcNormalPosition.left = wpConsole.x;\r
7318       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7319       wp.rcNormalPosition.top = wpConsole.y;\r
7320       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7321       SetWindowPlacement(hDlg, &wp);\r
7322     }\r
7323 \r
7324    // [HGM] Chessknight's change 2004-07-13\r
7325    else { /* Determine Defaults */\r
7326        WINDOWPLACEMENT wp;\r
7327        wpConsole.x = wpMain.width + 1;\r
7328        wpConsole.y = wpMain.y;\r
7329        wpConsole.width = screenWidth -  wpMain.width;\r
7330        wpConsole.height = wpMain.height;\r
7331        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7332        wp.length = sizeof(WINDOWPLACEMENT);\r
7333        wp.flags = 0;\r
7334        wp.showCmd = SW_SHOW;\r
7335        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7336        wp.rcNormalPosition.left = wpConsole.x;\r
7337        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7338        wp.rcNormalPosition.top = wpConsole.y;\r
7339        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7340        SetWindowPlacement(hDlg, &wp);\r
7341     }\r
7342 \r
7343    // Allow hText to highlight URLs and send notifications on them\r
7344    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7345    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7346    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7347    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7348 \r
7349     return FALSE;\r
7350 \r
7351   case WM_SETFOCUS:\r
7352     SetFocus(hInput);\r
7353     return 0;\r
7354 \r
7355   case WM_CLOSE:\r
7356     ExitEvent(0);\r
7357     /* not reached */\r
7358     break;\r
7359 \r
7360   case WM_SIZE:\r
7361     if (IsIconic(hDlg)) break;\r
7362     newSizeX = LOWORD(lParam);\r
7363     newSizeY = HIWORD(lParam);\r
7364     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7365       RECT rectText, rectInput;\r
7366       POINT pt;\r
7367       int newTextHeight, newTextWidth;\r
7368       GetWindowRect(hText, &rectText);\r
7369       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7370       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7371       if (newTextHeight < 0) {\r
7372         newSizeY += -newTextHeight;\r
7373         newTextHeight = 0;\r
7374       }\r
7375       SetWindowPos(hText, NULL, 0, 0,\r
7376         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7377       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7378       pt.x = rectInput.left;\r
7379       pt.y = rectInput.top + newSizeY - sizeY;\r
7380       ScreenToClient(hDlg, &pt);\r
7381       SetWindowPos(hInput, NULL, \r
7382         pt.x, pt.y, /* needs client coords */   \r
7383         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7384         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7385     }\r
7386     sizeX = newSizeX;\r
7387     sizeY = newSizeY;\r
7388     break;\r
7389 \r
7390   case WM_GETMINMAXINFO:\r
7391     /* Prevent resizing window too small */\r
7392     mmi = (MINMAXINFO *) lParam;\r
7393     mmi->ptMinTrackSize.x = 100;\r
7394     mmi->ptMinTrackSize.y = 100;\r
7395     break;\r
7396 \r
7397   /* [AS] Snapping */\r
7398   case WM_ENTERSIZEMOVE:\r
7399     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7400 \r
7401   case WM_SIZING:\r
7402     return OnSizing( &sd, hDlg, wParam, lParam );\r
7403 \r
7404   case WM_MOVING:\r
7405     return OnMoving( &sd, hDlg, wParam, lParam );\r
7406 \r
7407   case WM_EXITSIZEMOVE:\r
7408         UpdateICSWidth(hText);\r
7409     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7410   }\r
7411 \r
7412   return DefWindowProc(hDlg, message, wParam, lParam);\r
7413 }\r
7414 \r
7415 \r
7416 VOID\r
7417 ConsoleCreate()\r
7418 {\r
7419   HWND hCons;\r
7420   if (hwndConsole) return;\r
7421   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7422   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7423 }\r
7424 \r
7425 \r
7426 VOID\r
7427 ConsoleOutput(char* data, int length, int forceVisible)\r
7428 {\r
7429   HWND hText;\r
7430   int trim, exlen;\r
7431   char *p, *q;\r
7432   char buf[CO_MAX+1];\r
7433   POINT pEnd;\r
7434   RECT rect;\r
7435   static int delayLF = 0;\r
7436   CHARRANGE savesel, sel;\r
7437 \r
7438   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7439   p = data;\r
7440   q = buf;\r
7441   if (delayLF) {\r
7442     *q++ = '\r';\r
7443     *q++ = '\n';\r
7444     delayLF = 0;\r
7445   }\r
7446   while (length--) {\r
7447     if (*p == '\n') {\r
7448       if (*++p) {\r
7449         *q++ = '\r';\r
7450         *q++ = '\n';\r
7451       } else {\r
7452         delayLF = 1;\r
7453       }\r
7454     } else if (*p == '\007') {\r
7455        MyPlaySound(&sounds[(int)SoundBell]);\r
7456        p++;\r
7457     } else {\r
7458       *q++ = *p++;\r
7459     }\r
7460   }\r
7461   *q = NULLCHAR;\r
7462   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7463   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7464   /* Save current selection */\r
7465   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7466   exlen = GetWindowTextLength(hText);\r
7467   /* Find out whether current end of text is visible */\r
7468   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7469   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7470   /* Trim existing text if it's too long */\r
7471   if (exlen + (q - buf) > CO_MAX) {\r
7472     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7473     sel.cpMin = 0;\r
7474     sel.cpMax = trim;\r
7475     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7476     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7477     exlen -= trim;\r
7478     savesel.cpMin -= trim;\r
7479     savesel.cpMax -= trim;\r
7480     if (exlen < 0) exlen = 0;\r
7481     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7482     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7483   }\r
7484   /* Append the new text */\r
7485   sel.cpMin = exlen;\r
7486   sel.cpMax = exlen;\r
7487   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7488   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7489   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7490   if (forceVisible || exlen == 0 ||\r
7491       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7492        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7493     /* Scroll to make new end of text visible if old end of text\r
7494        was visible or new text is an echo of user typein */\r
7495     sel.cpMin = 9999999;\r
7496     sel.cpMax = 9999999;\r
7497     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7498     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7499     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7500     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7501   }\r
7502   if (savesel.cpMax == exlen || forceVisible) {\r
7503     /* Move insert point to new end of text if it was at the old\r
7504        end of text or if the new text is an echo of user typein */\r
7505     sel.cpMin = 9999999;\r
7506     sel.cpMax = 9999999;\r
7507     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7508   } else {\r
7509     /* Restore previous selection */\r
7510     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7511   }\r
7512   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7513 }\r
7514 \r
7515 /*---------*/\r
7516 \r
7517 \r
7518 void\r
7519 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7520 {\r
7521   char buf[100];\r
7522   char *str;\r
7523   COLORREF oldFg, oldBg;\r
7524   HFONT oldFont;\r
7525   RECT rect;\r
7526 \r
7527   if(copyNumber > 1)\r
7528     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7529 \r
7530   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7531   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7532   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7533 \r
7534   rect.left = x;\r
7535   rect.right = x + squareSize;\r
7536   rect.top  = y;\r
7537   rect.bottom = y + squareSize;\r
7538   str = buf;\r
7539 \r
7540   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7541                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7542              y, ETO_CLIPPED|ETO_OPAQUE,\r
7543              &rect, str, strlen(str), NULL);\r
7544 \r
7545   (void) SetTextColor(hdc, oldFg);\r
7546   (void) SetBkColor(hdc, oldBg);\r
7547   (void) SelectObject(hdc, oldFont);\r
7548 }\r
7549 \r
7550 void\r
7551 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7552               RECT *rect, char *color, char *flagFell)\r
7553 {\r
7554   char buf[100];\r
7555   char *str;\r
7556   COLORREF oldFg, oldBg;\r
7557   HFONT oldFont;\r
7558 \r
7559   if (twoBoards && partnerUp) return;\r
7560   if (appData.clockMode) {\r
7561     if (tinyLayout)\r
7562       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7563     else\r
7564       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7565     str = buf;\r
7566   } else {\r
7567     str = color;\r
7568   }\r
7569 \r
7570   if (highlight) {\r
7571     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7572     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7573   } else {\r
7574     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7575     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7576   }\r
7577   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7578 \r
7579   JAWS_SILENCE\r
7580 \r
7581   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7582              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7583              rect, str, strlen(str), NULL);\r
7584   if(logoHeight > 0 && appData.clockMode) {\r
7585       RECT r;\r
7586       str += strlen(color)+2;\r
7587       r.top = rect->top + logoHeight/2;\r
7588       r.left = rect->left;\r
7589       r.right = rect->right;\r
7590       r.bottom = rect->bottom;\r
7591       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7592                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7593                  &r, str, strlen(str), NULL);\r
7594   }\r
7595   (void) SetTextColor(hdc, oldFg);\r
7596   (void) SetBkColor(hdc, oldBg);\r
7597   (void) SelectObject(hdc, oldFont);\r
7598 }\r
7599 \r
7600 \r
7601 int\r
7602 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7603            OVERLAPPED *ovl)\r
7604 {\r
7605   int ok, err;\r
7606 \r
7607   /* [AS]  */\r
7608   if( count <= 0 ) {\r
7609     if (appData.debugMode) {\r
7610       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7611     }\r
7612 \r
7613     return ERROR_INVALID_USER_BUFFER;\r
7614   }\r
7615 \r
7616   ResetEvent(ovl->hEvent);\r
7617   ovl->Offset = ovl->OffsetHigh = 0;\r
7618   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7619   if (ok) {\r
7620     err = NO_ERROR;\r
7621   } else {\r
7622     err = GetLastError();\r
7623     if (err == ERROR_IO_PENDING) {\r
7624       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7625       if (ok)\r
7626         err = NO_ERROR;\r
7627       else\r
7628         err = GetLastError();\r
7629     }\r
7630   }\r
7631   return err;\r
7632 }\r
7633 \r
7634 int\r
7635 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7636             OVERLAPPED *ovl)\r
7637 {\r
7638   int ok, err;\r
7639 \r
7640   ResetEvent(ovl->hEvent);\r
7641   ovl->Offset = ovl->OffsetHigh = 0;\r
7642   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7643   if (ok) {\r
7644     err = NO_ERROR;\r
7645   } else {\r
7646     err = GetLastError();\r
7647     if (err == ERROR_IO_PENDING) {\r
7648       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7649       if (ok)\r
7650         err = NO_ERROR;\r
7651       else\r
7652         err = GetLastError();\r
7653     }\r
7654   }\r
7655   return err;\r
7656 }\r
7657 \r
7658 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7659 void CheckForInputBufferFull( InputSource * is )\r
7660 {\r
7661     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7662         /* Look for end of line */\r
7663         char * p = is->buf;\r
7664         \r
7665         while( p < is->next && *p != '\n' ) {\r
7666             p++;\r
7667         }\r
7668 \r
7669         if( p >= is->next ) {\r
7670             if (appData.debugMode) {\r
7671                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7672             }\r
7673 \r
7674             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7675             is->count = (DWORD) -1;\r
7676             is->next = is->buf;\r
7677         }\r
7678     }\r
7679 }\r
7680 \r
7681 DWORD\r
7682 InputThread(LPVOID arg)\r
7683 {\r
7684   InputSource *is;\r
7685   OVERLAPPED ovl;\r
7686 \r
7687   is = (InputSource *) arg;\r
7688   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7689   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7690   while (is->hThread != NULL) {\r
7691     is->error = DoReadFile(is->hFile, is->next,\r
7692                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7693                            &is->count, &ovl);\r
7694     if (is->error == NO_ERROR) {\r
7695       is->next += is->count;\r
7696     } else {\r
7697       if (is->error == ERROR_BROKEN_PIPE) {\r
7698         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7699         is->count = 0;\r
7700       } else {\r
7701         is->count = (DWORD) -1;\r
7702         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7703         break; \r
7704       }\r
7705     }\r
7706 \r
7707     CheckForInputBufferFull( is );\r
7708 \r
7709     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7710 \r
7711     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7712 \r
7713     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7714   }\r
7715 \r
7716   CloseHandle(ovl.hEvent);\r
7717   CloseHandle(is->hFile);\r
7718 \r
7719   if (appData.debugMode) {\r
7720     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7721   }\r
7722 \r
7723   return 0;\r
7724 }\r
7725 \r
7726 \r
7727 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7728 DWORD\r
7729 NonOvlInputThread(LPVOID arg)\r
7730 {\r
7731   InputSource *is;\r
7732   char *p, *q;\r
7733   int i;\r
7734   char prev;\r
7735 \r
7736   is = (InputSource *) arg;\r
7737   while (is->hThread != NULL) {\r
7738     is->error = ReadFile(is->hFile, is->next,\r
7739                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7740                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7741     if (is->error == NO_ERROR) {\r
7742       /* Change CRLF to LF */\r
7743       if (is->next > is->buf) {\r
7744         p = is->next - 1;\r
7745         i = is->count + 1;\r
7746       } else {\r
7747         p = is->next;\r
7748         i = is->count;\r
7749       }\r
7750       q = p;\r
7751       prev = NULLCHAR;\r
7752       while (i > 0) {\r
7753         if (prev == '\r' && *p == '\n') {\r
7754           *(q-1) = '\n';\r
7755           is->count--;\r
7756         } else { \r
7757           *q++ = *p;\r
7758         }\r
7759         prev = *p++;\r
7760         i--;\r
7761       }\r
7762       *q = NULLCHAR;\r
7763       is->next = q;\r
7764     } else {\r
7765       if (is->error == ERROR_BROKEN_PIPE) {\r
7766         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7767         is->count = 0; \r
7768       } else {\r
7769         is->count = (DWORD) -1;\r
7770       }\r
7771     }\r
7772 \r
7773     CheckForInputBufferFull( is );\r
7774 \r
7775     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7776 \r
7777     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7778 \r
7779     if (is->count < 0) break;  /* Quit on error */\r
7780   }\r
7781   CloseHandle(is->hFile);\r
7782   return 0;\r
7783 }\r
7784 \r
7785 DWORD\r
7786 SocketInputThread(LPVOID arg)\r
7787 {\r
7788   InputSource *is;\r
7789 \r
7790   is = (InputSource *) arg;\r
7791   while (is->hThread != NULL) {\r
7792     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7793     if ((int)is->count == SOCKET_ERROR) {\r
7794       is->count = (DWORD) -1;\r
7795       is->error = WSAGetLastError();\r
7796     } else {\r
7797       is->error = NO_ERROR;\r
7798       is->next += is->count;\r
7799       if (is->count == 0 && is->second == is) {\r
7800         /* End of file on stderr; quit with no message */\r
7801         break;\r
7802       }\r
7803     }\r
7804     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7805 \r
7806     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7807 \r
7808     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7809   }\r
7810   return 0;\r
7811 }\r
7812 \r
7813 VOID\r
7814 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7815 {\r
7816   InputSource *is;\r
7817 \r
7818   is = (InputSource *) lParam;\r
7819   if (is->lineByLine) {\r
7820     /* Feed in lines one by one */\r
7821     char *p = is->buf;\r
7822     char *q = p;\r
7823     while (q < is->next) {\r
7824       if (*q++ == '\n') {\r
7825         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7826         p = q;\r
7827       }\r
7828     }\r
7829     \r
7830     /* Move any partial line to the start of the buffer */\r
7831     q = is->buf;\r
7832     while (p < is->next) {\r
7833       *q++ = *p++;\r
7834     }\r
7835     is->next = q;\r
7836 \r
7837     if (is->error != NO_ERROR || is->count == 0) {\r
7838       /* Notify backend of the error.  Note: If there was a partial\r
7839          line at the end, it is not flushed through. */\r
7840       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7841     }\r
7842   } else {\r
7843     /* Feed in the whole chunk of input at once */\r
7844     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7845     is->next = is->buf;\r
7846   }\r
7847 }\r
7848 \r
7849 /*---------------------------------------------------------------------------*\\r
7850  *\r
7851  *  Menu enables. Used when setting various modes.\r
7852  *\r
7853 \*---------------------------------------------------------------------------*/\r
7854 \r
7855 typedef struct {\r
7856   int item;\r
7857   int flags;\r
7858 } Enables;\r
7859 \r
7860 VOID\r
7861 GreyRevert(Boolean grey)\r
7862 { // [HGM] vari: for retracting variations in local mode\r
7863   HMENU hmenu = GetMenu(hwndMain);\r
7864   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7865   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7866 }\r
7867 \r
7868 VOID\r
7869 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7870 {\r
7871   while (enab->item > 0) {\r
7872     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7873     enab++;\r
7874   }\r
7875 }\r
7876 \r
7877 Enables gnuEnables[] = {\r
7878   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7886   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7891 \r
7892   // Needed to switch from ncp to GNU mode on Engine Load\r
7893   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7894   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7895   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7896   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7897   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7898   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7899   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7900   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7901   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7902   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7903   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7907   { -1, -1 }\r
7908 };\r
7909 \r
7910 Enables icsEnables[] = {\r
7911   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7913   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7919   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7925   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7929   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7931   { -1, -1 }\r
7932 };\r
7933 \r
7934 #if ZIPPY\r
7935 Enables zippyEnables[] = {\r
7936   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7937   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7938   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7939   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7940   { -1, -1 }\r
7941 };\r
7942 #endif\r
7943 \r
7944 Enables ncpEnables[] = {\r
7945   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7954   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7955   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7967   { -1, -1 }\r
7968 };\r
7969 \r
7970 Enables trainingOnEnables[] = {\r
7971   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7980   { -1, -1 }\r
7981 };\r
7982 \r
7983 Enables trainingOffEnables[] = {\r
7984   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7989   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7990   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7991   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7992   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7993   { -1, -1 }\r
7994 };\r
7995 \r
7996 /* These modify either ncpEnables or gnuEnables */\r
7997 Enables cmailEnables[] = {\r
7998   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7999   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8000   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8001   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8002   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8003   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8004   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8005   { -1, -1 }\r
8006 };\r
8007 \r
8008 Enables machineThinkingEnables[] = {\r
8009   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8016   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8017   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8018   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8019   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8020   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8022 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8025   { -1, -1 }\r
8026 };\r
8027 \r
8028 Enables userThinkingEnables[] = {\r
8029   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8030   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8031   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8032   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8033   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8034   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8035   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8042 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8043   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8044   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8045   { -1, -1 }\r
8046 };\r
8047 \r
8048 /*---------------------------------------------------------------------------*\\r
8049  *\r
8050  *  Front-end interface functions exported by XBoard.\r
8051  *  Functions appear in same order as prototypes in frontend.h.\r
8052  * \r
8053 \*---------------------------------------------------------------------------*/\r
8054 VOID\r
8055 CheckMark(UINT item, int state)\r
8056 {\r
8057     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8058 }\r
8059 \r
8060 VOID\r
8061 ModeHighlight()\r
8062 {\r
8063   static UINT prevChecked = 0;\r
8064   static int prevPausing = 0;\r
8065   UINT nowChecked;\r
8066 \r
8067   if (pausing != prevPausing) {\r
8068     prevPausing = pausing;\r
8069     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8070                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8071     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8072   }\r
8073 \r
8074   switch (gameMode) {\r
8075   case BeginningOfGame:\r
8076     if (appData.icsActive)\r
8077       nowChecked = IDM_IcsClient;\r
8078     else if (appData.noChessProgram)\r
8079       nowChecked = IDM_EditGame;\r
8080     else\r
8081       nowChecked = IDM_MachineBlack;\r
8082     break;\r
8083   case MachinePlaysBlack:\r
8084     nowChecked = IDM_MachineBlack;\r
8085     break;\r
8086   case MachinePlaysWhite:\r
8087     nowChecked = IDM_MachineWhite;\r
8088     break;\r
8089   case TwoMachinesPlay:\r
8090     nowChecked = IDM_TwoMachines;\r
8091     break;\r
8092   case AnalyzeMode:\r
8093     nowChecked = IDM_AnalysisMode;\r
8094     break;\r
8095   case AnalyzeFile:\r
8096     nowChecked = IDM_AnalyzeFile;\r
8097     break;\r
8098   case EditGame:\r
8099     nowChecked = IDM_EditGame;\r
8100     break;\r
8101   case PlayFromGameFile:\r
8102     nowChecked = IDM_LoadGame;\r
8103     break;\r
8104   case EditPosition:\r
8105     nowChecked = IDM_EditPosition;\r
8106     break;\r
8107   case Training:\r
8108     nowChecked = IDM_Training;\r
8109     break;\r
8110   case IcsPlayingWhite:\r
8111   case IcsPlayingBlack:\r
8112   case IcsObserving:\r
8113   case IcsIdle:\r
8114     nowChecked = IDM_IcsClient;\r
8115     break;\r
8116   default:\r
8117   case EndOfGame:\r
8118     nowChecked = 0;\r
8119     break;\r
8120   }\r
8121   CheckMark(prevChecked, MF_UNCHECKED);\r
8122   CheckMark(nowChecked, MF_CHECKED);\r
8123   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8124 \r
8125   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8126     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8127                           MF_BYCOMMAND|MF_ENABLED);\r
8128   } else {\r
8129     (void) EnableMenuItem(GetMenu(hwndMain), \r
8130                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8131   }\r
8132 \r
8133   prevChecked = nowChecked;\r
8134 \r
8135   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8136   if (appData.icsActive) {\r
8137        if (appData.icsEngineAnalyze) {\r
8138                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8139        } else {\r
8140                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8141        }\r
8142   }\r
8143   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8144 }\r
8145 \r
8146 VOID\r
8147 SetICSMode()\r
8148 {\r
8149   HMENU hmenu = GetMenu(hwndMain);\r
8150   SetMenuEnables(hmenu, icsEnables);\r
8151   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8152     MF_BYCOMMAND|MF_ENABLED);\r
8153 #if ZIPPY\r
8154   if (appData.zippyPlay) {\r
8155     SetMenuEnables(hmenu, zippyEnables);\r
8156     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8157          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8158           MF_BYCOMMAND|MF_ENABLED);\r
8159   }\r
8160 #endif\r
8161 }\r
8162 \r
8163 VOID\r
8164 SetGNUMode()\r
8165 {\r
8166   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8167 }\r
8168 \r
8169 VOID\r
8170 SetNCPMode()\r
8171 {\r
8172   HMENU hmenu = GetMenu(hwndMain);\r
8173   SetMenuEnables(hmenu, ncpEnables);\r
8174     DrawMenuBar(hwndMain);\r
8175 }\r
8176 \r
8177 VOID\r
8178 SetCmailMode()\r
8179 {\r
8180   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8181 }\r
8182 \r
8183 VOID \r
8184 SetTrainingModeOn()\r
8185 {\r
8186   int i;\r
8187   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8188   for (i = 0; i < N_BUTTONS; i++) {\r
8189     if (buttonDesc[i].hwnd != NULL)\r
8190       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8191   }\r
8192   CommentPopDown();\r
8193 }\r
8194 \r
8195 VOID SetTrainingModeOff()\r
8196 {\r
8197   int i;\r
8198   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8199   for (i = 0; i < N_BUTTONS; i++) {\r
8200     if (buttonDesc[i].hwnd != NULL)\r
8201       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8202   }\r
8203 }\r
8204 \r
8205 \r
8206 VOID\r
8207 SetUserThinkingEnables()\r
8208 {\r
8209   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8210 }\r
8211 \r
8212 VOID\r
8213 SetMachineThinkingEnables()\r
8214 {\r
8215   HMENU hMenu = GetMenu(hwndMain);\r
8216   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8217 \r
8218   SetMenuEnables(hMenu, machineThinkingEnables);\r
8219 \r
8220   if (gameMode == MachinePlaysBlack) {\r
8221     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8222   } else if (gameMode == MachinePlaysWhite) {\r
8223     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8224   } else if (gameMode == TwoMachinesPlay) {\r
8225     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8226   }\r
8227 }\r
8228 \r
8229 \r
8230 VOID\r
8231 DisplayTitle(char *str)\r
8232 {\r
8233   char title[MSG_SIZ], *host;\r
8234   if (str[0] != NULLCHAR) {\r
8235     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8236   } else if (appData.icsActive) {\r
8237     if (appData.icsCommPort[0] != NULLCHAR)\r
8238       host = "ICS";\r
8239     else \r
8240       host = appData.icsHost;\r
8241       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8242   } else if (appData.noChessProgram) {\r
8243     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8244   } else {\r
8245     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8246     strcat(title, ": ");\r
8247     strcat(title, first.tidy);\r
8248   }\r
8249   SetWindowText(hwndMain, title);\r
8250 }\r
8251 \r
8252 \r
8253 VOID\r
8254 DisplayMessage(char *str1, char *str2)\r
8255 {\r
8256   HDC hdc;\r
8257   HFONT oldFont;\r
8258   int remain = MESSAGE_TEXT_MAX - 1;\r
8259   int len;\r
8260 \r
8261   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8262   messageText[0] = NULLCHAR;\r
8263   if (*str1) {\r
8264     len = strlen(str1);\r
8265     if (len > remain) len = remain;\r
8266     strncpy(messageText, str1, len);\r
8267     messageText[len] = NULLCHAR;\r
8268     remain -= len;\r
8269   }\r
8270   if (*str2 && remain >= 2) {\r
8271     if (*str1) {\r
8272       strcat(messageText, "  ");\r
8273       remain -= 2;\r
8274     }\r
8275     len = strlen(str2);\r
8276     if (len > remain) len = remain;\r
8277     strncat(messageText, str2, len);\r
8278   }\r
8279   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8280   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8281 \r
8282   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8283 \r
8284   SAYMACHINEMOVE();\r
8285 \r
8286   hdc = GetDC(hwndMain);\r
8287   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8288   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8289              &messageRect, messageText, strlen(messageText), NULL);\r
8290   (void) SelectObject(hdc, oldFont);\r
8291   (void) ReleaseDC(hwndMain, hdc);\r
8292 }\r
8293 \r
8294 VOID\r
8295 DisplayError(char *str, int error)\r
8296 {\r
8297   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8298   int len;\r
8299 \r
8300   if (error == 0) {\r
8301     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8302   } else {\r
8303     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8304                         NULL, error, LANG_NEUTRAL,\r
8305                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8306     if (len > 0) {\r
8307       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8308     } else {\r
8309       ErrorMap *em = errmap;\r
8310       while (em->err != 0 && em->err != error) em++;\r
8311       if (em->err != 0) {\r
8312         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8313       } else {\r
8314         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8315       }\r
8316     }\r
8317   }\r
8318   \r
8319   ErrorPopUp(_("Error"), buf);\r
8320 }\r
8321 \r
8322 \r
8323 VOID\r
8324 DisplayMoveError(char *str)\r
8325 {\r
8326   fromX = fromY = -1;\r
8327   ClearHighlights();\r
8328   DrawPosition(FALSE, NULL);\r
8329   if (appData.popupMoveErrors) {\r
8330     ErrorPopUp(_("Error"), str);\r
8331   } else {\r
8332     DisplayMessage(str, "");\r
8333     moveErrorMessageUp = TRUE;\r
8334   }\r
8335 }\r
8336 \r
8337 VOID\r
8338 DisplayFatalError(char *str, int error, int exitStatus)\r
8339 {\r
8340   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8341   int len;\r
8342   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8343 \r
8344   if (error != 0) {\r
8345     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8346                         NULL, error, LANG_NEUTRAL,\r
8347                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8348     if (len > 0) {\r
8349       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8350     } else {\r
8351       ErrorMap *em = errmap;\r
8352       while (em->err != 0 && em->err != error) em++;\r
8353       if (em->err != 0) {\r
8354         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8355       } else {\r
8356         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8357       }\r
8358     }\r
8359     str = buf;\r
8360   }\r
8361   if (appData.debugMode) {\r
8362     fprintf(debugFP, "%s: %s\n", label, str);\r
8363   }\r
8364   if (appData.popupExitMessage) {\r
8365     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8366                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8367   }\r
8368   ExitEvent(exitStatus);\r
8369 }\r
8370 \r
8371 \r
8372 VOID\r
8373 DisplayInformation(char *str)\r
8374 {\r
8375   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8376 }\r
8377 \r
8378 \r
8379 VOID\r
8380 DisplayNote(char *str)\r
8381 {\r
8382   ErrorPopUp(_("Note"), str);\r
8383 }\r
8384 \r
8385 \r
8386 typedef struct {\r
8387   char *title, *question, *replyPrefix;\r
8388   ProcRef pr;\r
8389 } QuestionParams;\r
8390 \r
8391 LRESULT CALLBACK\r
8392 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8393 {\r
8394   static QuestionParams *qp;\r
8395   char reply[MSG_SIZ];\r
8396   int len, err;\r
8397 \r
8398   switch (message) {\r
8399   case WM_INITDIALOG:\r
8400     qp = (QuestionParams *) lParam;\r
8401     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8402     Translate(hDlg, DLG_Question);\r
8403     SetWindowText(hDlg, qp->title);\r
8404     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8405     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8406     return FALSE;\r
8407 \r
8408   case WM_COMMAND:\r
8409     switch (LOWORD(wParam)) {\r
8410     case IDOK:\r
8411       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8412       if (*reply) strcat(reply, " ");\r
8413       len = strlen(reply);\r
8414       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8415       strcat(reply, "\n");\r
8416       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8417       EndDialog(hDlg, TRUE);\r
8418       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8419       return TRUE;\r
8420     case IDCANCEL:\r
8421       EndDialog(hDlg, FALSE);\r
8422       return TRUE;\r
8423     default:\r
8424       break;\r
8425     }\r
8426     break;\r
8427   }\r
8428   return FALSE;\r
8429 }\r
8430 \r
8431 VOID\r
8432 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8433 {\r
8434     QuestionParams qp;\r
8435     FARPROC lpProc;\r
8436     \r
8437     qp.title = title;\r
8438     qp.question = question;\r
8439     qp.replyPrefix = replyPrefix;\r
8440     qp.pr = pr;\r
8441     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8442     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8443       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8444     FreeProcInstance(lpProc);\r
8445 }\r
8446 \r
8447 /* [AS] Pick FRC position */\r
8448 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8449 {\r
8450     static int * lpIndexFRC;\r
8451     BOOL index_is_ok;\r
8452     char buf[16];\r
8453 \r
8454     switch( message )\r
8455     {\r
8456     case WM_INITDIALOG:\r
8457         lpIndexFRC = (int *) lParam;\r
8458 \r
8459         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8460         Translate(hDlg, DLG_NewGameFRC);\r
8461 \r
8462         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8463         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8464         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8465         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8466 \r
8467         break;\r
8468 \r
8469     case WM_COMMAND:\r
8470         switch( LOWORD(wParam) ) {\r
8471         case IDOK:\r
8472             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8473             EndDialog( hDlg, 0 );\r
8474             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8475             return TRUE;\r
8476         case IDCANCEL:\r
8477             EndDialog( hDlg, 1 );   \r
8478             return TRUE;\r
8479         case IDC_NFG_Edit:\r
8480             if( HIWORD(wParam) == EN_CHANGE ) {\r
8481                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8482 \r
8483                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8484             }\r
8485             return TRUE;\r
8486         case IDC_NFG_Random:\r
8487           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8488             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8489             return TRUE;\r
8490         }\r
8491 \r
8492         break;\r
8493     }\r
8494 \r
8495     return FALSE;\r
8496 }\r
8497 \r
8498 int NewGameFRC()\r
8499 {\r
8500     int result;\r
8501     int index = appData.defaultFrcPosition;\r
8502     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8503 \r
8504     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8505 \r
8506     if( result == 0 ) {\r
8507         appData.defaultFrcPosition = index;\r
8508     }\r
8509 \r
8510     return result;\r
8511 }\r
8512 \r
8513 /* [AS] Game list options. Refactored by HGM */\r
8514 \r
8515 HWND gameListOptionsDialog;\r
8516 \r
8517 // low-level front-end: clear text edit / list widget\r
8518 void\r
8519 GLT_ClearList()\r
8520 {\r
8521     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8522 }\r
8523 \r
8524 // low-level front-end: clear text edit / list widget\r
8525 void\r
8526 GLT_DeSelectList()\r
8527 {\r
8528     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8529 }\r
8530 \r
8531 // low-level front-end: append line to text edit / list widget\r
8532 void\r
8533 GLT_AddToList( char *name )\r
8534 {\r
8535     if( name != 0 ) {\r
8536             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8537     }\r
8538 }\r
8539 \r
8540 // low-level front-end: get line from text edit / list widget\r
8541 Boolean\r
8542 GLT_GetFromList( int index, char *name )\r
8543 {\r
8544     if( name != 0 ) {\r
8545             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8546                 return TRUE;\r
8547     }\r
8548     return FALSE;\r
8549 }\r
8550 \r
8551 void GLT_MoveSelection( HWND hDlg, int delta )\r
8552 {\r
8553     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8554     int idx2 = idx1 + delta;\r
8555     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8556 \r
8557     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8558         char buf[128];\r
8559 \r
8560         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8561         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8562         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8563         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8564     }\r
8565 }\r
8566 \r
8567 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8568 {\r
8569     switch( message )\r
8570     {\r
8571     case WM_INITDIALOG:\r
8572         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8573         \r
8574         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8575         Translate(hDlg, DLG_GameListOptions);\r
8576 \r
8577         /* Initialize list */\r
8578         GLT_TagsToList( lpUserGLT );\r
8579 \r
8580         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8581 \r
8582         break;\r
8583 \r
8584     case WM_COMMAND:\r
8585         switch( LOWORD(wParam) ) {\r
8586         case IDOK:\r
8587             GLT_ParseList();\r
8588             EndDialog( hDlg, 0 );\r
8589             return TRUE;\r
8590         case IDCANCEL:\r
8591             EndDialog( hDlg, 1 );\r
8592             return TRUE;\r
8593 \r
8594         case IDC_GLT_Default:\r
8595             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8596             return TRUE;\r
8597 \r
8598         case IDC_GLT_Restore:\r
8599             GLT_TagsToList( appData.gameListTags );\r
8600             return TRUE;\r
8601 \r
8602         case IDC_GLT_Up:\r
8603             GLT_MoveSelection( hDlg, -1 );\r
8604             return TRUE;\r
8605 \r
8606         case IDC_GLT_Down:\r
8607             GLT_MoveSelection( hDlg, +1 );\r
8608             return TRUE;\r
8609         }\r
8610 \r
8611         break;\r
8612     }\r
8613 \r
8614     return FALSE;\r
8615 }\r
8616 \r
8617 int GameListOptions()\r
8618 {\r
8619     int result;\r
8620     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8621 \r
8622       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8623 \r
8624     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8625 \r
8626     if( result == 0 ) {\r
8627         /* [AS] Memory leak here! */\r
8628         appData.gameListTags = strdup( lpUserGLT ); \r
8629     }\r
8630 \r
8631     return result;\r
8632 }\r
8633 \r
8634 VOID\r
8635 DisplayIcsInteractionTitle(char *str)\r
8636 {\r
8637   char consoleTitle[MSG_SIZ];\r
8638 \r
8639     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8640     SetWindowText(hwndConsole, consoleTitle);\r
8641 \r
8642     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8643       char buf[MSG_SIZ], *p = buf, *q;\r
8644         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8645       do {\r
8646         q = strchr(p, ';');\r
8647         if(q) *q++ = 0;\r
8648         if(*p) ChatPopUp(p);\r
8649       } while(p=q);\r
8650     }\r
8651 \r
8652     SetActiveWindow(hwndMain);\r
8653 }\r
8654 \r
8655 void\r
8656 DrawPosition(int fullRedraw, Board board)\r
8657 {\r
8658   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8659 }\r
8660 \r
8661 void NotifyFrontendLogin()\r
8662 {\r
8663         if (hwndConsole)\r
8664                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8665 }\r
8666 \r
8667 VOID\r
8668 ResetFrontEnd()\r
8669 {\r
8670   fromX = fromY = -1;\r
8671   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8672     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8673     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8674     dragInfo.lastpos = dragInfo.pos;\r
8675     dragInfo.start.x = dragInfo.start.y = -1;\r
8676     dragInfo.from = dragInfo.start;\r
8677     ReleaseCapture();\r
8678     DrawPosition(TRUE, NULL);\r
8679   }\r
8680   TagsPopDown();\r
8681 }\r
8682 \r
8683 \r
8684 VOID\r
8685 CommentPopUp(char *title, char *str)\r
8686 {\r
8687   HWND hwnd = GetActiveWindow();\r
8688   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8689   SAY(str);\r
8690   SetActiveWindow(hwnd);\r
8691 }\r
8692 \r
8693 VOID\r
8694 CommentPopDown(void)\r
8695 {\r
8696   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8697   if (commentDialog) {\r
8698     ShowWindow(commentDialog, SW_HIDE);\r
8699   }\r
8700   commentUp = FALSE;\r
8701 }\r
8702 \r
8703 VOID\r
8704 EditCommentPopUp(int index, char *title, char *str)\r
8705 {\r
8706   EitherCommentPopUp(index, title, str, TRUE);\r
8707 }\r
8708 \r
8709 \r
8710 VOID\r
8711 RingBell()\r
8712 {\r
8713   MyPlaySound(&sounds[(int)SoundMove]);\r
8714 }\r
8715 \r
8716 VOID PlayIcsWinSound()\r
8717 {\r
8718   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8719 }\r
8720 \r
8721 VOID PlayIcsLossSound()\r
8722 {\r
8723   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8724 }\r
8725 \r
8726 VOID PlayIcsDrawSound()\r
8727 {\r
8728   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8729 }\r
8730 \r
8731 VOID PlayIcsUnfinishedSound()\r
8732 {\r
8733   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8734 }\r
8735 \r
8736 VOID\r
8737 PlayAlarmSound()\r
8738 {\r
8739   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8740 }\r
8741 \r
8742 VOID\r
8743 PlayTellSound()\r
8744 {\r
8745   MyPlaySound(&textAttribs[ColorTell].sound);\r
8746 }\r
8747 \r
8748 \r
8749 VOID\r
8750 EchoOn()\r
8751 {\r
8752   HWND hInput;\r
8753   consoleEcho = TRUE;\r
8754   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8755   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8756   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8757 }\r
8758 \r
8759 \r
8760 VOID\r
8761 EchoOff()\r
8762 {\r
8763   CHARFORMAT cf;\r
8764   HWND hInput;\r
8765   consoleEcho = FALSE;\r
8766   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8767   /* This works OK: set text and background both to the same color */\r
8768   cf = consoleCF;\r
8769   cf.crTextColor = COLOR_ECHOOFF;\r
8770   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8771   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8772 }\r
8773 \r
8774 /* No Raw()...? */\r
8775 \r
8776 void Colorize(ColorClass cc, int continuation)\r
8777 {\r
8778   currentColorClass = cc;\r
8779   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8780   consoleCF.crTextColor = textAttribs[cc].color;\r
8781   consoleCF.dwEffects = textAttribs[cc].effects;\r
8782   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8783 }\r
8784 \r
8785 char *\r
8786 UserName()\r
8787 {\r
8788   static char buf[MSG_SIZ];\r
8789   DWORD bufsiz = MSG_SIZ;\r
8790 \r
8791   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8792         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8793   }\r
8794   if (!GetUserName(buf, &bufsiz)) {\r
8795     /*DisplayError("Error getting user name", GetLastError());*/\r
8796     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8797   }\r
8798   return buf;\r
8799 }\r
8800 \r
8801 char *\r
8802 HostName()\r
8803 {\r
8804   static char buf[MSG_SIZ];\r
8805   DWORD bufsiz = MSG_SIZ;\r
8806 \r
8807   if (!GetComputerName(buf, &bufsiz)) {\r
8808     /*DisplayError("Error getting host name", GetLastError());*/\r
8809     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8810   }\r
8811   return buf;\r
8812 }\r
8813 \r
8814 \r
8815 int\r
8816 ClockTimerRunning()\r
8817 {\r
8818   return clockTimerEvent != 0;\r
8819 }\r
8820 \r
8821 int\r
8822 StopClockTimer()\r
8823 {\r
8824   if (clockTimerEvent == 0) return FALSE;\r
8825   KillTimer(hwndMain, clockTimerEvent);\r
8826   clockTimerEvent = 0;\r
8827   return TRUE;\r
8828 }\r
8829 \r
8830 void\r
8831 StartClockTimer(long millisec)\r
8832 {\r
8833   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8834                              (UINT) millisec, NULL);\r
8835 }\r
8836 \r
8837 void\r
8838 DisplayWhiteClock(long timeRemaining, int highlight)\r
8839 {\r
8840   HDC hdc;\r
8841   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8842 \r
8843   if(appData.noGUI) return;\r
8844   hdc = GetDC(hwndMain);\r
8845   if (!IsIconic(hwndMain)) {\r
8846     DisplayAClock(hdc, timeRemaining, highlight, \r
8847                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8848   }\r
8849   if (highlight && iconCurrent == iconBlack) {\r
8850     iconCurrent = iconWhite;\r
8851     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8852     if (IsIconic(hwndMain)) {\r
8853       DrawIcon(hdc, 2, 2, iconCurrent);\r
8854     }\r
8855   }\r
8856   (void) ReleaseDC(hwndMain, hdc);\r
8857   if (hwndConsole)\r
8858     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8859 }\r
8860 \r
8861 void\r
8862 DisplayBlackClock(long timeRemaining, int highlight)\r
8863 {\r
8864   HDC hdc;\r
8865   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8866 \r
8867   if(appData.noGUI) return;\r
8868   hdc = GetDC(hwndMain);\r
8869   if (!IsIconic(hwndMain)) {\r
8870     DisplayAClock(hdc, timeRemaining, highlight, \r
8871                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8872   }\r
8873   if (highlight && iconCurrent == iconWhite) {\r
8874     iconCurrent = iconBlack;\r
8875     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8876     if (IsIconic(hwndMain)) {\r
8877       DrawIcon(hdc, 2, 2, iconCurrent);\r
8878     }\r
8879   }\r
8880   (void) ReleaseDC(hwndMain, hdc);\r
8881   if (hwndConsole)\r
8882     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8883 }\r
8884 \r
8885 \r
8886 int\r
8887 LoadGameTimerRunning()\r
8888 {\r
8889   return loadGameTimerEvent != 0;\r
8890 }\r
8891 \r
8892 int\r
8893 StopLoadGameTimer()\r
8894 {\r
8895   if (loadGameTimerEvent == 0) return FALSE;\r
8896   KillTimer(hwndMain, loadGameTimerEvent);\r
8897   loadGameTimerEvent = 0;\r
8898   return TRUE;\r
8899 }\r
8900 \r
8901 void\r
8902 StartLoadGameTimer(long millisec)\r
8903 {\r
8904   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8905                                 (UINT) millisec, NULL);\r
8906 }\r
8907 \r
8908 void\r
8909 AutoSaveGame()\r
8910 {\r
8911   char *defName;\r
8912   FILE *f;\r
8913   char fileTitle[MSG_SIZ];\r
8914 \r
8915   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8916   f = OpenFileDialog(hwndMain, "a", defName,\r
8917                      appData.oldSaveStyle ? "gam" : "pgn",\r
8918                      GAME_FILT, \r
8919                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8920   if (f != NULL) {\r
8921     SaveGame(f, 0, "");\r
8922     fclose(f);\r
8923   }\r
8924 }\r
8925 \r
8926 \r
8927 void\r
8928 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8929 {\r
8930   if (delayedTimerEvent != 0) {\r
8931     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8932       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8933     }\r
8934     KillTimer(hwndMain, delayedTimerEvent);\r
8935     delayedTimerEvent = 0;\r
8936     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8937     delayedTimerCallback();\r
8938   }\r
8939   delayedTimerCallback = cb;\r
8940   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8941                                 (UINT) millisec, NULL);\r
8942 }\r
8943 \r
8944 DelayedEventCallback\r
8945 GetDelayedEvent()\r
8946 {\r
8947   if (delayedTimerEvent) {\r
8948     return delayedTimerCallback;\r
8949   } else {\r
8950     return NULL;\r
8951   }\r
8952 }\r
8953 \r
8954 void\r
8955 CancelDelayedEvent()\r
8956 {\r
8957   if (delayedTimerEvent) {\r
8958     KillTimer(hwndMain, delayedTimerEvent);\r
8959     delayedTimerEvent = 0;\r
8960   }\r
8961 }\r
8962 \r
8963 DWORD GetWin32Priority(int nice)\r
8964 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8965 /*\r
8966 REALTIME_PRIORITY_CLASS     0x00000100\r
8967 HIGH_PRIORITY_CLASS         0x00000080\r
8968 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8969 NORMAL_PRIORITY_CLASS       0x00000020\r
8970 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8971 IDLE_PRIORITY_CLASS         0x00000040\r
8972 */\r
8973         if (nice < -15) return 0x00000080;\r
8974         if (nice < 0)   return 0x00008000;\r
8975         if (nice == 0)  return 0x00000020;\r
8976         if (nice < 15)  return 0x00004000;\r
8977         return 0x00000040;\r
8978 }\r
8979 \r
8980 void RunCommand(char *cmdLine)\r
8981 {\r
8982   /* Now create the child process. */\r
8983   STARTUPINFO siStartInfo;\r
8984   PROCESS_INFORMATION piProcInfo;\r
8985 \r
8986   siStartInfo.cb = sizeof(STARTUPINFO);\r
8987   siStartInfo.lpReserved = NULL;\r
8988   siStartInfo.lpDesktop = NULL;\r
8989   siStartInfo.lpTitle = NULL;\r
8990   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8991   siStartInfo.cbReserved2 = 0;\r
8992   siStartInfo.lpReserved2 = NULL;\r
8993   siStartInfo.hStdInput = NULL;\r
8994   siStartInfo.hStdOutput = NULL;\r
8995   siStartInfo.hStdError = NULL;\r
8996 \r
8997   CreateProcess(NULL,\r
8998                 cmdLine,           /* command line */\r
8999                 NULL,      /* process security attributes */\r
9000                 NULL,      /* primary thread security attrs */\r
9001                 TRUE,      /* handles are inherited */\r
9002                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9003                 NULL,      /* use parent's environment */\r
9004                 NULL,\r
9005                 &siStartInfo, /* STARTUPINFO pointer */\r
9006                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9007 \r
9008   CloseHandle(piProcInfo.hThread);\r
9009 }\r
9010 \r
9011 /* Start a child process running the given program.\r
9012    The process's standard output can be read from "from", and its\r
9013    standard input can be written to "to".\r
9014    Exit with fatal error if anything goes wrong.\r
9015    Returns an opaque pointer that can be used to destroy the process\r
9016    later.\r
9017 */\r
9018 int\r
9019 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9020 {\r
9021 #define BUFSIZE 4096\r
9022 \r
9023   HANDLE hChildStdinRd, hChildStdinWr,\r
9024     hChildStdoutRd, hChildStdoutWr;\r
9025   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9026   SECURITY_ATTRIBUTES saAttr;\r
9027   BOOL fSuccess;\r
9028   PROCESS_INFORMATION piProcInfo;\r
9029   STARTUPINFO siStartInfo;\r
9030   ChildProc *cp;\r
9031   char buf[MSG_SIZ];\r
9032   DWORD err;\r
9033 \r
9034   if (appData.debugMode) {\r
9035     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9036   }\r
9037 \r
9038   *pr = NoProc;\r
9039 \r
9040   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9041   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9042   saAttr.bInheritHandle = TRUE;\r
9043   saAttr.lpSecurityDescriptor = NULL;\r
9044 \r
9045   /*\r
9046    * The steps for redirecting child's STDOUT:\r
9047    *     1. Create anonymous pipe to be STDOUT for child.\r
9048    *     2. Create a noninheritable duplicate of read handle,\r
9049    *         and close the inheritable read handle.\r
9050    */\r
9051 \r
9052   /* Create a pipe for the child's STDOUT. */\r
9053   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9054     return GetLastError();\r
9055   }\r
9056 \r
9057   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9058   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9059                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9060                              FALSE,     /* not inherited */\r
9061                              DUPLICATE_SAME_ACCESS);\r
9062   if (! fSuccess) {\r
9063     return GetLastError();\r
9064   }\r
9065   CloseHandle(hChildStdoutRd);\r
9066 \r
9067   /*\r
9068    * The steps for redirecting child's STDIN:\r
9069    *     1. Create anonymous pipe to be STDIN for child.\r
9070    *     2. Create a noninheritable duplicate of write handle,\r
9071    *         and close the inheritable write handle.\r
9072    */\r
9073 \r
9074   /* Create a pipe for the child's STDIN. */\r
9075   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9076     return GetLastError();\r
9077   }\r
9078 \r
9079   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9080   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9081                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9082                              FALSE,     /* not inherited */\r
9083                              DUPLICATE_SAME_ACCESS);\r
9084   if (! fSuccess) {\r
9085     return GetLastError();\r
9086   }\r
9087   CloseHandle(hChildStdinWr);\r
9088 \r
9089   /* Arrange to (1) look in dir for the child .exe file, and\r
9090    * (2) have dir be the child's working directory.  Interpret\r
9091    * dir relative to the directory WinBoard loaded from. */\r
9092   GetCurrentDirectory(MSG_SIZ, buf);\r
9093   SetCurrentDirectory(installDir);\r
9094   SetCurrentDirectory(dir);\r
9095 \r
9096   /* Now create the child process. */\r
9097 \r
9098   siStartInfo.cb = sizeof(STARTUPINFO);\r
9099   siStartInfo.lpReserved = NULL;\r
9100   siStartInfo.lpDesktop = NULL;\r
9101   siStartInfo.lpTitle = NULL;\r
9102   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9103   siStartInfo.cbReserved2 = 0;\r
9104   siStartInfo.lpReserved2 = NULL;\r
9105   siStartInfo.hStdInput = hChildStdinRd;\r
9106   siStartInfo.hStdOutput = hChildStdoutWr;\r
9107   siStartInfo.hStdError = hChildStdoutWr;\r
9108 \r
9109   fSuccess = CreateProcess(NULL,\r
9110                            cmdLine,        /* command line */\r
9111                            NULL,           /* process security attributes */\r
9112                            NULL,           /* primary thread security attrs */\r
9113                            TRUE,           /* handles are inherited */\r
9114                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9115                            NULL,           /* use parent's environment */\r
9116                            NULL,\r
9117                            &siStartInfo, /* STARTUPINFO pointer */\r
9118                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9119 \r
9120   err = GetLastError();\r
9121   SetCurrentDirectory(buf); /* return to prev directory */\r
9122   if (! fSuccess) {\r
9123     return err;\r
9124   }\r
9125 \r
9126   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9127     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9128     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9129   }\r
9130 \r
9131   /* Close the handles we don't need in the parent */\r
9132   CloseHandle(piProcInfo.hThread);\r
9133   CloseHandle(hChildStdinRd);\r
9134   CloseHandle(hChildStdoutWr);\r
9135 \r
9136   /* Prepare return value */\r
9137   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9138   cp->kind = CPReal;\r
9139   cp->hProcess = piProcInfo.hProcess;\r
9140   cp->pid = piProcInfo.dwProcessId;\r
9141   cp->hFrom = hChildStdoutRdDup;\r
9142   cp->hTo = hChildStdinWrDup;\r
9143 \r
9144   *pr = (void *) cp;\r
9145 \r
9146   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9147      2000 where engines sometimes don't see the initial command(s)\r
9148      from WinBoard and hang.  I don't understand how that can happen,\r
9149      but the Sleep is harmless, so I've put it in.  Others have also\r
9150      reported what may be the same problem, so hopefully this will fix\r
9151      it for them too.  */\r
9152   Sleep(500);\r
9153 \r
9154   return NO_ERROR;\r
9155 }\r
9156 \r
9157 \r
9158 void\r
9159 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9160 {\r
9161   ChildProc *cp; int result;\r
9162 \r
9163   cp = (ChildProc *) pr;\r
9164   if (cp == NULL) return;\r
9165 \r
9166   switch (cp->kind) {\r
9167   case CPReal:\r
9168     /* TerminateProcess is considered harmful, so... */\r
9169     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9170     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9171     /* The following doesn't work because the chess program\r
9172        doesn't "have the same console" as WinBoard.  Maybe\r
9173        we could arrange for this even though neither WinBoard\r
9174        nor the chess program uses a console for stdio? */\r
9175     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9176 \r
9177     /* [AS] Special termination modes for misbehaving programs... */\r
9178     if( signal == 9 ) { \r
9179         result = TerminateProcess( cp->hProcess, 0 );\r
9180 \r
9181         if ( appData.debugMode) {\r
9182             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9183         }\r
9184     }\r
9185     else if( signal == 10 ) {\r
9186         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9187 \r
9188         if( dw != WAIT_OBJECT_0 ) {\r
9189             result = TerminateProcess( cp->hProcess, 0 );\r
9190 \r
9191             if ( appData.debugMode) {\r
9192                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9193             }\r
9194 \r
9195         }\r
9196     }\r
9197 \r
9198     CloseHandle(cp->hProcess);\r
9199     break;\r
9200 \r
9201   case CPComm:\r
9202     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9203     break;\r
9204 \r
9205   case CPSock:\r
9206     closesocket(cp->sock);\r
9207     WSACleanup();\r
9208     break;\r
9209 \r
9210   case CPRcmd:\r
9211     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9212     closesocket(cp->sock);\r
9213     closesocket(cp->sock2);\r
9214     WSACleanup();\r
9215     break;\r
9216   }\r
9217   free(cp);\r
9218 }\r
9219 \r
9220 void\r
9221 InterruptChildProcess(ProcRef pr)\r
9222 {\r
9223   ChildProc *cp;\r
9224 \r
9225   cp = (ChildProc *) pr;\r
9226   if (cp == NULL) return;\r
9227   switch (cp->kind) {\r
9228   case CPReal:\r
9229     /* The following doesn't work because the chess program\r
9230        doesn't "have the same console" as WinBoard.  Maybe\r
9231        we could arrange for this even though neither WinBoard\r
9232        nor the chess program uses a console for stdio */\r
9233     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9234     break;\r
9235 \r
9236   case CPComm:\r
9237   case CPSock:\r
9238     /* Can't interrupt */\r
9239     break;\r
9240 \r
9241   case CPRcmd:\r
9242     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9243     break;\r
9244   }\r
9245 }\r
9246 \r
9247 \r
9248 int\r
9249 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9250 {\r
9251   char cmdLine[MSG_SIZ];\r
9252 \r
9253   if (port[0] == NULLCHAR) {\r
9254     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9255   } else {\r
9256     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9257   }\r
9258   return StartChildProcess(cmdLine, "", pr);\r
9259 }\r
9260 \r
9261 \r
9262 /* Code to open TCP sockets */\r
9263 \r
9264 int\r
9265 OpenTCP(char *host, char *port, ProcRef *pr)\r
9266 {\r
9267   ChildProc *cp;\r
9268   int err;\r
9269   SOCKET s;\r
9270 \r
9271   struct sockaddr_in sa, mysa;\r
9272   struct hostent FAR *hp;\r
9273   unsigned short uport;\r
9274   WORD wVersionRequested;\r
9275   WSADATA wsaData;\r
9276 \r
9277   /* Initialize socket DLL */\r
9278   wVersionRequested = MAKEWORD(1, 1);\r
9279   err = WSAStartup(wVersionRequested, &wsaData);\r
9280   if (err != 0) return err;\r
9281 \r
9282   /* Make socket */\r
9283   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9284     err = WSAGetLastError();\r
9285     WSACleanup();\r
9286     return err;\r
9287   }\r
9288 \r
9289   /* Bind local address using (mostly) don't-care values.\r
9290    */\r
9291   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9292   mysa.sin_family = AF_INET;\r
9293   mysa.sin_addr.s_addr = INADDR_ANY;\r
9294   uport = (unsigned short) 0;\r
9295   mysa.sin_port = htons(uport);\r
9296   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9297       == SOCKET_ERROR) {\r
9298     err = WSAGetLastError();\r
9299     WSACleanup();\r
9300     return err;\r
9301   }\r
9302 \r
9303   /* Resolve remote host name */\r
9304   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9305   if (!(hp = gethostbyname(host))) {\r
9306     unsigned int b0, b1, b2, b3;\r
9307 \r
9308     err = WSAGetLastError();\r
9309 \r
9310     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9311       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9312       hp->h_addrtype = AF_INET;\r
9313       hp->h_length = 4;\r
9314       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9315       hp->h_addr_list[0] = (char *) malloc(4);\r
9316       hp->h_addr_list[0][0] = (char) b0;\r
9317       hp->h_addr_list[0][1] = (char) b1;\r
9318       hp->h_addr_list[0][2] = (char) b2;\r
9319       hp->h_addr_list[0][3] = (char) b3;\r
9320     } else {\r
9321       WSACleanup();\r
9322       return err;\r
9323     }\r
9324   }\r
9325   sa.sin_family = hp->h_addrtype;\r
9326   uport = (unsigned short) atoi(port);\r
9327   sa.sin_port = htons(uport);\r
9328   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9329 \r
9330   /* Make connection */\r
9331   if (connect(s, (struct sockaddr *) &sa,\r
9332               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9333     err = WSAGetLastError();\r
9334     WSACleanup();\r
9335     return err;\r
9336   }\r
9337 \r
9338   /* Prepare return value */\r
9339   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9340   cp->kind = CPSock;\r
9341   cp->sock = s;\r
9342   *pr = (ProcRef *) cp;\r
9343 \r
9344   return NO_ERROR;\r
9345 }\r
9346 \r
9347 int\r
9348 OpenCommPort(char *name, ProcRef *pr)\r
9349 {\r
9350   HANDLE h;\r
9351   COMMTIMEOUTS ct;\r
9352   ChildProc *cp;\r
9353   char fullname[MSG_SIZ];\r
9354 \r
9355   if (*name != '\\')\r
9356     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9357   else\r
9358     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9359 \r
9360   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9361                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9362   if (h == (HANDLE) -1) {\r
9363     return GetLastError();\r
9364   }\r
9365   hCommPort = h;\r
9366 \r
9367   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9368 \r
9369   /* Accumulate characters until a 100ms pause, then parse */\r
9370   ct.ReadIntervalTimeout = 100;\r
9371   ct.ReadTotalTimeoutMultiplier = 0;\r
9372   ct.ReadTotalTimeoutConstant = 0;\r
9373   ct.WriteTotalTimeoutMultiplier = 0;\r
9374   ct.WriteTotalTimeoutConstant = 0;\r
9375   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9376 \r
9377   /* Prepare return value */\r
9378   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9379   cp->kind = CPComm;\r
9380   cp->hFrom = h;\r
9381   cp->hTo = h;\r
9382   *pr = (ProcRef *) cp;\r
9383 \r
9384   return NO_ERROR;\r
9385 }\r
9386 \r
9387 int\r
9388 OpenLoopback(ProcRef *pr)\r
9389 {\r
9390   DisplayFatalError(_("Not implemented"), 0, 1);\r
9391   return NO_ERROR;\r
9392 }\r
9393 \r
9394 \r
9395 int\r
9396 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9397 {\r
9398   ChildProc *cp;\r
9399   int err;\r
9400   SOCKET s, s2, s3;\r
9401   struct sockaddr_in sa, mysa;\r
9402   struct hostent FAR *hp;\r
9403   unsigned short uport;\r
9404   WORD wVersionRequested;\r
9405   WSADATA wsaData;\r
9406   int fromPort;\r
9407   char stderrPortStr[MSG_SIZ];\r
9408 \r
9409   /* Initialize socket DLL */\r
9410   wVersionRequested = MAKEWORD(1, 1);\r
9411   err = WSAStartup(wVersionRequested, &wsaData);\r
9412   if (err != 0) return err;\r
9413 \r
9414   /* Resolve remote host name */\r
9415   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9416   if (!(hp = gethostbyname(host))) {\r
9417     unsigned int b0, b1, b2, b3;\r
9418 \r
9419     err = WSAGetLastError();\r
9420 \r
9421     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9422       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9423       hp->h_addrtype = AF_INET;\r
9424       hp->h_length = 4;\r
9425       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9426       hp->h_addr_list[0] = (char *) malloc(4);\r
9427       hp->h_addr_list[0][0] = (char) b0;\r
9428       hp->h_addr_list[0][1] = (char) b1;\r
9429       hp->h_addr_list[0][2] = (char) b2;\r
9430       hp->h_addr_list[0][3] = (char) b3;\r
9431     } else {\r
9432       WSACleanup();\r
9433       return err;\r
9434     }\r
9435   }\r
9436   sa.sin_family = hp->h_addrtype;\r
9437   uport = (unsigned short) 514;\r
9438   sa.sin_port = htons(uport);\r
9439   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9440 \r
9441   /* Bind local socket to unused "privileged" port address\r
9442    */\r
9443   s = INVALID_SOCKET;\r
9444   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9445   mysa.sin_family = AF_INET;\r
9446   mysa.sin_addr.s_addr = INADDR_ANY;\r
9447   for (fromPort = 1023;; fromPort--) {\r
9448     if (fromPort < 0) {\r
9449       WSACleanup();\r
9450       return WSAEADDRINUSE;\r
9451     }\r
9452     if (s == INVALID_SOCKET) {\r
9453       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9454         err = WSAGetLastError();\r
9455         WSACleanup();\r
9456         return err;\r
9457       }\r
9458     }\r
9459     uport = (unsigned short) fromPort;\r
9460     mysa.sin_port = htons(uport);\r
9461     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9462         == SOCKET_ERROR) {\r
9463       err = WSAGetLastError();\r
9464       if (err == WSAEADDRINUSE) continue;\r
9465       WSACleanup();\r
9466       return err;\r
9467     }\r
9468     if (connect(s, (struct sockaddr *) &sa,\r
9469       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9470       err = WSAGetLastError();\r
9471       if (err == WSAEADDRINUSE) {\r
9472         closesocket(s);\r
9473         s = -1;\r
9474         continue;\r
9475       }\r
9476       WSACleanup();\r
9477       return err;\r
9478     }\r
9479     break;\r
9480   }\r
9481 \r
9482   /* Bind stderr local socket to unused "privileged" port address\r
9483    */\r
9484   s2 = INVALID_SOCKET;\r
9485   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9486   mysa.sin_family = AF_INET;\r
9487   mysa.sin_addr.s_addr = INADDR_ANY;\r
9488   for (fromPort = 1023;; fromPort--) {\r
9489     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9490     if (fromPort < 0) {\r
9491       (void) closesocket(s);\r
9492       WSACleanup();\r
9493       return WSAEADDRINUSE;\r
9494     }\r
9495     if (s2 == INVALID_SOCKET) {\r
9496       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9497         err = WSAGetLastError();\r
9498         closesocket(s);\r
9499         WSACleanup();\r
9500         return err;\r
9501       }\r
9502     }\r
9503     uport = (unsigned short) fromPort;\r
9504     mysa.sin_port = htons(uport);\r
9505     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9506         == SOCKET_ERROR) {\r
9507       err = WSAGetLastError();\r
9508       if (err == WSAEADDRINUSE) continue;\r
9509       (void) closesocket(s);\r
9510       WSACleanup();\r
9511       return err;\r
9512     }\r
9513     if (listen(s2, 1) == SOCKET_ERROR) {\r
9514       err = WSAGetLastError();\r
9515       if (err == WSAEADDRINUSE) {\r
9516         closesocket(s2);\r
9517         s2 = INVALID_SOCKET;\r
9518         continue;\r
9519       }\r
9520       (void) closesocket(s);\r
9521       (void) closesocket(s2);\r
9522       WSACleanup();\r
9523       return err;\r
9524     }\r
9525     break;\r
9526   }\r
9527   prevStderrPort = fromPort; // remember port used\r
9528   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9529 \r
9530   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9531     err = WSAGetLastError();\r
9532     (void) closesocket(s);\r
9533     (void) closesocket(s2);\r
9534     WSACleanup();\r
9535     return err;\r
9536   }\r
9537 \r
9538   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9539     err = WSAGetLastError();\r
9540     (void) closesocket(s);\r
9541     (void) closesocket(s2);\r
9542     WSACleanup();\r
9543     return err;\r
9544   }\r
9545   if (*user == NULLCHAR) user = UserName();\r
9546   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9547     err = WSAGetLastError();\r
9548     (void) closesocket(s);\r
9549     (void) closesocket(s2);\r
9550     WSACleanup();\r
9551     return err;\r
9552   }\r
9553   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9554     err = WSAGetLastError();\r
9555     (void) closesocket(s);\r
9556     (void) closesocket(s2);\r
9557     WSACleanup();\r
9558     return err;\r
9559   }\r
9560 \r
9561   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9562     err = WSAGetLastError();\r
9563     (void) closesocket(s);\r
9564     (void) closesocket(s2);\r
9565     WSACleanup();\r
9566     return err;\r
9567   }\r
9568   (void) closesocket(s2);  /* Stop listening */\r
9569 \r
9570   /* Prepare return value */\r
9571   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9572   cp->kind = CPRcmd;\r
9573   cp->sock = s;\r
9574   cp->sock2 = s3;\r
9575   *pr = (ProcRef *) cp;\r
9576 \r
9577   return NO_ERROR;\r
9578 }\r
9579 \r
9580 \r
9581 InputSourceRef\r
9582 AddInputSource(ProcRef pr, int lineByLine,\r
9583                InputCallback func, VOIDSTAR closure)\r
9584 {\r
9585   InputSource *is, *is2 = NULL;\r
9586   ChildProc *cp = (ChildProc *) pr;\r
9587 \r
9588   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9589   is->lineByLine = lineByLine;\r
9590   is->func = func;\r
9591   is->closure = closure;\r
9592   is->second = NULL;\r
9593   is->next = is->buf;\r
9594   if (pr == NoProc) {\r
9595     is->kind = CPReal;\r
9596     consoleInputSource = is;\r
9597   } else {\r
9598     is->kind = cp->kind;\r
9599     /* \r
9600         [AS] Try to avoid a race condition if the thread is given control too early:\r
9601         we create all threads suspended so that the is->hThread variable can be\r
9602         safely assigned, then let the threads start with ResumeThread.\r
9603     */\r
9604     switch (cp->kind) {\r
9605     case CPReal:\r
9606       is->hFile = cp->hFrom;\r
9607       cp->hFrom = NULL; /* now owned by InputThread */\r
9608       is->hThread =\r
9609         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9610                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9611       break;\r
9612 \r
9613     case CPComm:\r
9614       is->hFile = cp->hFrom;\r
9615       cp->hFrom = NULL; /* now owned by InputThread */\r
9616       is->hThread =\r
9617         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9618                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9619       break;\r
9620 \r
9621     case CPSock:\r
9622       is->sock = cp->sock;\r
9623       is->hThread =\r
9624         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9625                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9626       break;\r
9627 \r
9628     case CPRcmd:\r
9629       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9630       *is2 = *is;\r
9631       is->sock = cp->sock;\r
9632       is->second = is2;\r
9633       is2->sock = cp->sock2;\r
9634       is2->second = is2;\r
9635       is->hThread =\r
9636         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9637                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9638       is2->hThread =\r
9639         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9640                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9641       break;\r
9642     }\r
9643 \r
9644     if( is->hThread != NULL ) {\r
9645         ResumeThread( is->hThread );\r
9646     }\r
9647 \r
9648     if( is2 != NULL && is2->hThread != NULL ) {\r
9649         ResumeThread( is2->hThread );\r
9650     }\r
9651   }\r
9652 \r
9653   return (InputSourceRef) is;\r
9654 }\r
9655 \r
9656 void\r
9657 RemoveInputSource(InputSourceRef isr)\r
9658 {\r
9659   InputSource *is;\r
9660 \r
9661   is = (InputSource *) isr;\r
9662   is->hThread = NULL;  /* tell thread to stop */\r
9663   CloseHandle(is->hThread);\r
9664   if (is->second != NULL) {\r
9665     is->second->hThread = NULL;\r
9666     CloseHandle(is->second->hThread);\r
9667   }\r
9668 }\r
9669 \r
9670 int no_wrap(char *message, int count)\r
9671 {\r
9672     ConsoleOutput(message, count, FALSE);\r
9673     return count;\r
9674 }\r
9675 \r
9676 int\r
9677 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9678 {\r
9679   DWORD dOutCount;\r
9680   int outCount = SOCKET_ERROR;\r
9681   ChildProc *cp = (ChildProc *) pr;\r
9682   static OVERLAPPED ovl;\r
9683   static int line = 0;\r
9684 \r
9685   if (pr == NoProc)\r
9686   {\r
9687     if (appData.noJoin || !appData.useInternalWrap)\r
9688       return no_wrap(message, count);\r
9689     else\r
9690     {\r
9691       int width = get_term_width();\r
9692       int len = wrap(NULL, message, count, width, &line);\r
9693       char *msg = malloc(len);\r
9694       int dbgchk;\r
9695 \r
9696       if (!msg)\r
9697         return no_wrap(message, count);\r
9698       else\r
9699       {\r
9700         dbgchk = wrap(msg, message, count, width, &line);\r
9701         if (dbgchk != len && appData.debugMode)\r
9702             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9703         ConsoleOutput(msg, len, FALSE);\r
9704         free(msg);\r
9705         return len;\r
9706       }\r
9707     }\r
9708   }\r
9709 \r
9710   if (ovl.hEvent == NULL) {\r
9711     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9712   }\r
9713   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9714 \r
9715   switch (cp->kind) {\r
9716   case CPSock:\r
9717   case CPRcmd:\r
9718     outCount = send(cp->sock, message, count, 0);\r
9719     if (outCount == SOCKET_ERROR) {\r
9720       *outError = WSAGetLastError();\r
9721     } else {\r
9722       *outError = NO_ERROR;\r
9723     }\r
9724     break;\r
9725 \r
9726   case CPReal:\r
9727     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9728                   &dOutCount, NULL)) {\r
9729       *outError = NO_ERROR;\r
9730       outCount = (int) dOutCount;\r
9731     } else {\r
9732       *outError = GetLastError();\r
9733     }\r
9734     break;\r
9735 \r
9736   case CPComm:\r
9737     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9738                             &dOutCount, &ovl);\r
9739     if (*outError == NO_ERROR) {\r
9740       outCount = (int) dOutCount;\r
9741     }\r
9742     break;\r
9743   }\r
9744   return outCount;\r
9745 }\r
9746 \r
9747 void\r
9748 DoSleep(int n)\r
9749 {\r
9750     if(n != 0) Sleep(n);\r
9751 }\r
9752 \r
9753 int\r
9754 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9755                        long msdelay)\r
9756 {\r
9757   /* Ignore delay, not implemented for WinBoard */\r
9758   return OutputToProcess(pr, message, count, outError);\r
9759 }\r
9760 \r
9761 \r
9762 void\r
9763 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9764                         char *buf, int count, int error)\r
9765 {\r
9766   DisplayFatalError(_("Not implemented"), 0, 1);\r
9767 }\r
9768 \r
9769 /* see wgamelist.c for Game List functions */\r
9770 /* see wedittags.c for Edit Tags functions */\r
9771 \r
9772 \r
9773 int\r
9774 ICSInitScript()\r
9775 {\r
9776   FILE *f;\r
9777   char buf[MSG_SIZ];\r
9778   char *dummy;\r
9779 \r
9780   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9781     f = fopen(buf, "r");\r
9782     if (f != NULL) {\r
9783       ProcessICSInitScript(f);\r
9784       fclose(f);\r
9785       return TRUE;\r
9786     }\r
9787   }\r
9788   return FALSE;\r
9789 }\r
9790 \r
9791 \r
9792 VOID\r
9793 StartAnalysisClock()\r
9794 {\r
9795   if (analysisTimerEvent) return;\r
9796   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9797                                         (UINT) 2000, NULL);\r
9798 }\r
9799 \r
9800 VOID\r
9801 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9802 {\r
9803   highlightInfo.sq[0].x = fromX;\r
9804   highlightInfo.sq[0].y = fromY;\r
9805   highlightInfo.sq[1].x = toX;\r
9806   highlightInfo.sq[1].y = toY;\r
9807 }\r
9808 \r
9809 VOID\r
9810 ClearHighlights()\r
9811 {\r
9812   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9813     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9814 }\r
9815 \r
9816 VOID\r
9817 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9818 {\r
9819   premoveHighlightInfo.sq[0].x = fromX;\r
9820   premoveHighlightInfo.sq[0].y = fromY;\r
9821   premoveHighlightInfo.sq[1].x = toX;\r
9822   premoveHighlightInfo.sq[1].y = toY;\r
9823 }\r
9824 \r
9825 VOID\r
9826 ClearPremoveHighlights()\r
9827 {\r
9828   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9829     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9830 }\r
9831 \r
9832 VOID\r
9833 ShutDownFrontEnd()\r
9834 {\r
9835   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9836   DeleteClipboardTempFiles();\r
9837 }\r
9838 \r
9839 void\r
9840 BoardToTop()\r
9841 {\r
9842     if (IsIconic(hwndMain))\r
9843       ShowWindow(hwndMain, SW_RESTORE);\r
9844 \r
9845     SetActiveWindow(hwndMain);\r
9846 }\r
9847 \r
9848 /*\r
9849  * Prototypes for animation support routines\r
9850  */\r
9851 static void ScreenSquare(int column, int row, POINT * pt);\r
9852 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9853      POINT frames[], int * nFrames);\r
9854 \r
9855 \r
9856 #define kFactor 4\r
9857 \r
9858 void\r
9859 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9860 {       // [HGM] atomic: animate blast wave\r
9861         int i;\r
9862 \r
9863         explodeInfo.fromX = fromX;\r
9864         explodeInfo.fromY = fromY;\r
9865         explodeInfo.toX = toX;\r
9866         explodeInfo.toY = toY;\r
9867         for(i=1; i<4*kFactor; i++) {\r
9868             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9869             DrawPosition(FALSE, board);\r
9870             Sleep(appData.animSpeed);\r
9871         }\r
9872         explodeInfo.radius = 0;\r
9873         DrawPosition(TRUE, board);\r
9874 }\r
9875 \r
9876 void\r
9877 AnimateMove(board, fromX, fromY, toX, toY)\r
9878      Board board;\r
9879      int fromX;\r
9880      int fromY;\r
9881      int toX;\r
9882      int toY;\r
9883 {\r
9884   ChessSquare piece;\r
9885   POINT start, finish, mid;\r
9886   POINT frames[kFactor * 2 + 1];\r
9887   int nFrames, n;\r
9888 \r
9889   if (!appData.animate) return;\r
9890   if (doingSizing) return;\r
9891   if (fromY < 0 || fromX < 0) return;\r
9892   piece = board[fromY][fromX];\r
9893   if (piece >= EmptySquare) return;\r
9894 \r
9895   ScreenSquare(fromX, fromY, &start);\r
9896   ScreenSquare(toX, toY, &finish);\r
9897 \r
9898   /* All moves except knight jumps move in straight line */\r
9899   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9900     mid.x = start.x + (finish.x - start.x) / 2;\r
9901     mid.y = start.y + (finish.y - start.y) / 2;\r
9902   } else {\r
9903     /* Knight: make straight movement then diagonal */\r
9904     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9905        mid.x = start.x + (finish.x - start.x) / 2;\r
9906        mid.y = start.y;\r
9907      } else {\r
9908        mid.x = start.x;\r
9909        mid.y = start.y + (finish.y - start.y) / 2;\r
9910      }\r
9911   }\r
9912   \r
9913   /* Don't use as many frames for very short moves */\r
9914   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9915     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9916   else\r
9917     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9918 \r
9919   animInfo.from.x = fromX;\r
9920   animInfo.from.y = fromY;\r
9921   animInfo.to.x = toX;\r
9922   animInfo.to.y = toY;\r
9923   animInfo.lastpos = start;\r
9924   animInfo.piece = piece;\r
9925   for (n = 0; n < nFrames; n++) {\r
9926     animInfo.pos = frames[n];\r
9927     DrawPosition(FALSE, NULL);\r
9928     animInfo.lastpos = animInfo.pos;\r
9929     Sleep(appData.animSpeed);\r
9930   }\r
9931   animInfo.pos = finish;\r
9932   DrawPosition(FALSE, NULL);\r
9933   animInfo.piece = EmptySquare;\r
9934   Explode(board, fromX, fromY, toX, toY);\r
9935 }\r
9936 \r
9937 /*      Convert board position to corner of screen rect and color       */\r
9938 \r
9939 static void\r
9940 ScreenSquare(column, row, pt)\r
9941      int column; int row; POINT * pt;\r
9942 {\r
9943   if (flipView) {\r
9944     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9945     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9946   } else {\r
9947     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9948     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9949   }\r
9950 }\r
9951 \r
9952 /*      Generate a series of frame coords from start->mid->finish.\r
9953         The movement rate doubles until the half way point is\r
9954         reached, then halves back down to the final destination,\r
9955         which gives a nice slow in/out effect. The algorithmn\r
9956         may seem to generate too many intermediates for short\r
9957         moves, but remember that the purpose is to attract the\r
9958         viewers attention to the piece about to be moved and\r
9959         then to where it ends up. Too few frames would be less\r
9960         noticeable.                                             */\r
9961 \r
9962 static void\r
9963 Tween(start, mid, finish, factor, frames, nFrames)\r
9964      POINT * start; POINT * mid;\r
9965      POINT * finish; int factor;\r
9966      POINT frames[]; int * nFrames;\r
9967 {\r
9968   int n, fraction = 1, count = 0;\r
9969 \r
9970   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9971   for (n = 0; n < factor; n++)\r
9972     fraction *= 2;\r
9973   for (n = 0; n < factor; n++) {\r
9974     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9975     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9976     count ++;\r
9977     fraction = fraction / 2;\r
9978   }\r
9979   \r
9980   /* Midpoint */\r
9981   frames[count] = *mid;\r
9982   count ++;\r
9983   \r
9984   /* Slow out, stepping 1/2, then 1/4, ... */\r
9985   fraction = 2;\r
9986   for (n = 0; n < factor; n++) {\r
9987     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9988     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9989     count ++;\r
9990     fraction = fraction * 2;\r
9991   }\r
9992   *nFrames = count;\r
9993 }\r
9994 \r
9995 void\r
9996 SettingsPopUp(ChessProgramState *cps)\r
9997 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9998       EngineOptionsPopup(savedHwnd, cps);\r
9999 }\r
10000 \r
10001 int flock(int fid, int code)\r
10002 {\r
10003     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10004     OVERLAPPED ov;\r
10005     ov.hEvent = NULL;\r
10006     ov.Offset = 0;\r
10007     ov.OffsetHigh = 0;\r
10008     switch(code) {\r
10009       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10010       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10011       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10012       default: return -1;\r
10013     }\r
10014     return 0;\r
10015 }\r
10016 \r
10017 char *\r
10018 Col2Text (int n)\r
10019 {\r
10020     static int i=0;\r
10021     static char col[8][20];\r
10022     COLORREF color = *(COLORREF *) colorVariable[n];\r
10023     i = i+1 & 7;\r
10024     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10025     return col[i];\r
10026 }\r
10027 \r
10028 void\r
10029 ActivateTheme (int new)\r
10030 {   // Redo initialization of features depending on options that can occur in themes\r
10031    InitTextures();\r
10032    if(new) InitDrawingColors();\r
10033    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10034    InitDrawingSizes(-2, 0);\r
10035    InvalidateRect(hwndMain, NULL, TRUE);\r
10036 }\r