d643a457b674af7b8e67ffd7c9e5115486c646d0
[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 #define SLASH '/'\r
96 \r
97 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
98 \r
99   int myrandom(void);\r
100   void mysrandom(unsigned int seed);\r
101 \r
102 extern int whiteFlag, blackFlag;\r
103 Boolean flipClock = FALSE;\r
104 extern HANDLE chatHandle[];\r
105 extern enum ICS_TYPE ics_type;\r
106 \r
107 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
108 int  MyGetFullPathName P((char *name, char *fullname));\r
109 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
110 VOID NewVariantPopup(HWND hwnd);\r
111 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
112                    /*char*/int promoChar));\r
113 void DisplayMove P((int moveNumber));\r
114 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
115 void ChatPopUp P((char *s));\r
116 typedef struct {\r
117   ChessSquare piece;  \r
118   POINT pos;      /* window coordinates of current pos */\r
119   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
120   POINT from;     /* board coordinates of the piece's orig pos */\r
121   POINT to;       /* board coordinates of the piece's new pos */\r
122 } AnimInfo;\r
123 \r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
125 \r
126 typedef struct {\r
127   POINT start;    /* window coordinates of start pos */\r
128   POINT pos;      /* window coordinates of current pos */\r
129   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
130   POINT from;     /* board coordinates of the piece's orig pos */\r
131   ChessSquare piece;\r
132 } DragInfo;\r
133 \r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
135 \r
136 typedef struct {\r
137   POINT sq[2];    /* board coordinates of from, to squares */\r
138 } HighlightInfo;\r
139 \r
140 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
144 \r
145 typedef struct { // [HGM] atomic\r
146   int fromX, fromY, toX, toY, radius;\r
147 } ExplodeInfo;\r
148 \r
149 static ExplodeInfo explodeInfo;\r
150 \r
151 /* Window class names */\r
152 char szAppName[] = "WinBoard";\r
153 char szConsoleName[] = "WBConsole";\r
154 \r
155 /* Title bar text */\r
156 char szTitle[] = "WinBoard";\r
157 char szConsoleTitle[] = "I C S Interaction";\r
158 \r
159 char *programName;\r
160 char *settingsFileName;\r
161 Boolean saveSettingsOnExit;\r
162 char installDir[MSG_SIZ];\r
163 int errorExitStatus;\r
164 \r
165 BoardSize boardSize;\r
166 Boolean chessProgram;\r
167 //static int boardX, boardY;\r
168 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
169 int squareSize, lineGap, minorSize, border;\r
170 static int winW, winH;\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
172 static int logoHeight = 0;\r
173 static char messageText[MESSAGE_TEXT_MAX];\r
174 static int clockTimerEvent = 0;\r
175 static int loadGameTimerEvent = 0;\r
176 static int analysisTimerEvent = 0;\r
177 static DelayedEventCallback delayedTimerCallback;\r
178 static int delayedTimerEvent = 0;\r
179 static int buttonCount = 2;\r
180 char *icsTextMenuString;\r
181 char *icsNames;\r
182 char *firstChessProgramNames;\r
183 char *secondChessProgramNames;\r
184 \r
185 #define PALETTESIZE 256\r
186 \r
187 HINSTANCE hInst;          /* current instance */\r
188 Boolean alwaysOnTop = FALSE;\r
189 RECT boardRect;\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
191   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
192 HPALETTE hPal;\r
193 ColorClass currentColorClass;\r
194 \r
195 static HWND savedHwnd;\r
196 HWND hCommPort = NULL;    /* currently open comm port */\r
197 static HWND hwndPause;    /* pause button */\r
198 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
199 static HBRUSH lightSquareBrush, darkSquareBrush,\r
200   blackSquareBrush, /* [HGM] for band between board and holdings */\r
201   explodeBrush,     /* [HGM] atomic */\r
202   markerBrush,      /* [HGM] markers */\r
203   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
204 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
205 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
206 static HPEN gridPen = NULL;\r
207 static HPEN highlightPen = NULL;\r
208 static HPEN premovePen = NULL;\r
209 static NPLOGPALETTE pLogPal;\r
210 static BOOL paletteChanged = FALSE;\r
211 static HICON iconWhite, iconBlack, iconCurrent;\r
212 static int doingSizing = FALSE;\r
213 static int lastSizing = 0;\r
214 static int prevStderrPort;\r
215 static HBITMAP userLogo;\r
216 \r
217 static HBITMAP liteBackTexture = NULL;\r
218 static HBITMAP darkBackTexture = NULL;\r
219 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
220 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int backTextureSquareSize = 0;\r
222 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
223 \r
224 #if __GNUC__ && !defined(_winmajor)\r
225 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
226 #else\r
227 \r
228 \r
229 \r
230 #if defined(_winmajor)\r
231 #define oldDialog (_winmajor < 4)\r
232 #else\r
233 #define oldDialog 0\r
234 #endif\r
235 #endif\r
236 \r
237 #define INTERNATIONAL\r
238 \r
239 #ifdef INTERNATIONAL\r
240 #  define _(s) T_(s)\r
241 #  define N_(s) s\r
242 #else\r
243 #  define _(s) s\r
244 #  define N_(s) s\r
245 #  define T_(s) s\r
246 #  define Translate(x, y)\r
247 #  define LoadLanguageFile(s)\r
248 #endif\r
249 \r
250 #ifdef INTERNATIONAL\r
251 \r
252 Boolean barbaric; // flag indicating if translation is needed\r
253 \r
254 // list of item numbers used in each dialog (used to alter language at run time)\r
255 \r
256 #define ABOUTBOX -1  /* not sure why these are needed */\r
257 #define ABOUTBOX2 -1\r
258 \r
259 int dialogItems[][42] = {\r
260 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
261 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
262   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
263 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
264   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
266   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
269   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
272   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
273 { ABOUTBOX2, IDC_ChessBoard }, \r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
275   OPT_GameListClose, IDC_GameListDoFilter }, \r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
277 { DLG_Error, IDOK }, \r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
279   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
282   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
283   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
284 { DLG_IndexNumber, IDC_Index }, \r
285 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
286 { DLG_TypeInName, IDOK, IDCANCEL }, \r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
288   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
290   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
291   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
292   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
293   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
294   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
295   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
297   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
298   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
299   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
300   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
301   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
302   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
303   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
304   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
306   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
307   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
308   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
309   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
310   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
311   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
312   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
314   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
315   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
316   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
317   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
318   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
319   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
320   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
321   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
323   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
324   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
325   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
326   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
329   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
330 { DLG_MoveHistory }, \r
331 { DLG_EvalGraph }, \r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
335   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
336   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
337   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
339   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
340   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
341 { 0 }\r
342 };\r
343 \r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
345 static int lastChecked;\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
347 extern int tinyLayout;\r
348 extern char * menuBarText[][10];\r
349 \r
350 void\r
351 LoadLanguageFile(char *name)\r
352 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
353     FILE *f;\r
354     int i=0, j=0, n=0, k;\r
355     char buf[MSG_SIZ];\r
356 \r
357     if(!name || name[0] == NULLCHAR) return;\r
358       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
359     appData.language = oldLanguage;\r
360     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
361     if((f = fopen(buf, "r")) == NULL) return;\r
362     while((k = fgetc(f)) != EOF) {\r
363         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
364         languageBuf[i] = k;\r
365         if(k == '\n') {\r
366             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
367                 char *p;\r
368                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
369                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
370                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
371                         english[j] = languageBuf + n + 1; *p = 0;\r
372                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
374                     }\r
375                 }\r
376             }\r
377             n = i + 1;\r
378         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
379             switch(k) {\r
380               case 'n': k = '\n'; break;\r
381               case 'r': k = '\r'; break;\r
382               case 't': k = '\t'; break;\r
383             }\r
384             languageBuf[--i] = k;\r
385         }\r
386         i++;\r
387     }\r
388     fclose(f);\r
389     barbaric = (j != 0);\r
390     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
391 }\r
392 \r
393 char *\r
394 T_(char *s)\r
395 {   // return the translation of the given string\r
396     // efficiency can be improved a lot...\r
397     int i=0;\r
398     static char buf[MSG_SIZ];\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
400     if(!barbaric) return s;\r
401     if(!s) return ""; // sanity\r
402     while(english[i]) {\r
403         if(!strcmp(s, english[i])) return foreign[i];\r
404         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
405             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
406             return buf;\r
407         }\r
408         i++;\r
409     }\r
410     return s;\r
411 }\r
412 \r
413 void\r
414 Translate(HWND hDlg, int dialogID)\r
415 {   // translate all text items in the given dialog\r
416     int i=0, j, k;\r
417     char buf[MSG_SIZ], *s;\r
418     if(!barbaric) return;\r
419     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
420     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
421     GetWindowText( hDlg, buf, MSG_SIZ );\r
422     s = T_(buf);\r
423     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
424     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
425         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
426         if(strlen(buf) == 0) continue;\r
427         s = T_(buf);\r
428         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
429     }\r
430 }\r
431 \r
432 HMENU\r
433 TranslateOneMenu(int i, HMENU subMenu)\r
434 {\r
435     int j;\r
436     static MENUITEMINFO info;\r
437 \r
438     info.cbSize = sizeof(MENUITEMINFO);\r
439     info.fMask = MIIM_STATE | MIIM_TYPE;\r
440           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
441             char buf[MSG_SIZ];\r
442             info.dwTypeData = buf;\r
443             info.cch = sizeof(buf);\r
444             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
445             if(i < 10) {\r
446                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
447                 else menuText[i][j] = strdup(buf); // remember original on first change\r
448             }\r
449             if(buf[0] == NULLCHAR) continue;\r
450             info.dwTypeData = T_(buf);\r
451             info.cch = strlen(buf)+1;\r
452             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
453           }\r
454     return subMenu;\r
455 }\r
456 \r
457 void\r
458 TranslateMenus(int addLanguage)\r
459 {\r
460     int i;\r
461     WIN32_FIND_DATA fileData;\r
462     HANDLE hFind;\r
463 #define IDM_English 1970\r
464     if(1) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
467           HMENU subMenu = GetSubMenu(mainMenu, i);\r
468           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
469                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
470           TranslateOneMenu(i, subMenu);\r
471         }\r
472         DrawMenuBar(hwndMain);\r
473     }\r
474 \r
475     if(!addLanguage) return;\r
476     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
477         HMENU mainMenu = GetMenu(hwndMain);\r
478         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
479         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
480         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
481         i = 0; lastChecked = IDM_English;\r
482         do {\r
483             char *p, *q = fileData.cFileName;\r
484             int checkFlag = MF_UNCHECKED;\r
485             languageFile[i] = strdup(q);\r
486             if(barbaric && !strcmp(oldLanguage, q)) {\r
487                 checkFlag = MF_CHECKED;\r
488                 lastChecked = IDM_English + i + 1;\r
489                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
490             }\r
491             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
492             p = strstr(fileData.cFileName, ".lng");\r
493             if(p) *p = 0;\r
494             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
495         } while(FindNextFile(hFind, &fileData));\r
496         FindClose(hFind);\r
497     }\r
498 }\r
499 \r
500 #endif\r
501 \r
502 #define IDM_RecentEngines 3000\r
503 \r
504 void\r
505 RecentEngineMenu (char *s)\r
506 {\r
507     if(appData.icsActive) return;\r
508     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
509         HMENU mainMenu = GetMenu(hwndMain);\r
510         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
511         int i=IDM_RecentEngines;\r
512         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
513         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
514         while(*s) {\r
515           char *p = strchr(s, '\n');\r
516           if(p == NULL) return; // malformed!\r
517           *p = NULLCHAR;\r
518           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
519           *p = '\n';\r
520           s = p+1;\r
521         }\r
522     }\r
523 }\r
524 \r
525 \r
526 typedef struct {\r
527   char *name;\r
528   int squareSize;\r
529   int lineGap;\r
530   int smallLayout;\r
531   int tinyLayout;\r
532   int cliWidth, cliHeight;\r
533 } SizeInfo;\r
534 \r
535 SizeInfo sizeInfo[] = \r
536 {\r
537   { "tiny",     21, 0, 1, 1, 0, 0 },\r
538   { "teeny",    25, 1, 1, 1, 0, 0 },\r
539   { "dinky",    29, 1, 1, 1, 0, 0 },\r
540   { "petite",   33, 1, 1, 1, 0, 0 },\r
541   { "slim",     37, 2, 1, 0, 0, 0 },\r
542   { "small",    40, 2, 1, 0, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576   { 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
577   { 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
578   { 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
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
607 };\r
608 \r
609 \r
610 MySound sounds[(int)NSoundClasses];\r
611 MyTextAttribs textAttribs[(int)NColorClasses];\r
612 \r
613 MyColorizeAttribs colorizeAttribs[] = {\r
614   { (COLORREF)0, 0, N_("Shout Text") },\r
615   { (COLORREF)0, 0, N_("SShout/CShout") },\r
616   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
617   { (COLORREF)0, 0, N_("Channel Text") },\r
618   { (COLORREF)0, 0, N_("Kibitz Text") },\r
619   { (COLORREF)0, 0, N_("Tell Text") },\r
620   { (COLORREF)0, 0, N_("Challenge Text") },\r
621   { (COLORREF)0, 0, N_("Request Text") },\r
622   { (COLORREF)0, 0, N_("Seek Text") },\r
623   { (COLORREF)0, 0, N_("Normal Text") },\r
624   { (COLORREF)0, 0, N_("None") }\r
625 };\r
626 \r
627 \r
628 \r
629 static char *commentTitle;\r
630 static char *commentText;\r
631 static int commentIndex;\r
632 static Boolean editComment = FALSE;\r
633 \r
634 \r
635 char errorTitle[MSG_SIZ];\r
636 char errorMessage[2*MSG_SIZ];\r
637 HWND errorDialog = NULL;\r
638 BOOLEAN moveErrorMessageUp = FALSE;\r
639 BOOLEAN consoleEcho = TRUE;\r
640 CHARFORMAT consoleCF;\r
641 COLORREF consoleBackgroundColor;\r
642 \r
643 char *programVersion;\r
644 \r
645 #define CPReal 1\r
646 #define CPComm 2\r
647 #define CPSock 3\r
648 #define CPRcmd 4\r
649 typedef int CPKind;\r
650 \r
651 typedef struct {\r
652   CPKind kind;\r
653   HANDLE hProcess;\r
654   DWORD pid;\r
655   HANDLE hTo;\r
656   HANDLE hFrom;\r
657   SOCKET sock;\r
658   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
659 } ChildProc;\r
660 \r
661 #define INPUT_SOURCE_BUF_SIZE 4096\r
662 \r
663 typedef struct _InputSource {\r
664   CPKind kind;\r
665   HANDLE hFile;\r
666   SOCKET sock;\r
667   int lineByLine;\r
668   HANDLE hThread;\r
669   DWORD id;\r
670   char buf[INPUT_SOURCE_BUF_SIZE];\r
671   char *next;\r
672   DWORD count;\r
673   int error;\r
674   InputCallback func;\r
675   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
676   VOIDSTAR closure;\r
677 } InputSource;\r
678 \r
679 InputSource *consoleInputSource;\r
680 \r
681 DCB dcb;\r
682 \r
683 /* forward */\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
685 VOID ConsoleCreate();\r
686 LRESULT CALLBACK\r
687   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);\r
691 LRESULT CALLBACK\r
692   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
694 void ParseIcsTextMenu(char *icsTextMenuString);\r
695 VOID PopUpNameDialog(char firstchar);\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
697 \r
698 /* [AS] */\r
699 int NewGameFRC();\r
700 int GameListOptions();\r
701 \r
702 int dummy; // [HGM] for obsolete args\r
703 \r
704 HWND hwndMain = NULL;        /* root window*/\r
705 HWND hwndConsole = NULL;\r
706 HWND commentDialog = NULL;\r
707 HWND moveHistoryDialog = NULL;\r
708 HWND evalGraphDialog = NULL;\r
709 HWND engineOutputDialog = NULL;\r
710 HWND gameListDialog = NULL;\r
711 HWND editTagsDialog = NULL;\r
712 \r
713 int commentUp = FALSE;\r
714 \r
715 WindowPlacement wpMain;\r
716 WindowPlacement wpConsole;\r
717 WindowPlacement wpComment;\r
718 WindowPlacement wpMoveHistory;\r
719 WindowPlacement wpEvalGraph;\r
720 WindowPlacement wpEngineOutput;\r
721 WindowPlacement wpGameList;\r
722 WindowPlacement wpTags;\r
723 \r
724 VOID EngineOptionsPopup(); // [HGM] settings\r
725 \r
726 VOID GothicPopUp(char *title, VariantClass variant);\r
727 /*\r
728  * Setting "frozen" should disable all user input other than deleting\r
729  * the window.  We do this while engines are initializing themselves.\r
730  */\r
731 static int frozen = 0;\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];\r
733 void FreezeUI()\r
734 {\r
735   HMENU hmenu;\r
736   int i;\r
737 \r
738   if (frozen) return;\r
739   frozen = 1;\r
740   hmenu = GetMenu(hwndMain);\r
741   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
742     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
743   }\r
744   DrawMenuBar(hwndMain);\r
745 }\r
746 \r
747 /* Undo a FreezeUI */\r
748 void ThawUI()\r
749 {\r
750   HMENU hmenu;\r
751   int i;\r
752 \r
753   if (!frozen) return;\r
754   frozen = 0;\r
755   hmenu = GetMenu(hwndMain);\r
756   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
757     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
758   }\r
759   DrawMenuBar(hwndMain);\r
760 }\r
761 \r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
763 \r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
765 #ifdef JAWS\r
766 #include "jaws.c"\r
767 #else\r
768 #define JAWS_INIT\r
769 #define JAWS_ARGS\r
770 #define JAWS_ALT_INTERCEPT\r
771 #define JAWS_KBUP_NAVIGATION\r
772 #define JAWS_KBDOWN_NAVIGATION\r
773 #define JAWS_MENU_ITEMS\r
774 #define JAWS_SILENCE\r
775 #define JAWS_REPLAY\r
776 #define JAWS_ACCEL\r
777 #define JAWS_COPYRIGHT\r
778 #define JAWS_DELETE(X) X\r
779 #define SAYMACHINEMOVE()\r
780 #define SAY(X)\r
781 #endif\r
782 \r
783 /*---------------------------------------------------------------------------*\\r
784  *\r
785  * WinMain\r
786  *\r
787 \*---------------------------------------------------------------------------*/\r
788 \r
789 int APIENTRY\r
790 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
791         LPSTR lpCmdLine, int nCmdShow)\r
792 {\r
793   MSG msg;\r
794   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
795 //  INITCOMMONCONTROLSEX ex;\r
796 \r
797   debugFP = stderr;\r
798 \r
799   LoadLibrary("RICHED32.DLL");\r
800   consoleCF.cbSize = sizeof(CHARFORMAT);\r
801 \r
802   if (!InitApplication(hInstance)) {\r
803     return (FALSE);\r
804   }\r
805   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
806     return (FALSE);\r
807   }\r
808 \r
809   JAWS_INIT\r
810   TranslateMenus(1);\r
811 \r
812 //  InitCommonControlsEx(&ex);\r
813   InitCommonControls();\r
814 \r
815   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
816   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
817   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
818 \r
819   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
820 \r
821   while (GetMessage(&msg, /* message structure */\r
822                     NULL, /* handle of window receiving the message */\r
823                     0,    /* lowest message to examine */\r
824                     0))   /* highest message to examine */\r
825     {\r
826 \r
827       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
828         // [HGM] navigate: switch between all windows with tab\r
829         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
830         int i, currentElement = 0;\r
831 \r
832         // first determine what element of the chain we come from (if any)\r
833         if(appData.icsActive) {\r
834             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
835             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
836         }\r
837         if(engineOutputDialog && EngineOutputIsUp()) {\r
838             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
839             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
840         }\r
841         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
842             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
843         }\r
844         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
845         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
846         if(msg.hwnd == e1)                 currentElement = 2; else\r
847         if(msg.hwnd == e2)                 currentElement = 3; else\r
848         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
849         if(msg.hwnd == mh)                currentElement = 4; else\r
850         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
851         if(msg.hwnd == hText)  currentElement = 5; else\r
852         if(msg.hwnd == hInput) currentElement = 6; else\r
853         for (i = 0; i < N_BUTTONS; i++) {\r
854             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
855         }\r
856 \r
857         // determine where to go to\r
858         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
859           do {\r
860             currentElement = (currentElement + direction) % 7;\r
861             switch(currentElement) {\r
862                 case 0:\r
863                   h = hwndMain; break; // passing this case always makes the loop exit\r
864                 case 1:\r
865                   h = buttonDesc[0].hwnd; break; // could be NULL\r
866                 case 2:\r
867                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
868                   h = e1; break;\r
869                 case 3:\r
870                   if(!EngineOutputIsUp()) continue;\r
871                   h = e2; break;\r
872                 case 4:\r
873                   if(!MoveHistoryIsUp()) continue;\r
874                   h = mh; break;\r
875 //              case 6: // input to eval graph does not seem to get here!\r
876 //                if(!EvalGraphIsUp()) continue;\r
877 //                h = evalGraphDialog; break;\r
878                 case 5:\r
879                   if(!appData.icsActive) continue;\r
880                   SAY("display");\r
881                   h = hText; break;\r
882                 case 6:\r
883                   if(!appData.icsActive) continue;\r
884                   SAY("input");\r
885                   h = hInput; break;\r
886             }\r
887           } while(h == 0);\r
888 \r
889           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
890           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
891           SetFocus(h);\r
892 \r
893           continue; // this message now has been processed\r
894         }\r
895       }\r
896 \r
897       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
898           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
899           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
900           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
901           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
902           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
903           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
904           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
905           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
906           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
907         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
908         for(i=0; i<MAX_CHAT; i++) \r
909             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
910                 done = 1; break;\r
911         }\r
912         if(done) continue; // [HGM] chat: end patch\r
913         TranslateMessage(&msg); /* Translates virtual key codes */\r
914         DispatchMessage(&msg);  /* Dispatches message to window */\r
915       }\r
916     }\r
917 \r
918 \r
919   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
920 }\r
921 \r
922 /*---------------------------------------------------------------------------*\\r
923  *\r
924  * Initialization functions\r
925  *\r
926 \*---------------------------------------------------------------------------*/\r
927 \r
928 void\r
929 SetUserLogo()\r
930 {   // update user logo if necessary\r
931     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
932 \r
933     if(appData.autoLogo) {\r
934           curName = UserName();\r
935           if(strcmp(curName, oldUserName)) {\r
936                 GetCurrentDirectory(MSG_SIZ, dir);\r
937                 SetCurrentDirectory(installDir);\r
938                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
939                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
940                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
941                 if(userLogo == NULL)\r
942                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
943                 SetCurrentDirectory(dir); /* return to prev directory */\r
944           }\r
945     }\r
946 }\r
947 \r
948 BOOL\r
949 InitApplication(HINSTANCE hInstance)\r
950 {\r
951   WNDCLASS wc;\r
952 \r
953   /* Fill in window class structure with parameters that describe the */\r
954   /* main window. */\r
955 \r
956   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
957   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
958   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
959   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
960   wc.hInstance     = hInstance;         /* Owner of this class */\r
961   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
962   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
963   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
964   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
965   wc.lpszClassName = szAppName;                 /* Name to register as */\r
966 \r
967   /* Register the window class and return success/failure code. */\r
968   if (!RegisterClass(&wc)) return FALSE;\r
969 \r
970   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
971   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
972   wc.cbClsExtra    = 0;\r
973   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
974   wc.hInstance     = hInstance;\r
975   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
976   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
977   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
978   wc.lpszMenuName  = NULL;\r
979   wc.lpszClassName = szConsoleName;\r
980 \r
981   if (!RegisterClass(&wc)) return FALSE;\r
982   return TRUE;\r
983 }\r
984 \r
985 \r
986 /* Set by InitInstance, used by EnsureOnScreen */\r
987 int screenHeight, screenWidth;\r
988 RECT screenGeometry;\r
989 \r
990 void\r
991 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
992 {\r
993 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
994   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
995   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
996   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
997   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
998   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
999 }\r
1000 \r
1001 VOID\r
1002 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1003 {\r
1004   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1005   GetCurrentDirectory(MSG_SIZ, dir);\r
1006   SetCurrentDirectory(installDir);\r
1007   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1008       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1009 \r
1010       if (cps->programLogo == NULL && appData.debugMode) {\r
1011           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1012       }\r
1013   } else if(appData.autoLogo) {\r
1014       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1015         char *opponent = "";\r
1016         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1017         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1018         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1019         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1020             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1021             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1022         }\r
1023       } else\r
1024       if(appData.directory[n] && appData.directory[n][0]) {\r
1025         SetCurrentDirectory(appData.directory[n]);\r
1026         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1027       }\r
1028   }\r
1029   SetCurrentDirectory(dir); /* return to prev directory */\r
1030 }\r
1031 \r
1032 VOID\r
1033 InitTextures()\r
1034 {\r
1035   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1036   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1037   \r
1038   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1039       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1040       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1041       liteBackTextureMode = appData.liteBackTextureMode;\r
1042 \r
1043       if (liteBackTexture == NULL && appData.debugMode) {\r
1044           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1045       }\r
1046   }\r
1047   \r
1048   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1049       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1050       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1051       darkBackTextureMode = appData.darkBackTextureMode;\r
1052 \r
1053       if (darkBackTexture == NULL && appData.debugMode) {\r
1054           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1055       }\r
1056   }\r
1057 }\r
1058 \r
1059 #ifndef SM_CXVIRTUALSCREEN\r
1060 #define SM_CXVIRTUALSCREEN 78\r
1061 #endif\r
1062 #ifndef SM_CYVIRTUALSCREEN\r
1063 #define SM_CYVIRTUALSCREEN 79\r
1064 #endif\r
1065 #ifndef SM_XVIRTUALSCREEN \r
1066 #define SM_XVIRTUALSCREEN 76\r
1067 #endif\r
1068 #ifndef SM_YVIRTUALSCREEN \r
1069 #define SM_YVIRTUALSCREEN 77\r
1070 #endif\r
1071 \r
1072 VOID\r
1073 InitGeometry()\r
1074 {\r
1075   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1076   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1077   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1078   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1079   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1080   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1081   screenGeometry.right = screenGeometry.left + screenWidth;\r
1082   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1083 }\r
1084 \r
1085 BOOL\r
1086 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1087 {\r
1088   HWND hwnd; /* Main window handle. */\r
1089   int ibs;\r
1090   WINDOWPLACEMENT wp;\r
1091   char *filepart;\r
1092 \r
1093   hInst = hInstance;    /* Store instance handle in our global variable */\r
1094   programName = szAppName;\r
1095 \r
1096   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1097     *filepart = NULLCHAR;\r
1098     SetCurrentDirectory(installDir);\r
1099   } else {\r
1100     GetCurrentDirectory(MSG_SIZ, installDir);\r
1101   }\r
1102   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1103   InitGeometry();\r
1104   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1105   /* xboard, and older WinBoards, controlled the move sound with the\r
1106      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1107      always turn the option on (so that the backend will call us),\r
1108      then let the user turn the sound off by setting it to silence if\r
1109      desired.  To accommodate old winboard.ini files saved by old\r
1110      versions of WinBoard, we also turn off the sound if the option\r
1111      was initially set to false. [HGM] taken out of InitAppData */\r
1112   if (!appData.ringBellAfterMoves) {\r
1113     sounds[(int)SoundMove].name = strdup("");\r
1114     appData.ringBellAfterMoves = TRUE;\r
1115   }\r
1116   if (appData.debugMode) {\r
1117     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1118     setbuf(debugFP, NULL);\r
1119   }\r
1120 \r
1121   LoadLanguageFile(appData.language);\r
1122 \r
1123   InitBackEnd1();\r
1124 \r
1125 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1126 //  InitEngineUCI( installDir, &second );\r
1127 \r
1128   /* Create a main window for this application instance. */\r
1129   hwnd = CreateWindow(szAppName, szTitle,\r
1130                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1131                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1132                       NULL, NULL, hInstance, NULL);\r
1133   hwndMain = hwnd;\r
1134 \r
1135   /* If window could not be created, return "failure" */\r
1136   if (!hwnd) {\r
1137     return (FALSE);\r
1138   }\r
1139 \r
1140   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1141   LoadLogo(&first, 0, FALSE);\r
1142   LoadLogo(&second, 1, appData.icsActive);\r
1143 \r
1144   SetUserLogo();\r
1145 \r
1146   iconWhite = LoadIcon(hInstance, "icon_white");\r
1147   iconBlack = LoadIcon(hInstance, "icon_black");\r
1148   iconCurrent = iconWhite;\r
1149   InitDrawingColors();\r
1150 \r
1151   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1152   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1153     /* Compute window size for each board size, and use the largest\r
1154        size that fits on this screen as the default. */\r
1155     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1156     if (boardSize == (BoardSize)-1 &&\r
1157         winH <= screenHeight\r
1158            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1159         && winW <= screenWidth) {\r
1160       boardSize = (BoardSize)ibs;\r
1161     }\r
1162   }\r
1163 \r
1164   InitDrawingSizes(boardSize, 0);\r
1165   RecentEngineMenu(appData.recentEngineList);\r
1166   InitMenuChecks();\r
1167   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1168 \r
1169   /* [AS] Load textures if specified */\r
1170   InitTextures();\r
1171 \r
1172   mysrandom( (unsigned) time(NULL) );\r
1173 \r
1174   /* [AS] Restore layout */\r
1175   if( wpMoveHistory.visible ) {\r
1176       MoveHistoryPopUp();\r
1177   }\r
1178 \r
1179   if( wpEvalGraph.visible ) {\r
1180       EvalGraphPopUp();\r
1181   }\r
1182 \r
1183   if( wpEngineOutput.visible ) {\r
1184       EngineOutputPopUp();\r
1185   }\r
1186 \r
1187   /* Make the window visible; update its client area; and return "success" */\r
1188   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1189   wp.length = sizeof(WINDOWPLACEMENT);\r
1190   wp.flags = 0;\r
1191   wp.showCmd = nCmdShow;\r
1192   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1193   wp.rcNormalPosition.left = wpMain.x;\r
1194   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1195   wp.rcNormalPosition.top = wpMain.y;\r
1196   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1197   SetWindowPlacement(hwndMain, &wp);\r
1198 \r
1199   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1200 \r
1201   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1202                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1203 \r
1204   if (hwndConsole) {\r
1205 #if AOT_CONSOLE\r
1206     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1207                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1208 #endif\r
1209     ShowWindow(hwndConsole, nCmdShow);\r
1210     SetActiveWindow(hwndConsole);\r
1211   }\r
1212   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1213   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1214 \r
1215   return TRUE;\r
1216 \r
1217 }\r
1218 \r
1219 VOID\r
1220 InitMenuChecks()\r
1221 {\r
1222   HMENU hmenu = GetMenu(hwndMain);\r
1223 \r
1224   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1225                         MF_BYCOMMAND|((appData.icsActive &&\r
1226                                        *appData.icsCommPort != NULLCHAR) ?\r
1227                                       MF_ENABLED : MF_GRAYED));\r
1228   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1229                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1230                                      MF_CHECKED : MF_UNCHECKED));\r
1231 }\r
1232 \r
1233 //---------------------------------------------------------------------------------------------------------\r
1234 \r
1235 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1236 #define XBOARD FALSE\r
1237 \r
1238 #define OPTCHAR "/"\r
1239 #define SEPCHAR "="\r
1240 #define TOPLEVEL 0\r
1241 \r
1242 #include "args.h"\r
1243 \r
1244 // front-end part of option handling\r
1245 \r
1246 VOID\r
1247 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1248 {\r
1249   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1250   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1251   DeleteDC(hdc);\r
1252   lf->lfWidth = 0;\r
1253   lf->lfEscapement = 0;\r
1254   lf->lfOrientation = 0;\r
1255   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1256   lf->lfItalic = mfp->italic;\r
1257   lf->lfUnderline = mfp->underline;\r
1258   lf->lfStrikeOut = mfp->strikeout;\r
1259   lf->lfCharSet = mfp->charset;\r
1260   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1261   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1262   lf->lfQuality = DEFAULT_QUALITY;\r
1263   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1264     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1265 }\r
1266 \r
1267 void\r
1268 CreateFontInMF(MyFont *mf)\r
1269\r
1270   LFfromMFP(&mf->lf, &mf->mfp);\r
1271   if (mf->hf) DeleteObject(mf->hf);\r
1272   mf->hf = CreateFontIndirect(&mf->lf);\r
1273 }\r
1274 \r
1275 // [HGM] This platform-dependent table provides the location for storing the color info\r
1276 void *\r
1277 colorVariable[] = {\r
1278   &whitePieceColor, \r
1279   &blackPieceColor, \r
1280   &lightSquareColor,\r
1281   &darkSquareColor, \r
1282   &highlightSquareColor,\r
1283   &premoveHighlightColor,\r
1284   NULL,\r
1285   &consoleBackgroundColor,\r
1286   &appData.fontForeColorWhite,\r
1287   &appData.fontBackColorWhite,\r
1288   &appData.fontForeColorBlack,\r
1289   &appData.fontBackColorBlack,\r
1290   &appData.evalHistColorWhite,\r
1291   &appData.evalHistColorBlack,\r
1292   &appData.highlightArrowColor,\r
1293 };\r
1294 \r
1295 /* Command line font name parser.  NULL name means do nothing.\r
1296    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1297    For backward compatibility, syntax without the colon is also\r
1298    accepted, but font names with digits in them won't work in that case.\r
1299 */\r
1300 VOID\r
1301 ParseFontName(char *name, MyFontParams *mfp)\r
1302 {\r
1303   char *p, *q;\r
1304   if (name == NULL) return;\r
1305   p = name;\r
1306   q = strchr(p, ':');\r
1307   if (q) {\r
1308     if (q - p >= sizeof(mfp->faceName))\r
1309       ExitArgError(_("Font name too long:"), name, TRUE);\r
1310     memcpy(mfp->faceName, p, q - p);\r
1311     mfp->faceName[q - p] = NULLCHAR;\r
1312     p = q + 1;\r
1313   } else {\r
1314     q = mfp->faceName;\r
1315 \r
1316     while (*p && !isdigit(*p)) {\r
1317       *q++ = *p++;\r
1318       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1319         ExitArgError(_("Font name too long:"), name, TRUE);\r
1320     }\r
1321     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1322     *q = NULLCHAR;\r
1323   }\r
1324   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1325   mfp->pointSize = (float) atof(p);\r
1326   mfp->bold = (strchr(p, 'b') != NULL);\r
1327   mfp->italic = (strchr(p, 'i') != NULL);\r
1328   mfp->underline = (strchr(p, 'u') != NULL);\r
1329   mfp->strikeout = (strchr(p, 's') != NULL);\r
1330   mfp->charset = DEFAULT_CHARSET;\r
1331   q = strchr(p, 'c');\r
1332   if (q)\r
1333     mfp->charset = (BYTE) atoi(q+1);\r
1334 }\r
1335 \r
1336 void\r
1337 ParseFont(char *name, int number)\r
1338 { // wrapper to shield back-end from 'font'\r
1339   ParseFontName(name, &font[boardSize][number]->mfp);\r
1340 }\r
1341 \r
1342 void\r
1343 SetFontDefaults()\r
1344 { // in WB  we have a 2D array of fonts; this initializes their description\r
1345   int i, j;\r
1346   /* Point font array elements to structures and\r
1347      parse default font names */\r
1348   for (i=0; i<NUM_FONTS; i++) {\r
1349     for (j=0; j<NUM_SIZES; j++) {\r
1350       font[j][i] = &fontRec[j][i];\r
1351       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1352     }\r
1353   }\r
1354 }\r
1355 \r
1356 void\r
1357 CreateFonts()\r
1358 { // here we create the actual fonts from the selected descriptions\r
1359   int i, j;\r
1360   for (i=0; i<NUM_FONTS; i++) {\r
1361     for (j=0; j<NUM_SIZES; j++) {\r
1362       CreateFontInMF(font[j][i]);\r
1363     }\r
1364   }\r
1365 }\r
1366 /* Color name parser.\r
1367    X version accepts X color names, but this one\r
1368    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1369 COLORREF\r
1370 ParseColorName(char *name)\r
1371 {\r
1372   int red, green, blue, count;\r
1373   char buf[MSG_SIZ];\r
1374 \r
1375   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1376   if (count != 3) {\r
1377     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1378       &red, &green, &blue);\r
1379   }\r
1380   if (count != 3) {\r
1381     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1382     DisplayError(buf, 0);\r
1383     return RGB(0, 0, 0);\r
1384   }\r
1385   return PALETTERGB(red, green, blue);\r
1386 }\r
1387 \r
1388 void\r
1389 ParseColor(int n, char *name)\r
1390 { // for WinBoard the color is an int, which needs to be derived from the string\r
1391   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1392 }\r
1393 \r
1394 void\r
1395 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1396 {\r
1397   char *e = argValue;\r
1398   int eff = 0;\r
1399 \r
1400   while (*e) {\r
1401     if (*e == 'b')      eff |= CFE_BOLD;\r
1402     else if (*e == 'i') eff |= CFE_ITALIC;\r
1403     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1404     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1405     else if (*e == '#' || isdigit(*e)) break;\r
1406     e++;\r
1407   }\r
1408   *effects = eff;\r
1409   *color   = ParseColorName(e);\r
1410 }\r
1411 \r
1412 void\r
1413 ParseTextAttribs(ColorClass cc, char *s)\r
1414 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1415     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1416     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1417 }\r
1418 \r
1419 void\r
1420 ParseBoardSize(void *addr, char *name)\r
1421 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1422   BoardSize bs = SizeTiny;\r
1423   while (sizeInfo[bs].name != NULL) {\r
1424     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1425         *(BoardSize *)addr = bs;\r
1426         return;\r
1427     }\r
1428     bs++;\r
1429   }\r
1430   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1431 }\r
1432 \r
1433 void\r
1434 LoadAllSounds()\r
1435 { // [HGM] import name from appData first\r
1436   ColorClass cc;\r
1437   SoundClass sc;\r
1438   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1439     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1440     textAttribs[cc].sound.data = NULL;\r
1441     MyLoadSound(&textAttribs[cc].sound);\r
1442   }\r
1443   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1444     textAttribs[cc].sound.name = strdup("");\r
1445     textAttribs[cc].sound.data = NULL;\r
1446   }\r
1447   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1448     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1449     sounds[sc].data = NULL;\r
1450     MyLoadSound(&sounds[sc]);\r
1451   }\r
1452 }\r
1453 \r
1454 void\r
1455 SetCommPortDefaults()\r
1456 {\r
1457    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1458   dcb.DCBlength = sizeof(DCB);\r
1459   dcb.BaudRate = 9600;\r
1460   dcb.fBinary = TRUE;\r
1461   dcb.fParity = FALSE;\r
1462   dcb.fOutxCtsFlow = FALSE;\r
1463   dcb.fOutxDsrFlow = FALSE;\r
1464   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1465   dcb.fDsrSensitivity = FALSE;\r
1466   dcb.fTXContinueOnXoff = TRUE;\r
1467   dcb.fOutX = FALSE;\r
1468   dcb.fInX = FALSE;\r
1469   dcb.fNull = FALSE;\r
1470   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1471   dcb.fAbortOnError = FALSE;\r
1472   dcb.ByteSize = 7;\r
1473   dcb.Parity = SPACEPARITY;\r
1474   dcb.StopBits = ONESTOPBIT;\r
1475 }\r
1476 \r
1477 // [HGM] args: these three cases taken out to stay in front-end\r
1478 void\r
1479 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1480 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1481         // while the curent board size determines the element. This system should be ported to XBoard.\r
1482         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1483         int bs;\r
1484         for (bs=0; bs<NUM_SIZES; bs++) {\r
1485           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1486           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1487           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1488             ad->argName, mfp->faceName, mfp->pointSize,\r
1489             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1490             mfp->bold ? "b" : "",\r
1491             mfp->italic ? "i" : "",\r
1492             mfp->underline ? "u" : "",\r
1493             mfp->strikeout ? "s" : "",\r
1494             (int)mfp->charset);\r
1495         }\r
1496       }\r
1497 \r
1498 void\r
1499 ExportSounds()\r
1500 { // [HGM] copy the names from the internal WB variables to appData\r
1501   ColorClass cc;\r
1502   SoundClass sc;\r
1503   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1504     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1505   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1506     (&appData.soundMove)[sc] = sounds[sc].name;\r
1507 }\r
1508 \r
1509 void\r
1510 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1511 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1512         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1513         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1514           (ta->effects & CFE_BOLD) ? "b" : "",\r
1515           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1516           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1517           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1518           (ta->effects) ? " " : "",\r
1519           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1520       }\r
1521 \r
1522 void\r
1523 SaveColor(FILE *f, ArgDescriptor *ad)\r
1524 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1525         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1526         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1527           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1528 }\r
1529 \r
1530 void\r
1531 SaveBoardSize(FILE *f, char *name, void *addr)\r
1532 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1533   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1534 }\r
1535 \r
1536 void\r
1537 ParseCommPortSettings(char *s)\r
1538 { // wrapper to keep dcb from back-end\r
1539   ParseCommSettings(s, &dcb);\r
1540 }\r
1541 \r
1542 void\r
1543 GetWindowCoords()\r
1544 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1545   GetActualPlacement(hwndMain, &wpMain);\r
1546   GetActualPlacement(hwndConsole, &wpConsole);\r
1547   GetActualPlacement(commentDialog, &wpComment);\r
1548   GetActualPlacement(editTagsDialog, &wpTags);\r
1549   GetActualPlacement(gameListDialog, &wpGameList);\r
1550   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1551   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1552   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1553 }\r
1554 \r
1555 void\r
1556 PrintCommPortSettings(FILE *f, char *name)\r
1557 { // wrapper to shield back-end from DCB\r
1558       PrintCommSettings(f, name, &dcb);\r
1559 }\r
1560 \r
1561 int\r
1562 MySearchPath(char *installDir, char *name, char *fullname)\r
1563 {\r
1564   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1565   if(name[0]== '%') {\r
1566     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1567     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1568       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1569       *strchr(buf, '%') = 0;\r
1570       strcat(fullname, getenv(buf));\r
1571       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1572     }\r
1573     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1574     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1575     return (int) strlen(fullname);\r
1576   }\r
1577   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1578 }\r
1579 \r
1580 int\r
1581 MyGetFullPathName(char *name, char *fullname)\r
1582 {\r
1583   char *dummy;\r
1584   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1585 }\r
1586 \r
1587 int\r
1588 MainWindowUp()\r
1589 { // [HGM] args: allows testing if main window is realized from back-end\r
1590   return hwndMain != NULL;\r
1591 }\r
1592 \r
1593 void\r
1594 PopUpStartupDialog()\r
1595 {\r
1596     FARPROC lpProc;\r
1597     \r
1598     LoadLanguageFile(appData.language);\r
1599     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1600     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1601     FreeProcInstance(lpProc);\r
1602 }\r
1603 \r
1604 /*---------------------------------------------------------------------------*\\r
1605  *\r
1606  * GDI board drawing routines\r
1607  *\r
1608 \*---------------------------------------------------------------------------*/\r
1609 \r
1610 /* [AS] Draw square using background texture */\r
1611 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1612 {\r
1613     XFORM   x;\r
1614 \r
1615     if( mode == 0 ) {\r
1616         return; /* Should never happen! */\r
1617     }\r
1618 \r
1619     SetGraphicsMode( dst, GM_ADVANCED );\r
1620 \r
1621     switch( mode ) {\r
1622     case 1:\r
1623         /* Identity */\r
1624         break;\r
1625     case 2:\r
1626         /* X reflection */\r
1627         x.eM11 = -1.0;\r
1628         x.eM12 = 0;\r
1629         x.eM21 = 0;\r
1630         x.eM22 = 1.0;\r
1631         x.eDx = (FLOAT) dw + dx - 1;\r
1632         x.eDy = 0;\r
1633         dx = 0;\r
1634         SetWorldTransform( dst, &x );\r
1635         break;\r
1636     case 3:\r
1637         /* Y reflection */\r
1638         x.eM11 = 1.0;\r
1639         x.eM12 = 0;\r
1640         x.eM21 = 0;\r
1641         x.eM22 = -1.0;\r
1642         x.eDx = 0;\r
1643         x.eDy = (FLOAT) dh + dy - 1;\r
1644         dy = 0;\r
1645         SetWorldTransform( dst, &x );\r
1646         break;\r
1647     case 4:\r
1648         /* X/Y flip */\r
1649         x.eM11 = 0;\r
1650         x.eM12 = 1.0;\r
1651         x.eM21 = 1.0;\r
1652         x.eM22 = 0;\r
1653         x.eDx = (FLOAT) dx;\r
1654         x.eDy = (FLOAT) dy;\r
1655         dx = 0;\r
1656         dy = 0;\r
1657         SetWorldTransform( dst, &x );\r
1658         break;\r
1659     }\r
1660 \r
1661     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1662 \r
1663     x.eM11 = 1.0;\r
1664     x.eM12 = 0;\r
1665     x.eM21 = 0;\r
1666     x.eM22 = 1.0;\r
1667     x.eDx = 0;\r
1668     x.eDy = 0;\r
1669     SetWorldTransform( dst, &x );\r
1670 \r
1671     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1672 }\r
1673 \r
1674 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1675 enum {\r
1676     PM_WP = (int) WhitePawn, \r
1677     PM_WN = (int) WhiteKnight, \r
1678     PM_WB = (int) WhiteBishop, \r
1679     PM_WR = (int) WhiteRook, \r
1680     PM_WQ = (int) WhiteQueen, \r
1681     PM_WF = (int) WhiteFerz, \r
1682     PM_WW = (int) WhiteWazir, \r
1683     PM_WE = (int) WhiteAlfil, \r
1684     PM_WM = (int) WhiteMan, \r
1685     PM_WO = (int) WhiteCannon, \r
1686     PM_WU = (int) WhiteUnicorn, \r
1687     PM_WH = (int) WhiteNightrider, \r
1688     PM_WA = (int) WhiteAngel, \r
1689     PM_WC = (int) WhiteMarshall, \r
1690     PM_WAB = (int) WhiteCardinal, \r
1691     PM_WD = (int) WhiteDragon, \r
1692     PM_WL = (int) WhiteLance, \r
1693     PM_WS = (int) WhiteCobra, \r
1694     PM_WV = (int) WhiteFalcon, \r
1695     PM_WSG = (int) WhiteSilver, \r
1696     PM_WG = (int) WhiteGrasshopper, \r
1697     PM_WK = (int) WhiteKing,\r
1698     PM_BP = (int) BlackPawn, \r
1699     PM_BN = (int) BlackKnight, \r
1700     PM_BB = (int) BlackBishop, \r
1701     PM_BR = (int) BlackRook, \r
1702     PM_BQ = (int) BlackQueen, \r
1703     PM_BF = (int) BlackFerz, \r
1704     PM_BW = (int) BlackWazir, \r
1705     PM_BE = (int) BlackAlfil, \r
1706     PM_BM = (int) BlackMan,\r
1707     PM_BO = (int) BlackCannon, \r
1708     PM_BU = (int) BlackUnicorn, \r
1709     PM_BH = (int) BlackNightrider, \r
1710     PM_BA = (int) BlackAngel, \r
1711     PM_BC = (int) BlackMarshall, \r
1712     PM_BG = (int) BlackGrasshopper, \r
1713     PM_BAB = (int) BlackCardinal,\r
1714     PM_BD = (int) BlackDragon,\r
1715     PM_BL = (int) BlackLance,\r
1716     PM_BS = (int) BlackCobra,\r
1717     PM_BV = (int) BlackFalcon,\r
1718     PM_BSG = (int) BlackSilver,\r
1719     PM_BK = (int) BlackKing\r
1720 };\r
1721 \r
1722 static HFONT hPieceFont = NULL;\r
1723 static HBITMAP hPieceMask[(int) EmptySquare];\r
1724 static HBITMAP hPieceFace[(int) EmptySquare];\r
1725 static int fontBitmapSquareSize = 0;\r
1726 static char pieceToFontChar[(int) EmptySquare] =\r
1727                               { 'p', 'n', 'b', 'r', 'q', \r
1728                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1729                       'k', 'o', 'm', 'v', 't', 'w', \r
1730                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1731                                                               'l' };\r
1732 \r
1733 extern BOOL SetCharTable( char *table, const char * map );\r
1734 /* [HGM] moved to backend.c */\r
1735 \r
1736 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1737 {\r
1738     HBRUSH hbrush;\r
1739     BYTE r1 = GetRValue( color );\r
1740     BYTE g1 = GetGValue( color );\r
1741     BYTE b1 = GetBValue( color );\r
1742     BYTE r2 = r1 / 2;\r
1743     BYTE g2 = g1 / 2;\r
1744     BYTE b2 = b1 / 2;\r
1745     RECT rc;\r
1746 \r
1747     /* Create a uniform background first */\r
1748     hbrush = CreateSolidBrush( color );\r
1749     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1750     FillRect( hdc, &rc, hbrush );\r
1751     DeleteObject( hbrush );\r
1752     \r
1753     if( mode == 1 ) {\r
1754         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1755         int steps = squareSize / 2;\r
1756         int i;\r
1757 \r
1758         for( i=0; i<steps; i++ ) {\r
1759             BYTE r = r1 - (r1-r2) * i / steps;\r
1760             BYTE g = g1 - (g1-g2) * i / steps;\r
1761             BYTE b = b1 - (b1-b2) * i / steps;\r
1762 \r
1763             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1764             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1765             FillRect( hdc, &rc, hbrush );\r
1766             DeleteObject(hbrush);\r
1767         }\r
1768     }\r
1769     else if( mode == 2 ) {\r
1770         /* Diagonal gradient, good more or less for every piece */\r
1771         POINT triangle[3];\r
1772         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1773         HBRUSH hbrush_old;\r
1774         int steps = squareSize;\r
1775         int i;\r
1776 \r
1777         triangle[0].x = squareSize - steps;\r
1778         triangle[0].y = squareSize;\r
1779         triangle[1].x = squareSize;\r
1780         triangle[1].y = squareSize;\r
1781         triangle[2].x = squareSize;\r
1782         triangle[2].y = squareSize - steps;\r
1783 \r
1784         for( i=0; i<steps; i++ ) {\r
1785             BYTE r = r1 - (r1-r2) * i / steps;\r
1786             BYTE g = g1 - (g1-g2) * i / steps;\r
1787             BYTE b = b1 - (b1-b2) * i / steps;\r
1788 \r
1789             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1790             hbrush_old = SelectObject( hdc, hbrush );\r
1791             Polygon( hdc, triangle, 3 );\r
1792             SelectObject( hdc, hbrush_old );\r
1793             DeleteObject(hbrush);\r
1794             triangle[0].x++;\r
1795             triangle[2].y++;\r
1796         }\r
1797 \r
1798         SelectObject( hdc, hpen );\r
1799     }\r
1800 }\r
1801 \r
1802 /*\r
1803     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1804     seems to work ok. The main problem here is to find the "inside" of a chess\r
1805     piece: follow the steps as explained below.\r
1806 */\r
1807 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1808 {\r
1809     HBITMAP hbm;\r
1810     HBITMAP hbm_old;\r
1811     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1812     RECT rc;\r
1813     SIZE sz;\r
1814     POINT pt;\r
1815     int backColor = whitePieceColor; \r
1816     int foreColor = blackPieceColor;\r
1817     \r
1818     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1819         backColor = appData.fontBackColorWhite;\r
1820         foreColor = appData.fontForeColorWhite;\r
1821     }\r
1822     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1823         backColor = appData.fontBackColorBlack;\r
1824         foreColor = appData.fontForeColorBlack;\r
1825     }\r
1826 \r
1827     /* Mask */\r
1828     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1829 \r
1830     hbm_old = SelectObject( hdc, hbm );\r
1831 \r
1832     rc.left = 0;\r
1833     rc.top = 0;\r
1834     rc.right = squareSize;\r
1835     rc.bottom = squareSize;\r
1836 \r
1837     /* Step 1: background is now black */\r
1838     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1839 \r
1840     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1841 \r
1842     pt.x = (squareSize - sz.cx) / 2;\r
1843     pt.y = (squareSize - sz.cy) / 2;\r
1844 \r
1845     SetBkMode( hdc, TRANSPARENT );\r
1846     SetTextColor( hdc, chroma );\r
1847     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1848     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1849 \r
1850     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1851     /* Step 3: the area outside the piece is filled with white */\r
1852 //    FloodFill( hdc, 0, 0, chroma );\r
1853     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1854     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1855     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1856     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1857     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1858     /* \r
1859         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1860         but if the start point is not inside the piece we're lost!\r
1861         There should be a better way to do this... if we could create a region or path\r
1862         from the fill operation we would be fine for example.\r
1863     */\r
1864 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1865     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1866 \r
1867     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1868         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1869         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1870 \r
1871         SelectObject( dc2, bm2 );\r
1872         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1873         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1874         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1875         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1876         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1877 \r
1878         DeleteDC( dc2 );\r
1879         DeleteObject( bm2 );\r
1880     }\r
1881 \r
1882     SetTextColor( hdc, 0 );\r
1883     /* \r
1884         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1885         draw the piece again in black for safety.\r
1886     */\r
1887     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1888 \r
1889     SelectObject( hdc, hbm_old );\r
1890 \r
1891     if( hPieceMask[index] != NULL ) {\r
1892         DeleteObject( hPieceMask[index] );\r
1893     }\r
1894 \r
1895     hPieceMask[index] = hbm;\r
1896 \r
1897     /* Face */\r
1898     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1899 \r
1900     SelectObject( hdc, hbm );\r
1901 \r
1902     {\r
1903         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1904         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1905         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1906 \r
1907         SelectObject( dc1, hPieceMask[index] );\r
1908         SelectObject( dc2, bm2 );\r
1909         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1910         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1911         \r
1912         /* \r
1913             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1914             the piece background and deletes (makes transparent) the rest.\r
1915             Thanks to that mask, we are free to paint the background with the greates\r
1916             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1917             We use this, to make gradients and give the pieces a "roundish" look.\r
1918         */\r
1919         SetPieceBackground( hdc, backColor, 2 );\r
1920         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1921 \r
1922         DeleteDC( dc2 );\r
1923         DeleteDC( dc1 );\r
1924         DeleteObject( bm2 );\r
1925     }\r
1926 \r
1927     SetTextColor( hdc, foreColor );\r
1928     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1929 \r
1930     SelectObject( hdc, hbm_old );\r
1931 \r
1932     if( hPieceFace[index] != NULL ) {\r
1933         DeleteObject( hPieceFace[index] );\r
1934     }\r
1935 \r
1936     hPieceFace[index] = hbm;\r
1937 }\r
1938 \r
1939 static int TranslatePieceToFontPiece( int piece )\r
1940 {\r
1941     switch( piece ) {\r
1942     case BlackPawn:\r
1943         return PM_BP;\r
1944     case BlackKnight:\r
1945         return PM_BN;\r
1946     case BlackBishop:\r
1947         return PM_BB;\r
1948     case BlackRook:\r
1949         return PM_BR;\r
1950     case BlackQueen:\r
1951         return PM_BQ;\r
1952     case BlackKing:\r
1953         return PM_BK;\r
1954     case WhitePawn:\r
1955         return PM_WP;\r
1956     case WhiteKnight:\r
1957         return PM_WN;\r
1958     case WhiteBishop:\r
1959         return PM_WB;\r
1960     case WhiteRook:\r
1961         return PM_WR;\r
1962     case WhiteQueen:\r
1963         return PM_WQ;\r
1964     case WhiteKing:\r
1965         return PM_WK;\r
1966 \r
1967     case BlackAngel:\r
1968         return PM_BA;\r
1969     case BlackMarshall:\r
1970         return PM_BC;\r
1971     case BlackFerz:\r
1972         return PM_BF;\r
1973     case BlackNightrider:\r
1974         return PM_BH;\r
1975     case BlackAlfil:\r
1976         return PM_BE;\r
1977     case BlackWazir:\r
1978         return PM_BW;\r
1979     case BlackUnicorn:\r
1980         return PM_BU;\r
1981     case BlackCannon:\r
1982         return PM_BO;\r
1983     case BlackGrasshopper:\r
1984         return PM_BG;\r
1985     case BlackMan:\r
1986         return PM_BM;\r
1987     case BlackSilver:\r
1988         return PM_BSG;\r
1989     case BlackLance:\r
1990         return PM_BL;\r
1991     case BlackFalcon:\r
1992         return PM_BV;\r
1993     case BlackCobra:\r
1994         return PM_BS;\r
1995     case BlackCardinal:\r
1996         return PM_BAB;\r
1997     case BlackDragon:\r
1998         return PM_BD;\r
1999 \r
2000     case WhiteAngel:\r
2001         return PM_WA;\r
2002     case WhiteMarshall:\r
2003         return PM_WC;\r
2004     case WhiteFerz:\r
2005         return PM_WF;\r
2006     case WhiteNightrider:\r
2007         return PM_WH;\r
2008     case WhiteAlfil:\r
2009         return PM_WE;\r
2010     case WhiteWazir:\r
2011         return PM_WW;\r
2012     case WhiteUnicorn:\r
2013         return PM_WU;\r
2014     case WhiteCannon:\r
2015         return PM_WO;\r
2016     case WhiteGrasshopper:\r
2017         return PM_WG;\r
2018     case WhiteMan:\r
2019         return PM_WM;\r
2020     case WhiteSilver:\r
2021         return PM_WSG;\r
2022     case WhiteLance:\r
2023         return PM_WL;\r
2024     case WhiteFalcon:\r
2025         return PM_WV;\r
2026     case WhiteCobra:\r
2027         return PM_WS;\r
2028     case WhiteCardinal:\r
2029         return PM_WAB;\r
2030     case WhiteDragon:\r
2031         return PM_WD;\r
2032     }\r
2033 \r
2034     return 0;\r
2035 }\r
2036 \r
2037 void CreatePiecesFromFont()\r
2038 {\r
2039     LOGFONT lf;\r
2040     HDC hdc_window = NULL;\r
2041     HDC hdc = NULL;\r
2042     HFONT hfont_old;\r
2043     int fontHeight;\r
2044     int i;\r
2045 \r
2046     if( fontBitmapSquareSize < 0 ) {\r
2047         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2048         return;\r
2049     }\r
2050 \r
2051     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2052             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2053         fontBitmapSquareSize = -1;\r
2054         return;\r
2055     }\r
2056 \r
2057     if( fontBitmapSquareSize != squareSize ) {\r
2058         hdc_window = GetDC( hwndMain );\r
2059         hdc = CreateCompatibleDC( hdc_window );\r
2060 \r
2061         if( hPieceFont != NULL ) {\r
2062             DeleteObject( hPieceFont );\r
2063         }\r
2064         else {\r
2065             for( i=0; i<=(int)BlackKing; i++ ) {\r
2066                 hPieceMask[i] = NULL;\r
2067                 hPieceFace[i] = NULL;\r
2068             }\r
2069         }\r
2070 \r
2071         fontHeight = 75;\r
2072 \r
2073         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2074             fontHeight = appData.fontPieceSize;\r
2075         }\r
2076 \r
2077         fontHeight = (fontHeight * squareSize) / 100;\r
2078 \r
2079         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2080         lf.lfWidth = 0;\r
2081         lf.lfEscapement = 0;\r
2082         lf.lfOrientation = 0;\r
2083         lf.lfWeight = FW_NORMAL;\r
2084         lf.lfItalic = 0;\r
2085         lf.lfUnderline = 0;\r
2086         lf.lfStrikeOut = 0;\r
2087         lf.lfCharSet = DEFAULT_CHARSET;\r
2088         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2089         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2090         lf.lfQuality = PROOF_QUALITY;\r
2091         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2092         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2093         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2094 \r
2095         hPieceFont = CreateFontIndirect( &lf );\r
2096 \r
2097         if( hPieceFont == NULL ) {\r
2098             fontBitmapSquareSize = -2;\r
2099         }\r
2100         else {\r
2101             /* Setup font-to-piece character table */\r
2102             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2103                 /* No (or wrong) global settings, try to detect the font */\r
2104                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2105                     /* Alpha */\r
2106                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2107                 }\r
2108                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2109                     /* DiagramTT* family */\r
2110                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2111                 }\r
2112                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2113                     /* Fairy symbols */\r
2114                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2115                 }\r
2116                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2117                     /* Good Companion (Some characters get warped as literal :-( */\r
2118                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2119                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2120                     SetCharTable(pieceToFontChar, s);\r
2121                 }\r
2122                 else {\r
2123                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2124                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2125                 }\r
2126             }\r
2127 \r
2128             /* Create bitmaps */\r
2129             hfont_old = SelectObject( hdc, hPieceFont );\r
2130             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2131                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2132                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2133 \r
2134             SelectObject( hdc, hfont_old );\r
2135 \r
2136             fontBitmapSquareSize = squareSize;\r
2137         }\r
2138     }\r
2139 \r
2140     if( hdc != NULL ) {\r
2141         DeleteDC( hdc );\r
2142     }\r
2143 \r
2144     if( hdc_window != NULL ) {\r
2145         ReleaseDC( hwndMain, hdc_window );\r
2146     }\r
2147 }\r
2148 \r
2149 HBITMAP\r
2150 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2151 {\r
2152   char name[128], buf[MSG_SIZ];\r
2153 \r
2154     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2155   if(appData.pieceDirectory[0]) {\r
2156     HBITMAP res;\r
2157     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2158     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2159     if(res) return res;\r
2160   }\r
2161   if (gameInfo.event &&\r
2162       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2163       strcmp(name, "k80s") == 0) {\r
2164     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2165   }\r
2166   return LoadBitmap(hinst, name);\r
2167 }\r
2168 \r
2169 \r
2170 /* Insert a color into the program's logical palette\r
2171    structure.  This code assumes the given color is\r
2172    the result of the RGB or PALETTERGB macro, and it\r
2173    knows how those macros work (which is documented).\r
2174 */\r
2175 VOID\r
2176 InsertInPalette(COLORREF color)\r
2177 {\r
2178   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2179 \r
2180   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2181     DisplayFatalError(_("Too many colors"), 0, 1);\r
2182     pLogPal->palNumEntries--;\r
2183     return;\r
2184   }\r
2185 \r
2186   pe->peFlags = (char) 0;\r
2187   pe->peRed = (char) (0xFF & color);\r
2188   pe->peGreen = (char) (0xFF & (color >> 8));\r
2189   pe->peBlue = (char) (0xFF & (color >> 16));\r
2190   return;\r
2191 }\r
2192 \r
2193 \r
2194 VOID\r
2195 InitDrawingColors()\r
2196 {\r
2197   if (pLogPal == NULL) {\r
2198     /* Allocate enough memory for a logical palette with\r
2199      * PALETTESIZE entries and set the size and version fields\r
2200      * of the logical palette structure.\r
2201      */\r
2202     pLogPal = (NPLOGPALETTE)\r
2203       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2204                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2205     pLogPal->palVersion    = 0x300;\r
2206   }\r
2207   pLogPal->palNumEntries = 0;\r
2208 \r
2209   InsertInPalette(lightSquareColor);\r
2210   InsertInPalette(darkSquareColor);\r
2211   InsertInPalette(whitePieceColor);\r
2212   InsertInPalette(blackPieceColor);\r
2213   InsertInPalette(highlightSquareColor);\r
2214   InsertInPalette(premoveHighlightColor);\r
2215 \r
2216   /*  create a logical color palette according the information\r
2217    *  in the LOGPALETTE structure.\r
2218    */\r
2219   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2220 \r
2221   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2222   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2223   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2224   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2225   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2226   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2227   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2228   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2229   /* [AS] Force rendering of the font-based pieces */\r
2230   if( fontBitmapSquareSize > 0 ) {\r
2231     fontBitmapSquareSize = 0;\r
2232   }\r
2233 }\r
2234 \r
2235 \r
2236 int\r
2237 BoardWidth(int boardSize, int n)\r
2238 { /* [HGM] argument n added to allow different width and height */\r
2239   int lineGap = sizeInfo[boardSize].lineGap;\r
2240 \r
2241   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2242       lineGap = appData.overrideLineGap;\r
2243   }\r
2244 \r
2245   return (n + 1) * lineGap +\r
2246           n * sizeInfo[boardSize].squareSize;\r
2247 }\r
2248 \r
2249 /* Respond to board resize by dragging edge */\r
2250 VOID\r
2251 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2252 {\r
2253   BoardSize newSize = NUM_SIZES - 1;\r
2254   static int recurse = 0;\r
2255   if (IsIconic(hwndMain)) return;\r
2256   if (recurse > 0) return;\r
2257   recurse++;\r
2258   while (newSize > 0) {\r
2259         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2260         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2261            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2262     newSize--;\r
2263   } \r
2264   boardSize = newSize;\r
2265   InitDrawingSizes(boardSize, flags);\r
2266   recurse--;\r
2267 }\r
2268 \r
2269 \r
2270 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2271 \r
2272 VOID\r
2273 InitDrawingSizes(BoardSize boardSize, int flags)\r
2274 {\r
2275   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2276   ChessSquare piece;\r
2277   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2278   HDC hdc;\r
2279   SIZE clockSize, messageSize;\r
2280   HFONT oldFont;\r
2281   char buf[MSG_SIZ];\r
2282   char *str;\r
2283   HMENU hmenu = GetMenu(hwndMain);\r
2284   RECT crect, wrect, oldRect;\r
2285   int offby;\r
2286   LOGBRUSH logbrush;\r
2287   VariantClass v = gameInfo.variant;\r
2288 \r
2289   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2290   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2291 \r
2292   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2293   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2294   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2295   oldBoardSize = boardSize;\r
2296 \r
2297   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2298   { // correct board size to one where built-in pieces exist\r
2299     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2300        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2301       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2302       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2303       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2304       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2305       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2306                                    boardSize = SizeMiddling;\r
2307     }\r
2308   }\r
2309   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2310 \r
2311   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2312   oldRect.top = wpMain.y;\r
2313   oldRect.right = wpMain.x + wpMain.width;\r
2314   oldRect.bottom = wpMain.y + wpMain.height;\r
2315 \r
2316   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2317   smallLayout = sizeInfo[boardSize].smallLayout;\r
2318   squareSize = sizeInfo[boardSize].squareSize;\r
2319   lineGap = sizeInfo[boardSize].lineGap;\r
2320   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2321   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2322 \r
2323   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2324       lineGap = appData.overrideLineGap;\r
2325   }\r
2326 \r
2327   if (tinyLayout != oldTinyLayout) {\r
2328     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2329     if (tinyLayout) {\r
2330       style &= ~WS_SYSMENU;\r
2331       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2332                  "&Minimize\tCtrl+F4");\r
2333     } else {\r
2334       style |= WS_SYSMENU;\r
2335       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2336     }\r
2337     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2338 \r
2339     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2340       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2341         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2342     }\r
2343     DrawMenuBar(hwndMain);\r
2344   }\r
2345 \r
2346   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2347   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2348 \r
2349   /* Get text area sizes */\r
2350   hdc = GetDC(hwndMain);\r
2351   if (appData.clockMode) {\r
2352     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2353   } else {\r
2354     snprintf(buf, MSG_SIZ, _("White"));\r
2355   }\r
2356   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2357   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2358   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2359   str = _("We only care about the height here");\r
2360   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2361   SelectObject(hdc, oldFont);\r
2362   ReleaseDC(hwndMain, hdc);\r
2363 \r
2364   /* Compute where everything goes */\r
2365   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2366         /* [HGM] logo: if either logo is on, reserve space for it */\r
2367         logoHeight =  2*clockSize.cy;\r
2368         leftLogoRect.left   = OUTER_MARGIN;\r
2369         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2370         leftLogoRect.top    = OUTER_MARGIN;\r
2371         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2372 \r
2373         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2374         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2375         rightLogoRect.top    = OUTER_MARGIN;\r
2376         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2377 \r
2378 \r
2379     whiteRect.left = leftLogoRect.right;\r
2380     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2381     whiteRect.top = OUTER_MARGIN;\r
2382     whiteRect.bottom = whiteRect.top + logoHeight;\r
2383 \r
2384     blackRect.right = rightLogoRect.left;\r
2385     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2386     blackRect.top = whiteRect.top;\r
2387     blackRect.bottom = whiteRect.bottom;\r
2388   } else {\r
2389     whiteRect.left = OUTER_MARGIN;\r
2390     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2391     whiteRect.top = OUTER_MARGIN;\r
2392     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2393 \r
2394     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2395     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2396     blackRect.top = whiteRect.top;\r
2397     blackRect.bottom = whiteRect.bottom;\r
2398 \r
2399     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2400   }\r
2401 \r
2402   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2403   if (appData.showButtonBar) {\r
2404     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2405       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2406   } else {\r
2407     messageRect.right = OUTER_MARGIN + boardWidth;\r
2408   }\r
2409   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2410   messageRect.bottom = messageRect.top + messageSize.cy;\r
2411 \r
2412   boardRect.left = OUTER_MARGIN;\r
2413   boardRect.right = boardRect.left + boardWidth;\r
2414   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2415   boardRect.bottom = boardRect.top + boardHeight;\r
2416 \r
2417   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2418   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2419   oldTinyLayout = tinyLayout;\r
2420   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2421   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2422     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2423   winW *= 1 + twoBoards;\r
2424   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2425   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2426   wpMain.height = winH; //       without disturbing window attachments\r
2427   GetWindowRect(hwndMain, &wrect);\r
2428   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2429                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2430 \r
2431   // [HGM] placement: let attached windows follow size change.\r
2432   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2433   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2434   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2435   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2436   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2437 \r
2438   /* compensate if menu bar wrapped */\r
2439   GetClientRect(hwndMain, &crect);\r
2440   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2441   wpMain.height += offby;\r
2442   switch (flags) {\r
2443   case WMSZ_TOPLEFT:\r
2444     SetWindowPos(hwndMain, NULL, \r
2445                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2446                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2447     break;\r
2448 \r
2449   case WMSZ_TOPRIGHT:\r
2450   case WMSZ_TOP:\r
2451     SetWindowPos(hwndMain, NULL, \r
2452                  wrect.left, wrect.bottom - wpMain.height, \r
2453                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2454     break;\r
2455 \r
2456   case WMSZ_BOTTOMLEFT:\r
2457   case WMSZ_LEFT:\r
2458     SetWindowPos(hwndMain, NULL, \r
2459                  wrect.right - wpMain.width, wrect.top, \r
2460                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2461     break;\r
2462 \r
2463   case WMSZ_BOTTOMRIGHT:\r
2464   case WMSZ_BOTTOM:\r
2465   case WMSZ_RIGHT:\r
2466   default:\r
2467     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2468                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2469     break;\r
2470   }\r
2471 \r
2472   hwndPause = NULL;\r
2473   for (i = 0; i < N_BUTTONS; i++) {\r
2474     if (buttonDesc[i].hwnd != NULL) {\r
2475       DestroyWindow(buttonDesc[i].hwnd);\r
2476       buttonDesc[i].hwnd = NULL;\r
2477     }\r
2478     if (appData.showButtonBar) {\r
2479       buttonDesc[i].hwnd =\r
2480         CreateWindow("BUTTON", buttonDesc[i].label,\r
2481                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2482                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2483                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2484                      (HMENU) buttonDesc[i].id,\r
2485                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2486       if (tinyLayout) {\r
2487         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2488                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2489                     MAKELPARAM(FALSE, 0));\r
2490       }\r
2491       if (buttonDesc[i].id == IDM_Pause)\r
2492         hwndPause = buttonDesc[i].hwnd;\r
2493       buttonDesc[i].wndproc = (WNDPROC)\r
2494         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2495     }\r
2496   }\r
2497   if (gridPen != NULL) DeleteObject(gridPen);\r
2498   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2499   if (premovePen != NULL) DeleteObject(premovePen);\r
2500   if (lineGap != 0) {\r
2501     logbrush.lbStyle = BS_SOLID;\r
2502     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2503     gridPen =\r
2504       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2505                    lineGap, &logbrush, 0, NULL);\r
2506     logbrush.lbColor = highlightSquareColor;\r
2507     highlightPen =\r
2508       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2509                    lineGap, &logbrush, 0, NULL);\r
2510 \r
2511     logbrush.lbColor = premoveHighlightColor; \r
2512     premovePen =\r
2513       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2514                    lineGap, &logbrush, 0, NULL);\r
2515 \r
2516     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2517     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2518       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2519       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2520         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2521       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2522         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2523       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2524     }\r
2525     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2526       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2527       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2528         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2529         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2530       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2531         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2532       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2533     }\r
2534   }\r
2535 \r
2536   /* [HGM] Licensing requirement */\r
2537 #ifdef GOTHIC\r
2538   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2539 #endif\r
2540 #ifdef FALCON\r
2541   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2542 #endif\r
2543   GothicPopUp( "", VariantNormal);\r
2544 \r
2545 \r
2546 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2547 \r
2548   /* Load piece bitmaps for this board size */\r
2549   for (i=0; i<=2; i++) {\r
2550     for (piece = WhitePawn;\r
2551          (int) piece < (int) BlackPawn;\r
2552          piece = (ChessSquare) ((int) piece + 1)) {\r
2553       if (pieceBitmap[i][piece] != NULL)\r
2554         DeleteObject(pieceBitmap[i][piece]);\r
2555     }\r
2556   }\r
2557 \r
2558   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2559   // Orthodox Chess pieces\r
2560   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2561   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2562   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2563   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2564   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2565   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2566   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2567   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2568   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2569   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2570   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2571   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2572   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2573   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2574   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2575   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2576     // in Shogi, Hijack the unused Queen for Lance\r
2577     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2578     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2579     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2580   } else {\r
2581     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2582     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2583     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2584   }\r
2585 \r
2586   if(squareSize <= 72 && squareSize >= 33) { \r
2587     /* A & C are available in most sizes now */\r
2588     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2589       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2590       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2591       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2592       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2593       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2594       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2595       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2596       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2597       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2598       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2599       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2600       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2601     } else { // Smirf-like\r
2602       if(gameInfo.variant == VariantSChess) {\r
2603         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2604         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2605         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2606       } else {\r
2607         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2608         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2609         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2610       }\r
2611     }\r
2612     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2613       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2614       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2615       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2616     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2617       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2618       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2619       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2620     } else { // WinBoard standard\r
2621       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2622       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2623       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2624     }\r
2625   }\r
2626 \r
2627 \r
2628   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2629     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2630     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2631     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2632     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2633     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2634     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2635     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2636     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2637     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2638     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2639     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2640     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2641     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2642     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2643     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2644     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2645     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2646     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2647     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2648     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2649     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2650     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2651     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2652     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2653     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2654     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2655     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2656     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2657     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2658     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2659 \r
2660     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2661       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2662       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2663       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2664       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2665       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2666       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2667       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2668       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2669       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2670       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2671       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2672       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2673     } else {\r
2674       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2675       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2676       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2677       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2678       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2679       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2680       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2681       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2682       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2683       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2684       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2685       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2686     }\r
2687 \r
2688   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2689     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2690     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2691     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2692     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2693     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2694     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2695     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2696     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2697     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2698     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2699     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2700     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2701     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2702     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2703   }\r
2704 \r
2705 \r
2706   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2707   /* special Shogi support in this size */\r
2708   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2709       for (piece = WhitePawn;\r
2710            (int) piece < (int) BlackPawn;\r
2711            piece = (ChessSquare) ((int) piece + 1)) {\r
2712         if (pieceBitmap[i][piece] != NULL)\r
2713           DeleteObject(pieceBitmap[i][piece]);\r
2714       }\r
2715     }\r
2716   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2717   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2718   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2719   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2720   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2721   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2722   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2723   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2724   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2725   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2726   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2727   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2728   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2729   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2730   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2731   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2732   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2733   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2734   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2735   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2736   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2737   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2738   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2739   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2740   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2741   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2742   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2743   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2744   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2745   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2746   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2747   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2748   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2749   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2750   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2751   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2752   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2753   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2754   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2755   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2756   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2757   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2758   minorSize = 0;\r
2759   }\r
2760 }\r
2761 \r
2762 HBITMAP\r
2763 PieceBitmap(ChessSquare p, int kind)\r
2764 {\r
2765   if ((int) p >= (int) BlackPawn)\r
2766     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2767 \r
2768   return pieceBitmap[kind][(int) p];\r
2769 }\r
2770 \r
2771 /***************************************************************/\r
2772 \r
2773 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2774 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2775 /*\r
2776 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2777 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2778 */\r
2779 \r
2780 VOID\r
2781 SquareToPos(int row, int column, int * x, int * y)\r
2782 {\r
2783   if (flipView) {\r
2784     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2785     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2786   } else {\r
2787     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2788     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2789   }\r
2790 }\r
2791 \r
2792 VOID\r
2793 DrawCoordsOnDC(HDC hdc)\r
2794 {\r
2795   static char files[] = "0123456789012345678901221098765432109876543210";\r
2796   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2797   char str[2] = { NULLCHAR, NULLCHAR };\r
2798   int oldMode, oldAlign, x, y, start, i;\r
2799   HFONT oldFont;\r
2800   HBRUSH oldBrush;\r
2801 \r
2802   if (!appData.showCoords)\r
2803     return;\r
2804 \r
2805   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2806 \r
2807   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2808   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2809   oldAlign = GetTextAlign(hdc);\r
2810   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2811 \r
2812   y = boardRect.top + lineGap;\r
2813   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2814 \r
2815   if(border) {\r
2816     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2817     x += border - lineGap - 4; y += squareSize - 6;\r
2818   } else\r
2819   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2820   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2821     str[0] = files[start + i];\r
2822     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2823     y += squareSize + lineGap;\r
2824   }\r
2825 \r
2826   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2827 \r
2828   if(border) {\r
2829     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2830     x += -border + 4; y += border - squareSize + 6;\r
2831   } else\r
2832   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2833   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2834     str[0] = ranks[start + i];\r
2835     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2836     x += squareSize + lineGap;\r
2837   }    \r
2838 \r
2839   SelectObject(hdc, oldBrush);\r
2840   SetBkMode(hdc, oldMode);\r
2841   SetTextAlign(hdc, oldAlign);\r
2842   SelectObject(hdc, oldFont);\r
2843 }\r
2844 \r
2845 VOID\r
2846 DrawGridOnDC(HDC hdc)\r
2847 {\r
2848   HPEN oldPen;\r
2849  \r
2850   if (lineGap != 0) {\r
2851     oldPen = SelectObject(hdc, gridPen);\r
2852     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2853     SelectObject(hdc, oldPen);\r
2854   }\r
2855 }\r
2856 \r
2857 #define HIGHLIGHT_PEN 0\r
2858 #define PREMOVE_PEN   1\r
2859 \r
2860 VOID\r
2861 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2862 {\r
2863   int x1, y1;\r
2864   HPEN oldPen, hPen;\r
2865   if (lineGap == 0) return;\r
2866   if (flipView) {\r
2867     x1 = boardRect.left +\r
2868       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2869     y1 = boardRect.top +\r
2870       lineGap/2 + y * (squareSize + lineGap) + border;\r
2871   } else {\r
2872     x1 = boardRect.left +\r
2873       lineGap/2 + x * (squareSize + lineGap) + border;\r
2874     y1 = boardRect.top +\r
2875       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2876   }\r
2877   hPen = pen ? premovePen : highlightPen;\r
2878   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2879   MoveToEx(hdc, x1, y1, NULL);\r
2880   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2881   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2882   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2883   LineTo(hdc, x1, y1);\r
2884   SelectObject(hdc, oldPen);\r
2885 }\r
2886 \r
2887 VOID\r
2888 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2889 {\r
2890   int i;\r
2891   for (i=0; i<2; i++) {\r
2892     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2893       DrawHighlightOnDC(hdc, TRUE,\r
2894                         h->sq[i].x, h->sq[i].y,\r
2895                         pen);\r
2896   }\r
2897 }\r
2898 \r
2899 /* Note: sqcolor is used only in monoMode */\r
2900 /* Note that this code is largely duplicated in woptions.c,\r
2901    function DrawSampleSquare, so that needs to be updated too */\r
2902 VOID\r
2903 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2904 {\r
2905   HBITMAP oldBitmap;\r
2906   HBRUSH oldBrush;\r
2907   int tmpSize;\r
2908 \r
2909   if (appData.blindfold) return;\r
2910 \r
2911   /* [AS] Use font-based pieces if needed */\r
2912   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2913     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2914     CreatePiecesFromFont();\r
2915 \r
2916     if( fontBitmapSquareSize == squareSize ) {\r
2917         int index = TranslatePieceToFontPiece(piece);\r
2918 \r
2919         SelectObject( tmphdc, hPieceMask[ index ] );\r
2920 \r
2921       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2922         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2923       else\r
2924         BitBlt( hdc,\r
2925             x, y,\r
2926             squareSize, squareSize,\r
2927             tmphdc,\r
2928             0, 0,\r
2929             SRCAND );\r
2930 \r
2931         SelectObject( tmphdc, hPieceFace[ index ] );\r
2932 \r
2933       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2934         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2935       else\r
2936         BitBlt( hdc,\r
2937             x, y,\r
2938             squareSize, squareSize,\r
2939             tmphdc,\r
2940             0, 0,\r
2941             SRCPAINT );\r
2942 \r
2943         return;\r
2944     }\r
2945   }\r
2946 \r
2947   if (appData.monoMode) {\r
2948     SelectObject(tmphdc, PieceBitmap(piece, \r
2949       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2950     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2951            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2952   } else {\r
2953     HBRUSH xBrush = whitePieceBrush;\r
2954     tmpSize = squareSize;\r
2955     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2956     if(minorSize &&\r
2957         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2958          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2959       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2960       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2961       x += (squareSize - minorSize)>>1;\r
2962       y += squareSize - minorSize - 2;\r
2963       tmpSize = minorSize;\r
2964     }\r
2965     if (color || appData.allWhite ) {\r
2966       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2967       if( color )\r
2968               oldBrush = SelectObject(hdc, xBrush);\r
2969       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2970       if(appData.upsideDown && color==flipView)\r
2971         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2972       else\r
2973         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2974       /* Use black for outline of white pieces */\r
2975       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2976       if(appData.upsideDown && color==flipView)\r
2977         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2978       else\r
2979         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2980     } else if(appData.pieceDirectory[0]) {\r
2981       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2982       oldBrush = SelectObject(hdc, xBrush);\r
2983       if(appData.upsideDown && color==flipView)\r
2984         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2985       else\r
2986         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2987       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2988       if(appData.upsideDown && color==flipView)\r
2989         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2990       else\r
2991         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2992     } else {\r
2993       /* Use square color for details of black pieces */\r
2994       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2995       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2996       if(appData.upsideDown && !flipView)\r
2997         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2998       else\r
2999         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3000     }\r
3001     SelectObject(hdc, oldBrush);\r
3002     SelectObject(tmphdc, oldBitmap);\r
3003   }\r
3004 }\r
3005 \r
3006 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3007 int GetBackTextureMode( int algo )\r
3008 {\r
3009     int result = BACK_TEXTURE_MODE_DISABLED;\r
3010 \r
3011     switch( algo ) \r
3012     {\r
3013         case BACK_TEXTURE_MODE_PLAIN:\r
3014             result = 1; /* Always use identity map */\r
3015             break;\r
3016         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3017             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3018             break;\r
3019     }\r
3020 \r
3021     return result;\r
3022 }\r
3023 \r
3024 /* \r
3025     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3026     to handle redraws cleanly (as random numbers would always be different).\r
3027 */\r
3028 VOID RebuildTextureSquareInfo()\r
3029 {\r
3030     BITMAP bi;\r
3031     int lite_w = 0;\r
3032     int lite_h = 0;\r
3033     int dark_w = 0;\r
3034     int dark_h = 0;\r
3035     int row;\r
3036     int col;\r
3037 \r
3038     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3039 \r
3040     if( liteBackTexture != NULL ) {\r
3041         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3042             lite_w = bi.bmWidth;\r
3043             lite_h = bi.bmHeight;\r
3044         }\r
3045     }\r
3046 \r
3047     if( darkBackTexture != NULL ) {\r
3048         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3049             dark_w = bi.bmWidth;\r
3050             dark_h = bi.bmHeight;\r
3051         }\r
3052     }\r
3053 \r
3054     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3055         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3056             if( (col + row) & 1 ) {\r
3057                 /* Lite square */\r
3058                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3059                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3060                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3061                   else\r
3062                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3063                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3064                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3065                   else\r
3066                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3067                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3068                 }\r
3069             }\r
3070             else {\r
3071                 /* Dark square */\r
3072                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3073                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3074                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3075                   else\r
3076                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3077                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3078                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3079                   else\r
3080                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3081                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3082                 }\r
3083             }\r
3084         }\r
3085     }\r
3086 }\r
3087 \r
3088 /* [AS] Arrow highlighting support */\r
3089 \r
3090 static double A_WIDTH = 5; /* Width of arrow body */\r
3091 \r
3092 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3093 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3094 \r
3095 static double Sqr( double x )\r
3096 {\r
3097     return x*x;\r
3098 }\r
3099 \r
3100 static int Round( double x )\r
3101 {\r
3102     return (int) (x + 0.5);\r
3103 }\r
3104 \r
3105 /* Draw an arrow between two points using current settings */\r
3106 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3107 {\r
3108     POINT arrow[7];\r
3109     double dx, dy, j, k, x, y;\r
3110 \r
3111     if( d_x == s_x ) {\r
3112         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3113 \r
3114         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3115         arrow[0].y = s_y;\r
3116 \r
3117         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3118         arrow[1].y = d_y - h;\r
3119 \r
3120         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3121         arrow[2].y = d_y - h;\r
3122 \r
3123         arrow[3].x = d_x;\r
3124         arrow[3].y = d_y;\r
3125 \r
3126         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3127         arrow[5].y = d_y - h;\r
3128 \r
3129         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3130         arrow[4].y = d_y - h;\r
3131 \r
3132         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3133         arrow[6].y = s_y;\r
3134     }\r
3135     else if( d_y == s_y ) {\r
3136         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3137 \r
3138         arrow[0].x = s_x;\r
3139         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3140 \r
3141         arrow[1].x = d_x - w;\r
3142         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3143 \r
3144         arrow[2].x = d_x - w;\r
3145         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3146 \r
3147         arrow[3].x = d_x;\r
3148         arrow[3].y = d_y;\r
3149 \r
3150         arrow[5].x = d_x - w;\r
3151         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3152 \r
3153         arrow[4].x = d_x - w;\r
3154         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3155 \r
3156         arrow[6].x = s_x;\r
3157         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3158     }\r
3159     else {\r
3160         /* [AS] Needed a lot of paper for this! :-) */\r
3161         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3162         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3163   \r
3164         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3165 \r
3166         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3167 \r
3168         x = s_x;\r
3169         y = s_y;\r
3170 \r
3171         arrow[0].x = Round(x - j);\r
3172         arrow[0].y = Round(y + j*dx);\r
3173 \r
3174         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3175         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3176 \r
3177         if( d_x > s_x ) {\r
3178             x = (double) d_x - k;\r
3179             y = (double) d_y - k*dy;\r
3180         }\r
3181         else {\r
3182             x = (double) d_x + k;\r
3183             y = (double) d_y + k*dy;\r
3184         }\r
3185 \r
3186         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3187 \r
3188         arrow[6].x = Round(x - j);\r
3189         arrow[6].y = Round(y + j*dx);\r
3190 \r
3191         arrow[2].x = Round(arrow[6].x + 2*j);\r
3192         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3193 \r
3194         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3195         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3196 \r
3197         arrow[4].x = d_x;\r
3198         arrow[4].y = d_y;\r
3199 \r
3200         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3201         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3202     }\r
3203 \r
3204     Polygon( hdc, arrow, 7 );\r
3205 }\r
3206 \r
3207 /* [AS] Draw an arrow between two squares */\r
3208 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3209 {\r
3210     int s_x, s_y, d_x, d_y;\r
3211     HPEN hpen;\r
3212     HPEN holdpen;\r
3213     HBRUSH hbrush;\r
3214     HBRUSH holdbrush;\r
3215     LOGBRUSH stLB;\r
3216 \r
3217     if( s_col == d_col && s_row == d_row ) {\r
3218         return;\r
3219     }\r
3220 \r
3221     /* Get source and destination points */\r
3222     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3223     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3224 \r
3225     if( d_y > s_y ) {\r
3226         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3227     }\r
3228     else if( d_y < s_y ) {\r
3229         d_y += squareSize / 2 + squareSize / 4;\r
3230     }\r
3231     else {\r
3232         d_y += squareSize / 2;\r
3233     }\r
3234 \r
3235     if( d_x > s_x ) {\r
3236         d_x += squareSize / 2 - squareSize / 4;\r
3237     }\r
3238     else if( d_x < s_x ) {\r
3239         d_x += squareSize / 2 + squareSize / 4;\r
3240     }\r
3241     else {\r
3242         d_x += squareSize / 2;\r
3243     }\r
3244 \r
3245     s_x += squareSize / 2;\r
3246     s_y += squareSize / 2;\r
3247 \r
3248     /* Adjust width */\r
3249     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3250 \r
3251     /* Draw */\r
3252     stLB.lbStyle = BS_SOLID;\r
3253     stLB.lbColor = appData.highlightArrowColor;\r
3254     stLB.lbHatch = 0;\r
3255 \r
3256     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3257     holdpen = SelectObject( hdc, hpen );\r
3258     hbrush = CreateBrushIndirect( &stLB );\r
3259     holdbrush = SelectObject( hdc, hbrush );\r
3260 \r
3261     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3262 \r
3263     SelectObject( hdc, holdpen );\r
3264     SelectObject( hdc, holdbrush );\r
3265     DeleteObject( hpen );\r
3266     DeleteObject( hbrush );\r
3267 }\r
3268 \r
3269 BOOL HasHighlightInfo()\r
3270 {\r
3271     BOOL result = FALSE;\r
3272 \r
3273     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3274         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3275     {\r
3276         result = TRUE;\r
3277     }\r
3278 \r
3279     return result;\r
3280 }\r
3281 \r
3282 BOOL IsDrawArrowEnabled()\r
3283 {\r
3284     BOOL result = FALSE;\r
3285 \r
3286     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3287         result = TRUE;\r
3288     }\r
3289 \r
3290     return result;\r
3291 }\r
3292 \r
3293 VOID DrawArrowHighlight( HDC hdc )\r
3294 {\r
3295     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3296         DrawArrowBetweenSquares( hdc,\r
3297             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3298             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3299     }\r
3300 }\r
3301 \r
3302 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3303 {\r
3304     HRGN result = NULL;\r
3305 \r
3306     if( HasHighlightInfo() ) {\r
3307         int x1, y1, x2, y2;\r
3308         int sx, sy, dx, dy;\r
3309 \r
3310         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3311         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3312 \r
3313         sx = MIN( x1, x2 );\r
3314         sy = MIN( y1, y2 );\r
3315         dx = MAX( x1, x2 ) + squareSize;\r
3316         dy = MAX( y1, y2 ) + squareSize;\r
3317 \r
3318         result = CreateRectRgn( sx, sy, dx, dy );\r
3319     }\r
3320 \r
3321     return result;\r
3322 }\r
3323 \r
3324 /*\r
3325     Warning: this function modifies the behavior of several other functions. \r
3326     \r
3327     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3328     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3329     repaint is scattered all over the place, which is not good for features such as\r
3330     "arrow highlighting" that require a full repaint of the board.\r
3331 \r
3332     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3333     user interaction, when speed is not so important) but especially to avoid errors\r
3334     in the displayed graphics.\r
3335 \r
3336     In such patched places, I always try refer to this function so there is a single\r
3337     place to maintain knowledge.\r
3338     \r
3339     To restore the original behavior, just return FALSE unconditionally.\r
3340 */\r
3341 BOOL IsFullRepaintPreferrable()\r
3342 {\r
3343     BOOL result = FALSE;\r
3344 \r
3345     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3346         /* Arrow may appear on the board */\r
3347         result = TRUE;\r
3348     }\r
3349 \r
3350     return result;\r
3351 }\r
3352 \r
3353 /* \r
3354     This function is called by DrawPosition to know whether a full repaint must\r
3355     be forced or not.\r
3356 \r
3357     Only DrawPosition may directly call this function, which makes use of \r
3358     some state information. Other function should call DrawPosition specifying \r
3359     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3360 */\r
3361 BOOL DrawPositionNeedsFullRepaint()\r
3362 {\r
3363     BOOL result = FALSE;\r
3364 \r
3365     /* \r
3366         Probably a slightly better policy would be to trigger a full repaint\r
3367         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3368         but animation is fast enough that it's difficult to notice.\r
3369     */\r
3370     if( animInfo.piece == EmptySquare ) {\r
3371         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3372             result = TRUE;\r
3373         }\r
3374     }\r
3375 \r
3376     return result;\r
3377 }\r
3378 \r
3379 static HBITMAP borderBitmap;\r
3380 \r
3381 VOID\r
3382 DrawBackgroundOnDC(HDC hdc)\r
3383 {\r
3384   \r
3385   BITMAP bi;\r
3386   HDC tmphdc;\r
3387   HBITMAP hbm;\r
3388   static char oldBorder[MSG_SIZ];\r
3389   int w = 600, h = 600, mode;\r
3390 \r
3391   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3392     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3393     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3394   }\r
3395   if(borderBitmap == NULL) { // loading failed, use white\r
3396     FillRect( hdc, &boardRect, whitePieceBrush );\r
3397     return;\r
3398   }\r
3399   tmphdc = CreateCompatibleDC(hdc);\r
3400   hbm = SelectObject(tmphdc, borderBitmap);\r
3401   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3402             w = bi.bmWidth;\r
3403             h = bi.bmHeight;\r
3404   }\r
3405   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3406   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3407                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3408   SetStretchBltMode(hdc, mode);\r
3409   SelectObject(tmphdc, hbm);\r
3410   DeleteDC(tmphdc);\r
3411 }\r
3412 \r
3413 VOID\r
3414 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3415 {\r
3416   int row, column, x, y, square_color, piece_color;\r
3417   ChessSquare piece;\r
3418   HBRUSH oldBrush;\r
3419   HDC texture_hdc = NULL;\r
3420 \r
3421   /* [AS] Initialize background textures if needed */\r
3422   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3423       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3424       if( backTextureSquareSize != squareSize \r
3425        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3426           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3427           backTextureSquareSize = squareSize;\r
3428           RebuildTextureSquareInfo();\r
3429       }\r
3430 \r
3431       texture_hdc = CreateCompatibleDC( hdc );\r
3432   }\r
3433 \r
3434   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3435     for (column = 0; column < BOARD_WIDTH; column++) {\r
3436   \r
3437       SquareToPos(row, column, &x, &y);\r
3438 \r
3439       piece = board[row][column];\r
3440 \r
3441       square_color = ((column + row) % 2) == 1;\r
3442       if( gameInfo.variant == VariantXiangqi ) {\r
3443           square_color = !InPalace(row, column);\r
3444           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3445           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3446       }\r
3447       piece_color = (int) piece < (int) BlackPawn;\r
3448 \r
3449 \r
3450       /* [HGM] holdings file: light square or black */\r
3451       if(column == BOARD_LEFT-2) {\r
3452             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3453                 square_color = 1;\r
3454             else {\r
3455                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3456                 continue;\r
3457             }\r
3458       } else\r
3459       if(column == BOARD_RGHT + 1 ) {\r
3460             if( row < gameInfo.holdingsSize )\r
3461                 square_color = 1;\r
3462             else {\r
3463                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3464                 continue;\r
3465             }\r
3466       }\r
3467       if(column == BOARD_LEFT-1 ) /* left align */\r
3468             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3469       else if( column == BOARD_RGHT) /* right align */\r
3470             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3471       else\r
3472       if (appData.monoMode) {\r
3473         if (piece == EmptySquare) {\r
3474           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3475                  square_color ? WHITENESS : BLACKNESS);\r
3476         } else {\r
3477           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3478         }\r
3479       } \r
3480       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3481           /* [AS] Draw the square using a texture bitmap */\r
3482           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3483           int r = row, c = column; // [HGM] do not flip board in flipView\r
3484           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3485 \r
3486           DrawTile( x, y, \r
3487               squareSize, squareSize, \r
3488               hdc, \r
3489               texture_hdc,\r
3490               backTextureSquareInfo[r][c].mode,\r
3491               backTextureSquareInfo[r][c].x,\r
3492               backTextureSquareInfo[r][c].y );\r
3493 \r
3494           SelectObject( texture_hdc, hbm );\r
3495 \r
3496           if (piece != EmptySquare) {\r
3497               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3498           }\r
3499       }\r
3500       else {\r
3501         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3502 \r
3503         oldBrush = SelectObject(hdc, brush );\r
3504         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3505         SelectObject(hdc, oldBrush);\r
3506         if (piece != EmptySquare)\r
3507           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3508       }\r
3509     }\r
3510   }\r
3511 \r
3512   if( texture_hdc != NULL ) {\r
3513     DeleteDC( texture_hdc );\r
3514   }\r
3515 }\r
3516 \r
3517 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3518 void fputDW(FILE *f, int x)\r
3519 {\r
3520         fputc(x     & 255, f);\r
3521         fputc(x>>8  & 255, f);\r
3522         fputc(x>>16 & 255, f);\r
3523         fputc(x>>24 & 255, f);\r
3524 }\r
3525 \r
3526 #define MAX_CLIPS 200   /* more than enough */\r
3527 \r
3528 VOID\r
3529 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3530 {\r
3531 //  HBITMAP bufferBitmap;\r
3532   BITMAP bi;\r
3533 //  RECT Rect;\r
3534   HDC tmphdc;\r
3535   HBITMAP hbm;\r
3536   int w = 100, h = 50;\r
3537 \r
3538   if(logo == NULL) {\r
3539     if(!logoHeight) return;\r
3540     FillRect( hdc, &logoRect, whitePieceBrush );\r
3541   }\r
3542 //  GetClientRect(hwndMain, &Rect);\r
3543 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3544 //                                      Rect.bottom-Rect.top+1);\r
3545   tmphdc = CreateCompatibleDC(hdc);\r
3546   hbm = SelectObject(tmphdc, logo);\r
3547   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3548             w = bi.bmWidth;\r
3549             h = bi.bmHeight;\r
3550   }\r
3551   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3552                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3553   SelectObject(tmphdc, hbm);\r
3554   DeleteDC(tmphdc);\r
3555 }\r
3556 \r
3557 VOID\r
3558 DisplayLogos()\r
3559 {\r
3560   if(logoHeight) {\r
3561         HDC hdc = GetDC(hwndMain);\r
3562         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3563         if(appData.autoLogo) {\r
3564           \r
3565           switch(gameMode) { // pick logos based on game mode\r
3566             case IcsObserving:\r
3567                 whiteLogo = second.programLogo; // ICS logo\r
3568                 blackLogo = second.programLogo;\r
3569             default:\r
3570                 break;\r
3571             case IcsPlayingWhite:\r
3572                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3573                 blackLogo = second.programLogo; // ICS logo\r
3574                 break;\r
3575             case IcsPlayingBlack:\r
3576                 whiteLogo = second.programLogo; // ICS logo\r
3577                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3578                 break;\r
3579             case TwoMachinesPlay:\r
3580                 if(first.twoMachinesColor[0] == 'b') {\r
3581                     whiteLogo = second.programLogo;\r
3582                     blackLogo = first.programLogo;\r
3583                 }\r
3584                 break;\r
3585             case MachinePlaysWhite:\r
3586                 blackLogo = userLogo;\r
3587                 break;\r
3588             case MachinePlaysBlack:\r
3589                 whiteLogo = userLogo;\r
3590                 blackLogo = first.programLogo;\r
3591           }\r
3592         }\r
3593         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3594         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3595         ReleaseDC(hwndMain, hdc);\r
3596   }\r
3597 }\r
3598 \r
3599 void\r
3600 UpdateLogos(int display)\r
3601 { // called after loading new engine(s), in tourney or from menu\r
3602   LoadLogo(&first, 0, FALSE);\r
3603   LoadLogo(&second, 1, appData.icsActive);\r
3604   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3605   if(display) DisplayLogos();\r
3606 }\r
3607 \r
3608 static HDC hdcSeek;\r
3609 \r
3610 // [HGM] seekgraph\r
3611 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3612 {\r
3613     POINT stPt;\r
3614     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3615     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3616     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3617     SelectObject( hdcSeek, hp );\r
3618 }\r
3619 \r
3620 // front-end wrapper for drawing functions to do rectangles\r
3621 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3622 {\r
3623     HPEN hp;\r
3624     RECT rc;\r
3625 \r
3626     if (hdcSeek == NULL) {\r
3627     hdcSeek = GetDC(hwndMain);\r
3628       if (!appData.monoMode) {\r
3629         SelectPalette(hdcSeek, hPal, FALSE);\r
3630         RealizePalette(hdcSeek);\r
3631       }\r
3632     }\r
3633     hp = SelectObject( hdcSeek, gridPen );\r
3634     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3635     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3636     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3637     SelectObject( hdcSeek, hp );\r
3638 }\r
3639 \r
3640 // front-end wrapper for putting text in graph\r
3641 void DrawSeekText(char *buf, int x, int y)\r
3642 {\r
3643         SIZE stSize;\r
3644         SetBkMode( hdcSeek, TRANSPARENT );\r
3645         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3646         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3647 }\r
3648 \r
3649 void DrawSeekDot(int x, int y, int color)\r
3650 {\r
3651         int square = color & 0x80;\r
3652         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3653                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3654         color &= 0x7F;\r
3655         if(square)\r
3656             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3657                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3658         else\r
3659             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3660                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3661             SelectObject(hdcSeek, oldBrush);\r
3662 }\r
3663 \r
3664 void DrawSeekOpen()\r
3665 {\r
3666 }\r
3667 \r
3668 void DrawSeekClose()\r
3669 {\r
3670 }\r
3671 \r
3672 VOID\r
3673 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3674 {\r
3675   static Board lastReq[2], lastDrawn[2];\r
3676   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3677   static int lastDrawnFlipView = 0;\r
3678   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3679   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3680   HDC tmphdc;\r
3681   HDC hdcmem;\r
3682   HBITMAP bufferBitmap;\r
3683   HBITMAP oldBitmap;\r
3684   RECT Rect;\r
3685   HRGN clips[MAX_CLIPS];\r
3686   ChessSquare dragged_piece = EmptySquare;\r
3687   int nr = twoBoards*partnerUp;\r
3688 \r
3689   /* I'm undecided on this - this function figures out whether a full\r
3690    * repaint is necessary on its own, so there's no real reason to have the\r
3691    * caller tell it that.  I think this can safely be set to FALSE - but\r
3692    * if we trust the callers not to request full repaints unnessesarily, then\r
3693    * we could skip some clipping work.  In other words, only request a full\r
3694    * redraw when the majority of pieces have changed positions (ie. flip, \r
3695    * gamestart and similar)  --Hawk\r
3696    */\r
3697   Boolean fullrepaint = repaint;\r
3698 \r
3699   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3700 \r
3701   if( DrawPositionNeedsFullRepaint() ) {\r
3702       fullrepaint = TRUE;\r
3703   }\r
3704 \r
3705   if (board == NULL) {\r
3706     if (!lastReqValid[nr]) {\r
3707       return;\r
3708     }\r
3709     board = lastReq[nr];\r
3710   } else {\r
3711     CopyBoard(lastReq[nr], board);\r
3712     lastReqValid[nr] = 1;\r
3713   }\r
3714 \r
3715   if (doingSizing) {\r
3716     return;\r
3717   }\r
3718 \r
3719   if (IsIconic(hwndMain)) {\r
3720     return;\r
3721   }\r
3722 \r
3723   if (hdc == NULL) {\r
3724     hdc = GetDC(hwndMain);\r
3725     if (!appData.monoMode) {\r
3726       SelectPalette(hdc, hPal, FALSE);\r
3727       RealizePalette(hdc);\r
3728     }\r
3729     releaseDC = TRUE;\r
3730   } else {\r
3731     releaseDC = FALSE;\r
3732   }\r
3733 \r
3734   /* Create some work-DCs */\r
3735   hdcmem = CreateCompatibleDC(hdc);\r
3736   tmphdc = CreateCompatibleDC(hdc);\r
3737 \r
3738   /* If dragging is in progress, we temporarely remove the piece */\r
3739   /* [HGM] or temporarily decrease count if stacked              */\r
3740   /*       !! Moved to before board compare !!                   */\r
3741   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3742     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3743     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3744             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3745         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3746     } else \r
3747     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3748             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3749         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3750     } else \r
3751         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3752   }\r
3753 \r
3754   /* Figure out which squares need updating by comparing the \r
3755    * newest board with the last drawn board and checking if\r
3756    * flipping has changed.\r
3757    */\r
3758   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3759     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3760       for (column = 0; column < BOARD_WIDTH; column++) {\r
3761         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3762           SquareToPos(row, column, &x, &y);\r
3763           clips[num_clips++] =\r
3764             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3765         }\r
3766       }\r
3767     }\r
3768    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3769     for (i=0; i<2; i++) {\r
3770       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3771           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3772         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3773             lastDrawnHighlight.sq[i].y >= 0) {\r
3774           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3775                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3776           clips[num_clips++] =\r
3777             CreateRectRgn(x - lineGap, y - lineGap, \r
3778                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3779         }\r
3780         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3781           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3782           clips[num_clips++] =\r
3783             CreateRectRgn(x - lineGap, y - lineGap, \r
3784                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3785         }\r
3786       }\r
3787     }\r
3788     for (i=0; i<2; i++) {\r
3789       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3790           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3791         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3792             lastDrawnPremove.sq[i].y >= 0) {\r
3793           SquareToPos(lastDrawnPremove.sq[i].y,\r
3794                       lastDrawnPremove.sq[i].x, &x, &y);\r
3795           clips[num_clips++] =\r
3796             CreateRectRgn(x - lineGap, y - lineGap, \r
3797                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3798         }\r
3799         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3800             premoveHighlightInfo.sq[i].y >= 0) {\r
3801           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3802                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3803           clips[num_clips++] =\r
3804             CreateRectRgn(x - lineGap, y - lineGap, \r
3805                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3806         }\r
3807       }\r
3808     }\r
3809    } else { // nr == 1\r
3810         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3811         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3812         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3813         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3814       for (i=0; i<2; i++) {\r
3815         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3816             partnerHighlightInfo.sq[i].y >= 0) {\r
3817           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3818                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3819           clips[num_clips++] =\r
3820             CreateRectRgn(x - lineGap, y - lineGap, \r
3821                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3822         }\r
3823         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3824             oldPartnerHighlight.sq[i].y >= 0) {\r
3825           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3826                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3827           clips[num_clips++] =\r
3828             CreateRectRgn(x - lineGap, y - lineGap, \r
3829                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3830         }\r
3831       }\r
3832    }\r
3833   } else {\r
3834     fullrepaint = TRUE;\r
3835   }\r
3836 \r
3837   /* Create a buffer bitmap - this is the actual bitmap\r
3838    * being written to.  When all the work is done, we can\r
3839    * copy it to the real DC (the screen).  This avoids\r
3840    * the problems with flickering.\r
3841    */\r
3842   GetClientRect(hwndMain, &Rect);\r
3843   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3844                                         Rect.bottom-Rect.top+1);\r
3845   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3846   if (!appData.monoMode) {\r
3847     SelectPalette(hdcmem, hPal, FALSE);\r
3848   }\r
3849 \r
3850   /* Create clips for dragging */\r
3851   if (!fullrepaint) {\r
3852     if (dragInfo.from.x >= 0) {\r
3853       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3854       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3855     }\r
3856     if (dragInfo.start.x >= 0) {\r
3857       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3858       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3859     }\r
3860     if (dragInfo.pos.x >= 0) {\r
3861       x = dragInfo.pos.x - squareSize / 2;\r
3862       y = dragInfo.pos.y - squareSize / 2;\r
3863       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3864     }\r
3865     if (dragInfo.lastpos.x >= 0) {\r
3866       x = dragInfo.lastpos.x - squareSize / 2;\r
3867       y = dragInfo.lastpos.y - squareSize / 2;\r
3868       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3869     }\r
3870   }\r
3871 \r
3872   /* Are we animating a move?  \r
3873    * If so, \r
3874    *   - remove the piece from the board (temporarely)\r
3875    *   - calculate the clipping region\r
3876    */\r
3877   if (!fullrepaint) {\r
3878     if (animInfo.piece != EmptySquare) {\r
3879       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3880       x = boardRect.left + animInfo.lastpos.x;\r
3881       y = boardRect.top + animInfo.lastpos.y;\r
3882       x2 = boardRect.left + animInfo.pos.x;\r
3883       y2 = boardRect.top + animInfo.pos.y;\r
3884       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3885       /* Slight kludge.  The real problem is that after AnimateMove is\r
3886          done, the position on the screen does not match lastDrawn.\r
3887          This currently causes trouble only on e.p. captures in\r
3888          atomic, where the piece moves to an empty square and then\r
3889          explodes.  The old and new positions both had an empty square\r
3890          at the destination, but animation has drawn a piece there and\r
3891          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3892       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3893     }\r
3894   }\r
3895 \r
3896   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3897   if (num_clips == 0)\r
3898     fullrepaint = TRUE;\r
3899 \r
3900   /* Set clipping on the memory DC */\r
3901   if (!fullrepaint) {\r
3902     SelectClipRgn(hdcmem, clips[0]);\r
3903     for (x = 1; x < num_clips; x++) {\r
3904       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3905         abort();  // this should never ever happen!\r
3906     }\r
3907   }\r
3908 \r
3909   /* Do all the drawing to the memory DC */\r
3910   if(explodeInfo.radius) { // [HGM] atomic\r
3911         HBRUSH oldBrush;\r
3912         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3913         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3914         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3915         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3916         x += squareSize/2;\r
3917         y += squareSize/2;\r
3918         if(!fullrepaint) {\r
3919           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3920           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3921         }\r
3922         DrawGridOnDC(hdcmem);\r
3923         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3924         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3925         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3926         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3927         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3928         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3929         SelectObject(hdcmem, oldBrush);\r
3930   } else {\r
3931     if(border) DrawBackgroundOnDC(hdcmem);\r
3932     DrawGridOnDC(hdcmem);\r
3933     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3934         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3935         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3936     } else {\r
3937         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3938         oldPartnerHighlight = partnerHighlightInfo;\r
3939     }\r
3940     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3941   }\r
3942   if(nr == 0) // [HGM] dual: markers only on left board\r
3943   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3944     for (column = 0; column < BOARD_WIDTH; column++) {\r
3945         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3946             HBRUSH oldBrush = SelectObject(hdcmem, \r
3947                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3948             SquareToPos(row, column, &x, &y);\r
3949             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3950                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3951             SelectObject(hdcmem, oldBrush);\r
3952         }\r
3953     }\r
3954   }\r
3955 \r
3956   if( appData.highlightMoveWithArrow ) {\r
3957     DrawArrowHighlight(hdcmem);\r
3958   }\r
3959 \r
3960   DrawCoordsOnDC(hdcmem);\r
3961 \r
3962   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3963                  /* to make sure lastDrawn contains what is actually drawn */\r
3964 \r
3965   /* Put the dragged piece back into place and draw it (out of place!) */\r
3966     if (dragged_piece != EmptySquare) {\r
3967     /* [HGM] or restack */\r
3968     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3969                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3970     else\r
3971     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3972                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3973     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3974     x = dragInfo.pos.x - squareSize / 2;\r
3975     y = dragInfo.pos.y - squareSize / 2;\r
3976     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3977                   ((int) dragInfo.piece < (int) BlackPawn), \r
3978                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3979   }   \r
3980   \r
3981   /* Put the animated piece back into place and draw it */\r
3982   if (animInfo.piece != EmptySquare) {\r
3983     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3984     x = boardRect.left + animInfo.pos.x;\r
3985     y = boardRect.top + animInfo.pos.y;\r
3986     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3987                   ((int) animInfo.piece < (int) BlackPawn),\r
3988                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3989   }\r
3990 \r
3991   /* Release the bufferBitmap by selecting in the old bitmap \r
3992    * and delete the memory DC\r
3993    */\r
3994   SelectObject(hdcmem, oldBitmap);\r
3995   DeleteDC(hdcmem);\r
3996 \r
3997   /* Set clipping on the target DC */\r
3998   if (!fullrepaint) {\r
3999     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4000         RECT rect;\r
4001         GetRgnBox(clips[x], &rect);\r
4002         DeleteObject(clips[x]);\r
4003         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4004                           rect.right + wpMain.width/2, rect.bottom);\r
4005     }\r
4006     SelectClipRgn(hdc, clips[0]);\r
4007     for (x = 1; x < num_clips; x++) {\r
4008       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4009         abort();   // this should never ever happen!\r
4010     } \r
4011   }\r
4012 \r
4013   /* Copy the new bitmap onto the screen in one go.\r
4014    * This way we avoid any flickering\r
4015    */\r
4016   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4017   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4018          boardRect.right - boardRect.left,\r
4019          boardRect.bottom - boardRect.top,\r
4020          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4021   if(saveDiagFlag) { \r
4022     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4023     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4024 \r
4025     GetObject(bufferBitmap, sizeof(b), &b);\r
4026     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4027         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4028         bih.biWidth = b.bmWidth;\r
4029         bih.biHeight = b.bmHeight;\r
4030         bih.biPlanes = 1;\r
4031         bih.biBitCount = b.bmBitsPixel;\r
4032         bih.biCompression = 0;\r
4033         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4034         bih.biXPelsPerMeter = 0;\r
4035         bih.biYPelsPerMeter = 0;\r
4036         bih.biClrUsed = 0;\r
4037         bih.biClrImportant = 0;\r
4038 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4039 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4040         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4041 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4042 \r
4043         wb = b.bmWidthBytes;\r
4044         // count colors\r
4045         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4046                 int k = ((int*) pData)[i];\r
4047                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4048                 if(j >= 16) break;\r
4049                 color[j] = k;\r
4050                 if(j >= nrColors) nrColors = j+1;\r
4051         }\r
4052         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4053                 INT p = 0;\r
4054                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4055                     for(w=0; w<(wb>>2); w+=2) {\r
4056                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4057                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4058                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4059                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4060                         pData[p++] = m | j<<4;\r
4061                     }\r
4062                     while(p&3) pData[p++] = 0;\r
4063                 }\r
4064                 fac = 3;\r
4065                 wb = ((wb+31)>>5)<<2;\r
4066         }\r
4067         // write BITMAPFILEHEADER\r
4068         fprintf(diagFile, "BM");\r
4069         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4070         fputDW(diagFile, 0);\r
4071         fputDW(diagFile, 0x36 + (fac?64:0));\r
4072         // write BITMAPINFOHEADER\r
4073         fputDW(diagFile, 40);\r
4074         fputDW(diagFile, b.bmWidth);\r
4075         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4076         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4077         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4078         fputDW(diagFile, 0);\r
4079         fputDW(diagFile, 0);\r
4080         fputDW(diagFile, 0);\r
4081         fputDW(diagFile, 0);\r
4082         fputDW(diagFile, 0);\r
4083         fputDW(diagFile, 0);\r
4084         // write color table\r
4085         if(fac)\r
4086         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4087         // write bitmap data\r
4088         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4089                 fputc(pData[i], diagFile);\r
4090         free(pData);\r
4091      }\r
4092   }\r
4093 \r
4094   SelectObject(tmphdc, oldBitmap);\r
4095 \r
4096   /* Massive cleanup */\r
4097   for (x = 0; x < num_clips; x++)\r
4098     DeleteObject(clips[x]);\r
4099 \r
4100   DeleteDC(tmphdc);\r
4101   DeleteObject(bufferBitmap);\r
4102 \r
4103   if (releaseDC) \r
4104     ReleaseDC(hwndMain, hdc);\r
4105   \r
4106   if (lastDrawnFlipView != flipView && nr == 0) {\r
4107     if (flipView)\r
4108       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4109     else\r
4110       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4111   }\r
4112 \r
4113 /*  CopyBoard(lastDrawn, board);*/\r
4114   lastDrawnHighlight = highlightInfo;\r
4115   lastDrawnPremove   = premoveHighlightInfo;\r
4116   lastDrawnFlipView = flipView;\r
4117   lastDrawnValid[nr] = 1;\r
4118 }\r
4119 \r
4120 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4121 int\r
4122 SaveDiagram(f)\r
4123      FILE *f;\r
4124 {\r
4125     saveDiagFlag = 1; diagFile = f;\r
4126     HDCDrawPosition(NULL, TRUE, NULL);\r
4127     saveDiagFlag = 0;\r
4128 \r
4129     fclose(f);\r
4130     return TRUE;\r
4131 }\r
4132 \r
4133 \r
4134 /*---------------------------------------------------------------------------*\\r
4135 | CLIENT PAINT PROCEDURE\r
4136 |   This is the main event-handler for the WM_PAINT message.\r
4137 |\r
4138 \*---------------------------------------------------------------------------*/\r
4139 VOID\r
4140 PaintProc(HWND hwnd)\r
4141 {\r
4142   HDC         hdc;\r
4143   PAINTSTRUCT ps;\r
4144   HFONT       oldFont;\r
4145 \r
4146   if((hdc = BeginPaint(hwnd, &ps))) {\r
4147     if (IsIconic(hwnd)) {\r
4148       DrawIcon(hdc, 2, 2, iconCurrent);\r
4149     } else {\r
4150       if (!appData.monoMode) {\r
4151         SelectPalette(hdc, hPal, FALSE);\r
4152         RealizePalette(hdc);\r
4153       }\r
4154       HDCDrawPosition(hdc, 1, NULL);\r
4155       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4156         flipView = !flipView; partnerUp = !partnerUp;\r
4157         HDCDrawPosition(hdc, 1, NULL);\r
4158         flipView = !flipView; partnerUp = !partnerUp;\r
4159       }\r
4160       oldFont =\r
4161         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4162       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4163                  ETO_CLIPPED|ETO_OPAQUE,\r
4164                  &messageRect, messageText, strlen(messageText), NULL);\r
4165       SelectObject(hdc, oldFont);\r
4166       DisplayBothClocks();\r
4167       DisplayLogos();\r
4168     }\r
4169     EndPaint(hwnd,&ps);\r
4170   }\r
4171 \r
4172   return;\r
4173 }\r
4174 \r
4175 \r
4176 /*\r
4177  * If the user selects on a border boundary, return -1; if off the board,\r
4178  *   return -2.  Otherwise map the event coordinate to the square.\r
4179  * The offset boardRect.left or boardRect.top must already have been\r
4180  *   subtracted from x.\r
4181  */\r
4182 int EventToSquare(x, limit)\r
4183      int x, limit;\r
4184 {\r
4185   if (x <= border)\r
4186     return -2;\r
4187   if (x < lineGap + border)\r
4188     return -1;\r
4189   x -= lineGap + border;\r
4190   if ((x % (squareSize + lineGap)) >= squareSize)\r
4191     return -1;\r
4192   x /= (squareSize + lineGap);\r
4193     if (x >= limit)\r
4194     return -2;\r
4195   return x;\r
4196 }\r
4197 \r
4198 typedef struct {\r
4199   char piece;\r
4200   int command;\r
4201   char* name;\r
4202 } DropEnable;\r
4203 \r
4204 DropEnable dropEnables[] = {\r
4205   { 'P', DP_Pawn, N_("Pawn") },\r
4206   { 'N', DP_Knight, N_("Knight") },\r
4207   { 'B', DP_Bishop, N_("Bishop") },\r
4208   { 'R', DP_Rook, N_("Rook") },\r
4209   { 'Q', DP_Queen, N_("Queen") },\r
4210 };\r
4211 \r
4212 VOID\r
4213 SetupDropMenu(HMENU hmenu)\r
4214 {\r
4215   int i, count, enable;\r
4216   char *p;\r
4217   extern char white_holding[], black_holding[];\r
4218   char item[MSG_SIZ];\r
4219 \r
4220   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4221     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4222                dropEnables[i].piece);\r
4223     count = 0;\r
4224     while (p && *p++ == dropEnables[i].piece) count++;\r
4225       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4226     enable = count > 0 || !appData.testLegality\r
4227       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4228                       && !appData.icsActive);\r
4229     ModifyMenu(hmenu, dropEnables[i].command,\r
4230                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4231                dropEnables[i].command, item);\r
4232   }\r
4233 }\r
4234 \r
4235 void DragPieceBegin(int x, int y, Boolean instantly)\r
4236 {\r
4237       dragInfo.lastpos.x = boardRect.left + x;\r
4238       dragInfo.lastpos.y = boardRect.top + y;\r
4239       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4240       dragInfo.from.x = fromX;\r
4241       dragInfo.from.y = fromY;\r
4242       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4243       dragInfo.start = dragInfo.from;\r
4244       SetCapture(hwndMain);\r
4245 }\r
4246 \r
4247 void DragPieceEnd(int x, int y)\r
4248 {\r
4249     ReleaseCapture();\r
4250     dragInfo.start.x = dragInfo.start.y = -1;\r
4251     dragInfo.from = dragInfo.start;\r
4252     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4253 }\r
4254 \r
4255 void ChangeDragPiece(ChessSquare piece)\r
4256 {\r
4257     dragInfo.piece = piece;\r
4258 }\r
4259 \r
4260 /* Event handler for mouse messages */\r
4261 VOID\r
4262 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4263 {\r
4264   int x, y, menuNr;\r
4265   POINT pt;\r
4266   static int recursive = 0;\r
4267   HMENU hmenu;\r
4268   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4269 \r
4270   if (recursive) {\r
4271     if (message == WM_MBUTTONUP) {\r
4272       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4273          to the middle button: we simulate pressing the left button too!\r
4274          */\r
4275       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4276       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4277     }\r
4278     return;\r
4279   }\r
4280   recursive++;\r
4281   \r
4282   pt.x = LOWORD(lParam);\r
4283   pt.y = HIWORD(lParam);\r
4284   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4285   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4286   if (!flipView && y >= 0) {\r
4287     y = BOARD_HEIGHT - 1 - y;\r
4288   }\r
4289   if (flipView && x >= 0) {\r
4290     x = BOARD_WIDTH - 1 - x;\r
4291   }\r
4292 \r
4293   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4294   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4295 \r
4296   switch (message) {\r
4297   case WM_LBUTTONDOWN:\r
4298       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4299         ClockClick(flipClock); break;\r
4300       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4301         ClockClick(!flipClock); break;\r
4302       }\r
4303       dragInfo.start.x = dragInfo.start.y = -1;\r
4304       dragInfo.from = dragInfo.start;\r
4305     if(fromX == -1 && frozen) { // not sure where this is for\r
4306                 fromX = fromY = -1; \r
4307       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4308       break;\r
4309     }\r
4310       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4311       DrawPosition(TRUE, NULL);\r
4312     break;\r
4313 \r
4314   case WM_LBUTTONUP:\r
4315       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4316       DrawPosition(TRUE, NULL);\r
4317     break;\r
4318 \r
4319   case WM_MOUSEMOVE:\r
4320     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4321     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4322     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4323     if ((appData.animateDragging || appData.highlightDragging)\r
4324         && (wParam & MK_LBUTTON)\r
4325         && dragInfo.from.x >= 0) \r
4326     {\r
4327       BOOL full_repaint = FALSE;\r
4328 \r
4329       if (appData.animateDragging) {\r
4330         dragInfo.pos = pt;\r
4331       }\r
4332       if (appData.highlightDragging) {\r
4333         SetHighlights(fromX, fromY, x, y);\r
4334         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4335             full_repaint = TRUE;\r
4336         }\r
4337       }\r
4338       \r
4339       DrawPosition( full_repaint, NULL);\r
4340       \r
4341       dragInfo.lastpos = dragInfo.pos;\r
4342     }\r
4343     break;\r
4344 \r
4345   case WM_MOUSEWHEEL: // [DM]\r
4346     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4347        /* Mouse Wheel is being rolled forward\r
4348         * Play moves forward\r
4349         */\r
4350        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4351                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4352        /* Mouse Wheel is being rolled backward\r
4353         * Play moves backward\r
4354         */\r
4355        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4356                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4357     }\r
4358     break;\r
4359 \r
4360   case WM_MBUTTONUP:\r
4361   case WM_RBUTTONUP:\r
4362     ReleaseCapture();\r
4363     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4364     break;\r
4365  \r
4366   case WM_MBUTTONDOWN:\r
4367   case WM_RBUTTONDOWN:\r
4368     ErrorPopDown();\r
4369     ReleaseCapture();\r
4370     fromX = fromY = -1;\r
4371     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4372     dragInfo.start.x = dragInfo.start.y = -1;\r
4373     dragInfo.from = dragInfo.start;\r
4374     dragInfo.lastpos = dragInfo.pos;\r
4375     if (appData.highlightDragging) {\r
4376       ClearHighlights();\r
4377     }\r
4378     if(y == -2) {\r
4379       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4380       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4381           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4382       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4383           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4384       }\r
4385       break;\r
4386     }\r
4387     DrawPosition(TRUE, NULL);\r
4388 \r
4389     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4390     switch (menuNr) {\r
4391     case 0:\r
4392       if (message == WM_MBUTTONDOWN) {\r
4393         buttonCount = 3;  /* even if system didn't think so */\r
4394         if (wParam & MK_SHIFT) \r
4395           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4396         else\r
4397           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4398       } else { /* message == WM_RBUTTONDOWN */\r
4399         /* Just have one menu, on the right button.  Windows users don't\r
4400            think to try the middle one, and sometimes other software steals\r
4401            it, or it doesn't really exist. */\r
4402         if(gameInfo.variant != VariantShogi)\r
4403             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4404         else\r
4405             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4406       }\r
4407       break;\r
4408     case 2:\r
4409       SetCapture(hwndMain);\r
4410       break;\r
4411     case 1:\r
4412       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4413       SetupDropMenu(hmenu);\r
4414       MenuPopup(hwnd, pt, hmenu, -1);\r
4415     default:\r
4416       break;\r
4417     }\r
4418     break;\r
4419   }\r
4420 \r
4421   recursive--;\r
4422 }\r
4423 \r
4424 /* Preprocess messages for buttons in main window */\r
4425 LRESULT CALLBACK\r
4426 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4427 {\r
4428   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4429   int i, dir;\r
4430 \r
4431   for (i=0; i<N_BUTTONS; i++) {\r
4432     if (buttonDesc[i].id == id) break;\r
4433   }\r
4434   if (i == N_BUTTONS) return 0;\r
4435   switch (message) {\r
4436   case WM_KEYDOWN:\r
4437     switch (wParam) {\r
4438     case VK_LEFT:\r
4439     case VK_RIGHT:\r
4440       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4441       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4442       return TRUE;\r
4443     }\r
4444     break;\r
4445   case WM_CHAR:\r
4446     switch (wParam) {\r
4447     case '\r':\r
4448       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4449       return TRUE;\r
4450     default:\r
4451       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4452         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4453         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4454         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4455         SetFocus(h);\r
4456         SendMessage(h, WM_CHAR, wParam, lParam);\r
4457         return TRUE;\r
4458       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4459         TypeInEvent((char)wParam);\r
4460       }\r
4461       break;\r
4462     }\r
4463     break;\r
4464   }\r
4465   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4466 }\r
4467 \r
4468 /* Process messages for Promotion dialog box */\r
4469 LRESULT CALLBACK\r
4470 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4471 {\r
4472   char promoChar;\r
4473 \r
4474   switch (message) {\r
4475   case WM_INITDIALOG: /* message: initialize dialog box */\r
4476     /* Center the dialog over the application window */\r
4477     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4478     Translate(hDlg, DLG_PromotionKing);\r
4479     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4480       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4481        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4482        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4483                SW_SHOW : SW_HIDE);\r
4484     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4485     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4486        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4487          PieceToChar(WhiteAngel) != '~') ||\r
4488         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4489          PieceToChar(BlackAngel) != '~')   ) ?\r
4490                SW_SHOW : SW_HIDE);\r
4491     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4492        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4493          PieceToChar(WhiteMarshall) != '~') ||\r
4494         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4495          PieceToChar(BlackMarshall) != '~')   ) ?\r
4496                SW_SHOW : SW_HIDE);\r
4497     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4498     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4499        gameInfo.variant != VariantShogi ?\r
4500                SW_SHOW : SW_HIDE);\r
4501     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4502        gameInfo.variant != VariantShogi ?\r
4503                SW_SHOW : SW_HIDE);\r
4504     if(gameInfo.variant == VariantShogi) {\r
4505         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4506         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4507         SetWindowText(hDlg, "Promote?");\r
4508     }\r
4509     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4510        gameInfo.variant == VariantSuper ?\r
4511                SW_SHOW : SW_HIDE);\r
4512     return TRUE;\r
4513 \r
4514   case WM_COMMAND: /* message: received a command */\r
4515     switch (LOWORD(wParam)) {\r
4516     case IDCANCEL:\r
4517       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4518       ClearHighlights();\r
4519       DrawPosition(FALSE, NULL);\r
4520       return TRUE;\r
4521     case PB_King:\r
4522       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4523       break;\r
4524     case PB_Queen:\r
4525       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4526       break;\r
4527     case PB_Rook:\r
4528       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4529       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4530       break;\r
4531     case PB_Bishop:\r
4532       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4533       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4534       break;\r
4535     case PB_Chancellor:\r
4536       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4537       break;\r
4538     case PB_Archbishop:\r
4539       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4540       break;\r
4541     case PB_Knight:\r
4542       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4543       break;\r
4544     default:\r
4545       return FALSE;\r
4546     }\r
4547     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4548     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4549     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4550     fromX = fromY = -1;\r
4551     if (!appData.highlightLastMove) {\r
4552       ClearHighlights();\r
4553       DrawPosition(FALSE, NULL);\r
4554     }\r
4555     return TRUE;\r
4556   }\r
4557   return FALSE;\r
4558 }\r
4559 \r
4560 /* Pop up promotion dialog */\r
4561 VOID\r
4562 PromotionPopup(HWND hwnd)\r
4563 {\r
4564   FARPROC lpProc;\r
4565 \r
4566   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4567   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4568     hwnd, (DLGPROC)lpProc);\r
4569   FreeProcInstance(lpProc);\r
4570 }\r
4571 \r
4572 void\r
4573 PromotionPopUp()\r
4574 {\r
4575   DrawPosition(TRUE, NULL);\r
4576   PromotionPopup(hwndMain);\r
4577 }\r
4578 \r
4579 VOID\r
4580 LoadGameDialog(HWND hwnd, char* title)\r
4581 {\r
4582   UINT number = 0;\r
4583   FILE *f;\r
4584   char fileTitle[MSG_SIZ];\r
4585   f = OpenFileDialog(hwnd, "rb", "",\r
4586                      appData.oldSaveStyle ? "gam" : "pgn",\r
4587                      GAME_FILT,\r
4588                      title, &number, fileTitle, NULL);\r
4589   if (f != NULL) {\r
4590     cmailMsgLoaded = FALSE;\r
4591     if (number == 0) {\r
4592       int error = GameListBuild(f);\r
4593       if (error) {\r
4594         DisplayError(_("Cannot build game list"), error);\r
4595       } else if (!ListEmpty(&gameList) &&\r
4596                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4597         GameListPopUp(f, fileTitle);\r
4598         return;\r
4599       }\r
4600       GameListDestroy();\r
4601       number = 1;\r
4602     }\r
4603     LoadGame(f, number, fileTitle, FALSE);\r
4604   }\r
4605 }\r
4606 \r
4607 int get_term_width()\r
4608 {\r
4609     HDC hdc;\r
4610     TEXTMETRIC tm;\r
4611     RECT rc;\r
4612     HFONT hfont, hold_font;\r
4613     LOGFONT lf;\r
4614     HWND hText;\r
4615 \r
4616     if (hwndConsole)\r
4617         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4618     else\r
4619         return 79;\r
4620 \r
4621     // get the text metrics\r
4622     hdc = GetDC(hText);\r
4623     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4624     if (consoleCF.dwEffects & CFE_BOLD)\r
4625         lf.lfWeight = FW_BOLD;\r
4626     if (consoleCF.dwEffects & CFE_ITALIC)\r
4627         lf.lfItalic = TRUE;\r
4628     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4629         lf.lfStrikeOut = TRUE;\r
4630     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4631         lf.lfUnderline = TRUE;\r
4632     hfont = CreateFontIndirect(&lf);\r
4633     hold_font = SelectObject(hdc, hfont);\r
4634     GetTextMetrics(hdc, &tm);\r
4635     SelectObject(hdc, hold_font);\r
4636     DeleteObject(hfont);\r
4637     ReleaseDC(hText, hdc);\r
4638 \r
4639     // get the rectangle\r
4640     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4641 \r
4642     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4643 }\r
4644 \r
4645 void UpdateICSWidth(HWND hText)\r
4646 {\r
4647     LONG old_width, new_width;\r
4648 \r
4649     new_width = get_term_width(hText, FALSE);\r
4650     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4651     if (new_width != old_width)\r
4652     {\r
4653         ics_update_width(new_width);\r
4654         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4655     }\r
4656 }\r
4657 \r
4658 VOID\r
4659 ChangedConsoleFont()\r
4660 {\r
4661   CHARFORMAT cfmt;\r
4662   CHARRANGE tmpsel, sel;\r
4663   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4664   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4665   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4666   PARAFORMAT paraf;\r
4667 \r
4668   cfmt.cbSize = sizeof(CHARFORMAT);\r
4669   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4670     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4671                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4672   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4673    * size.  This was undocumented in the version of MSVC++ that I had\r
4674    * when I wrote the code, but is apparently documented now.\r
4675    */\r
4676   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4677   cfmt.bCharSet = f->lf.lfCharSet;\r
4678   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4679   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4680   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4681   /* Why are the following seemingly needed too? */\r
4682   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4683   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4684   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4685   tmpsel.cpMin = 0;\r
4686   tmpsel.cpMax = -1; /*999999?*/\r
4687   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4688   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4689   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4690    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4691    */\r
4692   paraf.cbSize = sizeof(paraf);\r
4693   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4694   paraf.dxStartIndent = 0;\r
4695   paraf.dxOffset = WRAP_INDENT;\r
4696   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4697   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4698   UpdateICSWidth(hText);\r
4699 }\r
4700 \r
4701 /*---------------------------------------------------------------------------*\\r
4702  *\r
4703  * Window Proc for main window\r
4704  *\r
4705 \*---------------------------------------------------------------------------*/\r
4706 \r
4707 /* Process messages for main window, etc. */\r
4708 LRESULT CALLBACK\r
4709 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4710 {\r
4711   FARPROC lpProc;\r
4712   int wmId, wmEvent;\r
4713   char *defName;\r
4714   FILE *f;\r
4715   UINT number;\r
4716   char fileTitle[MSG_SIZ];\r
4717   static SnapData sd;\r
4718   static int peek=0;\r
4719 \r
4720   switch (message) {\r
4721 \r
4722   case WM_PAINT: /* message: repaint portion of window */\r
4723     PaintProc(hwnd);\r
4724     break;\r
4725 \r
4726   case WM_ERASEBKGND:\r
4727     if (IsIconic(hwnd)) {\r
4728       /* Cheat; change the message */\r
4729       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4730     } else {\r
4731       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4732     }\r
4733     break;\r
4734 \r
4735   case WM_LBUTTONDOWN:\r
4736   case WM_MBUTTONDOWN:\r
4737   case WM_RBUTTONDOWN:\r
4738   case WM_LBUTTONUP:\r
4739   case WM_MBUTTONUP:\r
4740   case WM_RBUTTONUP:\r
4741   case WM_MOUSEMOVE:\r
4742   case WM_MOUSEWHEEL:\r
4743     MouseEvent(hwnd, message, wParam, lParam);\r
4744     break;\r
4745 \r
4746   case WM_KEYUP:\r
4747     if((char)wParam == '\b') {\r
4748       ForwardEvent(); peek = 0;\r
4749     }\r
4750 \r
4751     JAWS_KBUP_NAVIGATION\r
4752 \r
4753     break;\r
4754 \r
4755   case WM_KEYDOWN:\r
4756     if((char)wParam == '\b') {\r
4757       if(!peek) BackwardEvent(), peek = 1;\r
4758     }\r
4759 \r
4760     JAWS_KBDOWN_NAVIGATION\r
4761 \r
4762     break;\r
4763 \r
4764   case WM_CHAR:\r
4765     \r
4766     JAWS_ALT_INTERCEPT\r
4767 \r
4768     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4769         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4770         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4771         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4772         SetFocus(h);\r
4773         SendMessage(h, message, wParam, lParam);\r
4774     } else if(lParam != KF_REPEAT) {\r
4775         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4776                 TypeInEvent((char)wParam);\r
4777         } else if((char)wParam == 003) CopyGameToClipboard();\r
4778          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4779     }\r
4780 \r
4781     break;\r
4782 \r
4783   case WM_PALETTECHANGED:\r
4784     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4785       int nnew;\r
4786       HDC hdc = GetDC(hwndMain);\r
4787       SelectPalette(hdc, hPal, TRUE);\r
4788       nnew = RealizePalette(hdc);\r
4789       if (nnew > 0) {\r
4790         paletteChanged = TRUE;\r
4791         InvalidateRect(hwnd, &boardRect, FALSE);\r
4792       }\r
4793       ReleaseDC(hwnd, hdc);\r
4794     }\r
4795     break;\r
4796 \r
4797   case WM_QUERYNEWPALETTE:\r
4798     if (!appData.monoMode /*&& paletteChanged*/) {\r
4799       int nnew;\r
4800       HDC hdc = GetDC(hwndMain);\r
4801       paletteChanged = FALSE;\r
4802       SelectPalette(hdc, hPal, FALSE);\r
4803       nnew = RealizePalette(hdc);\r
4804       if (nnew > 0) {\r
4805         InvalidateRect(hwnd, &boardRect, FALSE);\r
4806       }\r
4807       ReleaseDC(hwnd, hdc);\r
4808       return TRUE;\r
4809     }\r
4810     return FALSE;\r
4811 \r
4812   case WM_COMMAND: /* message: command from application menu */\r
4813     wmId    = LOWORD(wParam);\r
4814     wmEvent = HIWORD(wParam);\r
4815 \r
4816     switch (wmId) {\r
4817     case IDM_NewGame:\r
4818       ResetGameEvent();\r
4819       SAY("new game enter a move to play against the computer with white");\r
4820       break;\r
4821 \r
4822     case IDM_NewGameFRC:\r
4823       if( NewGameFRC() == 0 ) {\r
4824         ResetGameEvent();\r
4825       }\r
4826       break;\r
4827 \r
4828     case IDM_NewVariant:\r
4829       NewVariantPopup(hwnd);\r
4830       break;\r
4831 \r
4832     case IDM_LoadGame:\r
4833       LoadGameDialog(hwnd, _("Load Game from File"));\r
4834       break;\r
4835 \r
4836     case IDM_LoadNextGame:\r
4837       ReloadGame(1);\r
4838       break;\r
4839 \r
4840     case IDM_LoadPrevGame:\r
4841       ReloadGame(-1);\r
4842       break;\r
4843 \r
4844     case IDM_ReloadGame:\r
4845       ReloadGame(0);\r
4846       break;\r
4847 \r
4848     case IDM_LoadPosition:\r
4849       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4850         Reset(FALSE, TRUE);\r
4851       }\r
4852       number = 1;\r
4853       f = OpenFileDialog(hwnd, "rb", "",\r
4854                          appData.oldSaveStyle ? "pos" : "fen",\r
4855                          POSITION_FILT,\r
4856                          _("Load Position from File"), &number, fileTitle, NULL);\r
4857       if (f != NULL) {\r
4858         LoadPosition(f, number, fileTitle);\r
4859       }\r
4860       break;\r
4861 \r
4862     case IDM_LoadNextPosition:\r
4863       ReloadPosition(1);\r
4864       break;\r
4865 \r
4866     case IDM_LoadPrevPosition:\r
4867       ReloadPosition(-1);\r
4868       break;\r
4869 \r
4870     case IDM_ReloadPosition:\r
4871       ReloadPosition(0);\r
4872       break;\r
4873 \r
4874     case IDM_SaveGame:\r
4875       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4876       f = OpenFileDialog(hwnd, "a", defName,\r
4877                          appData.oldSaveStyle ? "gam" : "pgn",\r
4878                          GAME_FILT,\r
4879                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4880       if (f != NULL) {\r
4881         SaveGame(f, 0, "");\r
4882       }\r
4883       break;\r
4884 \r
4885     case IDM_SavePosition:\r
4886       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4887       f = OpenFileDialog(hwnd, "a", defName,\r
4888                          appData.oldSaveStyle ? "pos" : "fen",\r
4889                          POSITION_FILT,\r
4890                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4891       if (f != NULL) {\r
4892         SavePosition(f, 0, "");\r
4893       }\r
4894       break;\r
4895 \r
4896     case IDM_SaveDiagram:\r
4897       defName = "diagram";\r
4898       f = OpenFileDialog(hwnd, "wb", defName,\r
4899                          "bmp",\r
4900                          DIAGRAM_FILT,\r
4901                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4902       if (f != NULL) {\r
4903         SaveDiagram(f);\r
4904       }\r
4905       break;\r
4906 \r
4907     case IDM_CreateBook:\r
4908       CreateBookEvent();\r
4909       break;\r
4910 \r
4911     case IDM_CopyGame:\r
4912       CopyGameToClipboard();\r
4913       break;\r
4914 \r
4915     case IDM_PasteGame:\r
4916       PasteGameFromClipboard();\r
4917       break;\r
4918 \r
4919     case IDM_CopyGameListToClipboard:\r
4920       CopyGameListToClipboard();\r
4921       break;\r
4922 \r
4923     /* [AS] Autodetect FEN or PGN data */\r
4924     case IDM_PasteAny:\r
4925       PasteGameOrFENFromClipboard();\r
4926       break;\r
4927 \r
4928     /* [AS] Move history */\r
4929     case IDM_ShowMoveHistory:\r
4930         if( MoveHistoryIsUp() ) {\r
4931             MoveHistoryPopDown();\r
4932         }\r
4933         else {\r
4934             MoveHistoryPopUp();\r
4935         }\r
4936         break;\r
4937 \r
4938     /* [AS] Eval graph */\r
4939     case IDM_ShowEvalGraph:\r
4940         if( EvalGraphIsUp() ) {\r
4941             EvalGraphPopDown();\r
4942         }\r
4943         else {\r
4944             EvalGraphPopUp();\r
4945             SetFocus(hwndMain);\r
4946         }\r
4947         break;\r
4948 \r
4949     /* [AS] Engine output */\r
4950     case IDM_ShowEngineOutput:\r
4951         if( EngineOutputIsUp() ) {\r
4952             EngineOutputPopDown();\r
4953         }\r
4954         else {\r
4955             EngineOutputPopUp();\r
4956         }\r
4957         break;\r
4958 \r
4959     /* [AS] User adjudication */\r
4960     case IDM_UserAdjudication_White:\r
4961         UserAdjudicationEvent( +1 );\r
4962         break;\r
4963 \r
4964     case IDM_UserAdjudication_Black:\r
4965         UserAdjudicationEvent( -1 );\r
4966         break;\r
4967 \r
4968     case IDM_UserAdjudication_Draw:\r
4969         UserAdjudicationEvent( 0 );\r
4970         break;\r
4971 \r
4972     /* [AS] Game list options dialog */\r
4973     case IDM_GameListOptions:\r
4974       GameListOptions();\r
4975       break;\r
4976 \r
4977     case IDM_NewChat:\r
4978       ChatPopUp(NULL);\r
4979       break;\r
4980 \r
4981     case IDM_CopyPosition:\r
4982       CopyFENToClipboard();\r
4983       break;\r
4984 \r
4985     case IDM_PastePosition:\r
4986       PasteFENFromClipboard();\r
4987       break;\r
4988 \r
4989     case IDM_MailMove:\r
4990       MailMoveEvent();\r
4991       break;\r
4992 \r
4993     case IDM_ReloadCMailMsg:\r
4994       Reset(TRUE, TRUE);\r
4995       ReloadCmailMsgEvent(FALSE);\r
4996       break;\r
4997 \r
4998     case IDM_Minimize:\r
4999       ShowWindow(hwnd, SW_MINIMIZE);\r
5000       break;\r
5001 \r
5002     case IDM_Exit:\r
5003       ExitEvent(0);\r
5004       break;\r
5005 \r
5006     case IDM_MachineWhite:\r
5007       MachineWhiteEvent();\r
5008       /*\r
5009        * refresh the tags dialog only if it's visible\r
5010        */\r
5011       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5012           char *tags;\r
5013           tags = PGNTags(&gameInfo);\r
5014           TagsPopUp(tags, CmailMsg());\r
5015           free(tags);\r
5016       }\r
5017       SAY("computer starts playing white");\r
5018       break;\r
5019 \r
5020     case IDM_MachineBlack:\r
5021       MachineBlackEvent();\r
5022       /*\r
5023        * refresh the tags dialog only if it's visible\r
5024        */\r
5025       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5026           char *tags;\r
5027           tags = PGNTags(&gameInfo);\r
5028           TagsPopUp(tags, CmailMsg());\r
5029           free(tags);\r
5030       }\r
5031       SAY("computer starts playing black");\r
5032       break;\r
5033 \r
5034     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5035       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5036       break;\r
5037 \r
5038     case IDM_TwoMachines:\r
5039       TwoMachinesEvent();\r
5040       /*\r
5041        * refresh the tags dialog only if it's visible\r
5042        */\r
5043       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5044           char *tags;\r
5045           tags = PGNTags(&gameInfo);\r
5046           TagsPopUp(tags, CmailMsg());\r
5047           free(tags);\r
5048       }\r
5049       SAY("computer starts playing both sides");\r
5050       break;\r
5051 \r
5052     case IDM_AnalysisMode:\r
5053       if(AnalyzeModeEvent()) {\r
5054         SAY("analyzing current position");\r
5055       }\r
5056       break;\r
5057 \r
5058     case IDM_AnalyzeFile:\r
5059       AnalyzeFileEvent();\r
5060       break;\r
5061 \r
5062     case IDM_IcsClient:\r
5063       IcsClientEvent();\r
5064       break;\r
5065 \r
5066     case IDM_EditGame:\r
5067     case IDM_EditGame2:\r
5068       EditGameEvent();\r
5069       SAY("edit game");\r
5070       break;\r
5071 \r
5072     case IDM_EditPosition:\r
5073     case IDM_EditPosition2:\r
5074       EditPositionEvent();\r
5075       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5076       break;\r
5077 \r
5078     case IDM_Training:\r
5079       TrainingEvent();\r
5080       break;\r
5081 \r
5082     case IDM_ShowGameList:\r
5083       ShowGameListProc();\r
5084       break;\r
5085 \r
5086     case IDM_EditProgs1:\r
5087       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5088       break;\r
5089 \r
5090     case IDM_LoadProg1:\r
5091      LoadEnginePopUp(hwndMain, 0);\r
5092       break;\r
5093 \r
5094     case IDM_LoadProg2:\r
5095      LoadEnginePopUp(hwndMain, 1);\r
5096       break;\r
5097 \r
5098     case IDM_EditServers:\r
5099       EditTagsPopUp(icsNames, &icsNames);\r
5100       break;\r
5101 \r
5102     case IDM_EditTags:\r
5103     case IDM_Tags:\r
5104       EditTagsProc();\r
5105       break;\r
5106 \r
5107     case IDM_EditBook:\r
5108       EditBookEvent();\r
5109       break;\r
5110 \r
5111     case IDM_EditComment:\r
5112     case IDM_Comment:\r
5113       if (commentUp && editComment) {\r
5114         CommentPopDown();\r
5115       } else {\r
5116         EditCommentEvent();\r
5117       }\r
5118       break;\r
5119 \r
5120     case IDM_Pause:\r
5121       PauseEvent();\r
5122       break;\r
5123 \r
5124     case IDM_Accept:\r
5125       AcceptEvent();\r
5126       break;\r
5127 \r
5128     case IDM_Decline:\r
5129       DeclineEvent();\r
5130       break;\r
5131 \r
5132     case IDM_Rematch:\r
5133       RematchEvent();\r
5134       break;\r
5135 \r
5136     case IDM_CallFlag:\r
5137       CallFlagEvent();\r
5138       break;\r
5139 \r
5140     case IDM_Draw:\r
5141       DrawEvent();\r
5142       break;\r
5143 \r
5144     case IDM_Adjourn:\r
5145       AdjournEvent();\r
5146       break;\r
5147 \r
5148     case IDM_Abort:\r
5149       AbortEvent();\r
5150       break;\r
5151 \r
5152     case IDM_Resign:\r
5153       ResignEvent();\r
5154       break;\r
5155 \r
5156     case IDM_StopObserving:\r
5157       StopObservingEvent();\r
5158       break;\r
5159 \r
5160     case IDM_StopExamining:\r
5161       StopExaminingEvent();\r
5162       break;\r
5163 \r
5164     case IDM_Upload:\r
5165       UploadGameEvent();\r
5166       break;\r
5167 \r
5168     case IDM_TypeInMove:\r
5169       TypeInEvent('\000');\r
5170       break;\r
5171 \r
5172     case IDM_TypeInName:\r
5173       PopUpNameDialog('\000');\r
5174       break;\r
5175 \r
5176     case IDM_Backward:\r
5177       BackwardEvent();\r
5178       SetFocus(hwndMain);\r
5179       break;\r
5180 \r
5181     JAWS_MENU_ITEMS\r
5182 \r
5183     case IDM_Forward:\r
5184       ForwardEvent();\r
5185       SetFocus(hwndMain);\r
5186       break;\r
5187 \r
5188     case IDM_ToStart:\r
5189       ToStartEvent();\r
5190       SetFocus(hwndMain);\r
5191       break;\r
5192 \r
5193     case IDM_ToEnd:\r
5194       ToEndEvent();\r
5195       SetFocus(hwndMain);\r
5196       break;\r
5197 \r
5198     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5199     case OPT_GameListPrev:\r
5200       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5201       break;\r
5202 \r
5203     case IDM_Revert:\r
5204       RevertEvent(FALSE);\r
5205       break;\r
5206 \r
5207     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5208       RevertEvent(TRUE);\r
5209       break;\r
5210 \r
5211     case IDM_TruncateGame:\r
5212       TruncateGameEvent();\r
5213       break;\r
5214 \r
5215     case IDM_MoveNow:\r
5216       MoveNowEvent();\r
5217       break;\r
5218 \r
5219     case IDM_RetractMove:\r
5220       RetractMoveEvent();\r
5221       break;\r
5222 \r
5223     case IDM_FlipView:\r
5224       flipView = !flipView;\r
5225       DrawPosition(FALSE, NULL);\r
5226       break;\r
5227 \r
5228     case IDM_FlipClock:\r
5229       flipClock = !flipClock;\r
5230       DisplayBothClocks();\r
5231       DisplayLogos();\r
5232       break;\r
5233 \r
5234     case IDM_MuteSounds:\r
5235       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5236       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5237                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5238       break;\r
5239 \r
5240     case IDM_GeneralOptions:\r
5241       GeneralOptionsPopup(hwnd);\r
5242       DrawPosition(TRUE, NULL);\r
5243       break;\r
5244 \r
5245     case IDM_BoardOptions:\r
5246       BoardOptionsPopup(hwnd);\r
5247       break;\r
5248 \r
5249     case IDM_ThemeOptions:\r
5250       ThemeOptionsPopup(hwnd);\r
5251       break;\r
5252 \r
5253     case IDM_EnginePlayOptions:\r
5254       EnginePlayOptionsPopup(hwnd);\r
5255       break;\r
5256 \r
5257     case IDM_Engine1Options:\r
5258       EngineOptionsPopup(hwnd, &first);\r
5259       break;\r
5260 \r
5261     case IDM_Engine2Options:\r
5262       savedHwnd = hwnd;\r
5263       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5264       EngineOptionsPopup(hwnd, &second);\r
5265       break;\r
5266 \r
5267     case IDM_OptionsUCI:\r
5268       UciOptionsPopup(hwnd);\r
5269       break;\r
5270 \r
5271     case IDM_Tourney:\r
5272       TourneyPopup(hwnd);\r
5273       break;\r
5274 \r
5275     case IDM_IcsOptions:\r
5276       IcsOptionsPopup(hwnd);\r
5277       break;\r
5278 \r
5279     case IDM_Fonts:\r
5280       FontsOptionsPopup(hwnd);\r
5281       break;\r
5282 \r
5283     case IDM_Sounds:\r
5284       SoundOptionsPopup(hwnd);\r
5285       break;\r
5286 \r
5287     case IDM_CommPort:\r
5288       CommPortOptionsPopup(hwnd);\r
5289       break;\r
5290 \r
5291     case IDM_LoadOptions:\r
5292       LoadOptionsPopup(hwnd);\r
5293       break;\r
5294 \r
5295     case IDM_SaveOptions:\r
5296       SaveOptionsPopup(hwnd);\r
5297       break;\r
5298 \r
5299     case IDM_TimeControl:\r
5300       TimeControlOptionsPopup(hwnd);\r
5301       break;\r
5302 \r
5303     case IDM_SaveSettings:\r
5304       SaveSettings(settingsFileName);\r
5305       break;\r
5306 \r
5307     case IDM_SaveSettingsOnExit:\r
5308       saveSettingsOnExit = !saveSettingsOnExit;\r
5309       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5310                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5311                                          MF_CHECKED : MF_UNCHECKED));\r
5312       break;\r
5313 \r
5314     case IDM_Hint:\r
5315       HintEvent();\r
5316       break;\r
5317 \r
5318     case IDM_Book:\r
5319       BookEvent();\r
5320       break;\r
5321 \r
5322     case IDM_AboutGame:\r
5323       AboutGameEvent();\r
5324       break;\r
5325 \r
5326     case IDM_Debug:\r
5327       appData.debugMode = !appData.debugMode;\r
5328       if (appData.debugMode) {\r
5329         char dir[MSG_SIZ];\r
5330         GetCurrentDirectory(MSG_SIZ, dir);\r
5331         SetCurrentDirectory(installDir);\r
5332         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5333         SetCurrentDirectory(dir);\r
5334         setbuf(debugFP, NULL);\r
5335       } else {\r
5336         fclose(debugFP);\r
5337         debugFP = NULL;\r
5338       }\r
5339       break;\r
5340 \r
5341     case IDM_HELPCONTENTS:\r
5342       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5343           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5344           MessageBox (GetFocus(),\r
5345                     _("Unable to activate help"),\r
5346                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5347       }\r
5348       break;\r
5349 \r
5350     case IDM_HELPSEARCH:\r
5351         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5352             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5353         MessageBox (GetFocus(),\r
5354                     _("Unable to activate help"),\r
5355                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5356       }\r
5357       break;\r
5358 \r
5359     case IDM_HELPHELP:\r
5360       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5361         MessageBox (GetFocus(),\r
5362                     _("Unable to activate help"),\r
5363                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5364       }\r
5365       break;\r
5366 \r
5367     case IDM_ABOUT:\r
5368       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5369       DialogBox(hInst, \r
5370         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5371         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5372       FreeProcInstance(lpProc);\r
5373       break;\r
5374 \r
5375     case IDM_DirectCommand1:\r
5376       AskQuestionEvent(_("Direct Command"),\r
5377                        _("Send to chess program:"), "", "1");\r
5378       break;\r
5379     case IDM_DirectCommand2:\r
5380       AskQuestionEvent(_("Direct Command"),\r
5381                        _("Send to second chess program:"), "", "2");\r
5382       break;\r
5383 \r
5384     case EP_WhitePawn:\r
5385       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5386       fromX = fromY = -1;\r
5387       break;\r
5388 \r
5389     case EP_WhiteKnight:\r
5390       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5391       fromX = fromY = -1;\r
5392       break;\r
5393 \r
5394     case EP_WhiteBishop:\r
5395       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5396       fromX = fromY = -1;\r
5397       break;\r
5398 \r
5399     case EP_WhiteRook:\r
5400       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5401       fromX = fromY = -1;\r
5402       break;\r
5403 \r
5404     case EP_WhiteQueen:\r
5405       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5406       fromX = fromY = -1;\r
5407       break;\r
5408 \r
5409     case EP_WhiteFerz:\r
5410       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5411       fromX = fromY = -1;\r
5412       break;\r
5413 \r
5414     case EP_WhiteWazir:\r
5415       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5416       fromX = fromY = -1;\r
5417       break;\r
5418 \r
5419     case EP_WhiteAlfil:\r
5420       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5421       fromX = fromY = -1;\r
5422       break;\r
5423 \r
5424     case EP_WhiteCannon:\r
5425       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5426       fromX = fromY = -1;\r
5427       break;\r
5428 \r
5429     case EP_WhiteCardinal:\r
5430       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5431       fromX = fromY = -1;\r
5432       break;\r
5433 \r
5434     case EP_WhiteMarshall:\r
5435       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5436       fromX = fromY = -1;\r
5437       break;\r
5438 \r
5439     case EP_WhiteKing:\r
5440       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5441       fromX = fromY = -1;\r
5442       break;\r
5443 \r
5444     case EP_BlackPawn:\r
5445       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5446       fromX = fromY = -1;\r
5447       break;\r
5448 \r
5449     case EP_BlackKnight:\r
5450       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5451       fromX = fromY = -1;\r
5452       break;\r
5453 \r
5454     case EP_BlackBishop:\r
5455       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5456       fromX = fromY = -1;\r
5457       break;\r
5458 \r
5459     case EP_BlackRook:\r
5460       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5461       fromX = fromY = -1;\r
5462       break;\r
5463 \r
5464     case EP_BlackQueen:\r
5465       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5466       fromX = fromY = -1;\r
5467       break;\r
5468 \r
5469     case EP_BlackFerz:\r
5470       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5471       fromX = fromY = -1;\r
5472       break;\r
5473 \r
5474     case EP_BlackWazir:\r
5475       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5476       fromX = fromY = -1;\r
5477       break;\r
5478 \r
5479     case EP_BlackAlfil:\r
5480       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5481       fromX = fromY = -1;\r
5482       break;\r
5483 \r
5484     case EP_BlackCannon:\r
5485       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5486       fromX = fromY = -1;\r
5487       break;\r
5488 \r
5489     case EP_BlackCardinal:\r
5490       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5491       fromX = fromY = -1;\r
5492       break;\r
5493 \r
5494     case EP_BlackMarshall:\r
5495       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5496       fromX = fromY = -1;\r
5497       break;\r
5498 \r
5499     case EP_BlackKing:\r
5500       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5501       fromX = fromY = -1;\r
5502       break;\r
5503 \r
5504     case EP_EmptySquare:\r
5505       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5506       fromX = fromY = -1;\r
5507       break;\r
5508 \r
5509     case EP_ClearBoard:\r
5510       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5511       fromX = fromY = -1;\r
5512       break;\r
5513 \r
5514     case EP_White:\r
5515       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5516       fromX = fromY = -1;\r
5517       break;\r
5518 \r
5519     case EP_Black:\r
5520       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5521       fromX = fromY = -1;\r
5522       break;\r
5523 \r
5524     case EP_Promote:\r
5525       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5526       fromX = fromY = -1;\r
5527       break;\r
5528 \r
5529     case EP_Demote:\r
5530       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5531       fromX = fromY = -1;\r
5532       break;\r
5533 \r
5534     case DP_Pawn:\r
5535       DropMenuEvent(WhitePawn, fromX, fromY);\r
5536       fromX = fromY = -1;\r
5537       break;\r
5538 \r
5539     case DP_Knight:\r
5540       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5541       fromX = fromY = -1;\r
5542       break;\r
5543 \r
5544     case DP_Bishop:\r
5545       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5546       fromX = fromY = -1;\r
5547       break;\r
5548 \r
5549     case DP_Rook:\r
5550       DropMenuEvent(WhiteRook, fromX, fromY);\r
5551       fromX = fromY = -1;\r
5552       break;\r
5553 \r
5554     case DP_Queen:\r
5555       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5556       fromX = fromY = -1;\r
5557       break;\r
5558 \r
5559     case IDM_English:\r
5560       barbaric = 0; appData.language = "";\r
5561       TranslateMenus(0);\r
5562       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5563       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5564       lastChecked = wmId;\r
5565       break;\r
5566 \r
5567     default:\r
5568       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5569           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5570       else\r
5571       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5572           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5573           TranslateMenus(0);\r
5574           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5575           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5576           lastChecked = wmId;\r
5577           break;\r
5578       }\r
5579       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5580     }\r
5581     break;\r
5582 \r
5583   case WM_TIMER:\r
5584     switch (wParam) {\r
5585     case CLOCK_TIMER_ID:\r
5586       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5587       clockTimerEvent = 0;\r
5588       DecrementClocks(); /* call into back end */\r
5589       break;\r
5590     case LOAD_GAME_TIMER_ID:\r
5591       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5592       loadGameTimerEvent = 0;\r
5593       AutoPlayGameLoop(); /* call into back end */\r
5594       break;\r
5595     case ANALYSIS_TIMER_ID:\r
5596       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5597                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5598         AnalysisPeriodicEvent(0);\r
5599       } else {\r
5600         KillTimer(hwnd, analysisTimerEvent);\r
5601         analysisTimerEvent = 0;\r
5602       }\r
5603       break;\r
5604     case DELAYED_TIMER_ID:\r
5605       KillTimer(hwnd, delayedTimerEvent);\r
5606       delayedTimerEvent = 0;\r
5607       delayedTimerCallback();\r
5608       break;\r
5609     }\r
5610     break;\r
5611 \r
5612   case WM_USER_Input:\r
5613     InputEvent(hwnd, message, wParam, lParam);\r
5614     break;\r
5615 \r
5616   /* [AS] Also move "attached" child windows */\r
5617   case WM_WINDOWPOSCHANGING:\r
5618 \r
5619     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5620         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5621 \r
5622         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5623             /* Window is moving */\r
5624             RECT rcMain;\r
5625 \r
5626 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5627             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5628             rcMain.right  = wpMain.x + wpMain.width;\r
5629             rcMain.top    = wpMain.y;\r
5630             rcMain.bottom = wpMain.y + wpMain.height;\r
5631             \r
5632             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5633             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5634             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5635             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5636             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5637             wpMain.x = lpwp->x;\r
5638             wpMain.y = lpwp->y;\r
5639         }\r
5640     }\r
5641     break;\r
5642 \r
5643   /* [AS] Snapping */\r
5644   case WM_ENTERSIZEMOVE:\r
5645     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5646     if (hwnd == hwndMain) {\r
5647       doingSizing = TRUE;\r
5648       lastSizing = 0;\r
5649     }\r
5650     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5651     break;\r
5652 \r
5653   case WM_SIZING:\r
5654     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5655     if (hwnd == hwndMain) {\r
5656       lastSizing = wParam;\r
5657     }\r
5658     break;\r
5659 \r
5660   case WM_MOVING:\r
5661     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5662       return OnMoving( &sd, hwnd, wParam, lParam );\r
5663 \r
5664   case WM_EXITSIZEMOVE:\r
5665     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5666     if (hwnd == hwndMain) {\r
5667       RECT client;\r
5668       doingSizing = FALSE;\r
5669       InvalidateRect(hwnd, &boardRect, FALSE);\r
5670       GetClientRect(hwnd, &client);\r
5671       ResizeBoard(client.right, client.bottom, lastSizing);\r
5672       lastSizing = 0;\r
5673       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5674     }\r
5675     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5676     break;\r
5677 \r
5678   case WM_DESTROY: /* message: window being destroyed */\r
5679     PostQuitMessage(0);\r
5680     break;\r
5681 \r
5682   case WM_CLOSE:\r
5683     if (hwnd == hwndMain) {\r
5684       ExitEvent(0);\r
5685     }\r
5686     break;\r
5687 \r
5688   default:      /* Passes it on if unprocessed */\r
5689     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5690   }\r
5691   return 0;\r
5692 }\r
5693 \r
5694 /*---------------------------------------------------------------------------*\\r
5695  *\r
5696  * Misc utility routines\r
5697  *\r
5698 \*---------------------------------------------------------------------------*/\r
5699 \r
5700 /*\r
5701  * Decent random number generator, at least not as bad as Windows\r
5702  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5703  */\r
5704 unsigned int randstate;\r
5705 \r
5706 int\r
5707 myrandom(void)\r
5708 {\r
5709   randstate = randstate * 1664525 + 1013904223;\r
5710   return (int) randstate & 0x7fffffff;\r
5711 }\r
5712 \r
5713 void\r
5714 mysrandom(unsigned int seed)\r
5715 {\r
5716   randstate = seed;\r
5717 }\r
5718 \r
5719 \r
5720 /* \r
5721  * returns TRUE if user selects a different color, FALSE otherwise \r
5722  */\r
5723 \r
5724 BOOL\r
5725 ChangeColor(HWND hwnd, COLORREF *which)\r
5726 {\r
5727   static BOOL firstTime = TRUE;\r
5728   static DWORD customColors[16];\r
5729   CHOOSECOLOR cc;\r
5730   COLORREF newcolor;\r
5731   int i;\r
5732   ColorClass ccl;\r
5733 \r
5734   if (firstTime) {\r
5735     /* Make initial colors in use available as custom colors */\r
5736     /* Should we put the compiled-in defaults here instead? */\r
5737     i = 0;\r
5738     customColors[i++] = lightSquareColor & 0xffffff;\r
5739     customColors[i++] = darkSquareColor & 0xffffff;\r
5740     customColors[i++] = whitePieceColor & 0xffffff;\r
5741     customColors[i++] = blackPieceColor & 0xffffff;\r
5742     customColors[i++] = highlightSquareColor & 0xffffff;\r
5743     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5744 \r
5745     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5746       customColors[i++] = textAttribs[ccl].color;\r
5747     }\r
5748     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5749     firstTime = FALSE;\r
5750   }\r
5751 \r
5752   cc.lStructSize = sizeof(cc);\r
5753   cc.hwndOwner = hwnd;\r
5754   cc.hInstance = NULL;\r
5755   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5756   cc.lpCustColors = (LPDWORD) customColors;\r
5757   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5758 \r
5759   if (!ChooseColor(&cc)) return FALSE;\r
5760 \r
5761   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5762   if (newcolor == *which) return FALSE;\r
5763   *which = newcolor;\r
5764   return TRUE;\r
5765 \r
5766   /*\r
5767   InitDrawingColors();\r
5768   InvalidateRect(hwnd, &boardRect, FALSE);\r
5769   */\r
5770 }\r
5771 \r
5772 BOOLEAN\r
5773 MyLoadSound(MySound *ms)\r
5774 {\r
5775   BOOL ok = FALSE;\r
5776   struct stat st;\r
5777   FILE *f;\r
5778 \r
5779   if (ms->data && ms->flag) free(ms->data);\r
5780   ms->data = NULL;\r
5781 \r
5782   switch (ms->name[0]) {\r
5783   case NULLCHAR:\r
5784     /* Silence */\r
5785     ok = TRUE;\r
5786     break;\r
5787   case '$':\r
5788     /* System sound from Control Panel.  Don't preload here. */\r
5789     ok = TRUE;\r
5790     break;\r
5791   case '!':\r
5792     if (ms->name[1] == NULLCHAR) {\r
5793       /* "!" alone = silence */\r
5794       ok = TRUE;\r
5795     } else {\r
5796       /* Builtin wave resource.  Error if not found. */\r
5797       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5798       if (h == NULL) break;\r
5799       ms->data = (void *)LoadResource(hInst, h);\r
5800       ms->flag = 0; // not maloced, so cannot be freed!\r
5801       if (h == NULL) break;\r
5802       ok = TRUE;\r
5803     }\r
5804     break;\r
5805   default:\r
5806     /* .wav file.  Error if not found. */\r
5807     f = fopen(ms->name, "rb");\r
5808     if (f == NULL) break;\r
5809     if (fstat(fileno(f), &st) < 0) break;\r
5810     ms->data = malloc(st.st_size);\r
5811     ms->flag = 1;\r
5812     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5813     fclose(f);\r
5814     ok = TRUE;\r
5815     break;\r
5816   }\r
5817   if (!ok) {\r
5818     char buf[MSG_SIZ];\r
5819       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5820     DisplayError(buf, GetLastError());\r
5821   }\r
5822   return ok;\r
5823 }\r
5824 \r
5825 BOOLEAN\r
5826 MyPlaySound(MySound *ms)\r
5827 {\r
5828   BOOLEAN ok = FALSE;\r
5829 \r
5830   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5831   switch (ms->name[0]) {\r
5832   case NULLCHAR:\r
5833         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5834     /* Silence */\r
5835     ok = TRUE;\r
5836     break;\r
5837   case '$':\r
5838     /* System sound from Control Panel (deprecated feature).\r
5839        "$" alone or an unset sound name gets default beep (still in use). */\r
5840     if (ms->name[1]) {\r
5841       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5842     }\r
5843     if (!ok) ok = MessageBeep(MB_OK);\r
5844     break; \r
5845   case '!':\r
5846     /* Builtin wave resource, or "!" alone for silence */\r
5847     if (ms->name[1]) {\r
5848       if (ms->data == NULL) return FALSE;\r
5849       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5850     } else {\r
5851       ok = TRUE;\r
5852     }\r
5853     break;\r
5854   default:\r
5855     /* .wav file.  Error if not found. */\r
5856     if (ms->data == NULL) return FALSE;\r
5857     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5858     break;\r
5859   }\r
5860   /* Don't print an error: this can happen innocently if the sound driver\r
5861      is busy; for instance, if another instance of WinBoard is playing\r
5862      a sound at about the same time. */\r
5863   return ok;\r
5864 }\r
5865 \r
5866 \r
5867 LRESULT CALLBACK\r
5868 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5869 {\r
5870   BOOL ok;\r
5871   OPENFILENAME *ofn;\r
5872   static UINT *number; /* gross that this is static */\r
5873 \r
5874   switch (message) {\r
5875   case WM_INITDIALOG: /* message: initialize dialog box */\r
5876     /* Center the dialog over the application window */\r
5877     ofn = (OPENFILENAME *) lParam;\r
5878     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5879       number = (UINT *) ofn->lCustData;\r
5880       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5881     } else {\r
5882       number = NULL;\r
5883     }\r
5884     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5885     Translate(hDlg, 1536);\r
5886     return FALSE;  /* Allow for further processing */\r
5887 \r
5888   case WM_COMMAND:\r
5889     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5890       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5891     }\r
5892     return FALSE;  /* Allow for further processing */\r
5893   }\r
5894   return FALSE;\r
5895 }\r
5896 \r
5897 UINT APIENTRY\r
5898 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5899 {\r
5900   static UINT *number;\r
5901   OPENFILENAME *ofname;\r
5902   OFNOTIFY *ofnot;\r
5903   switch (uiMsg) {\r
5904   case WM_INITDIALOG:\r
5905     Translate(hdlg, DLG_IndexNumber);\r
5906     ofname = (OPENFILENAME *)lParam;\r
5907     number = (UINT *)(ofname->lCustData);\r
5908     break;\r
5909   case WM_NOTIFY:\r
5910     ofnot = (OFNOTIFY *)lParam;\r
5911     if (ofnot->hdr.code == CDN_FILEOK) {\r
5912       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5913     }\r
5914     break;\r
5915   }\r
5916   return 0;\r
5917 }\r
5918 \r
5919 \r
5920 FILE *\r
5921 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5922                char *nameFilt, char *dlgTitle, UINT *number,\r
5923                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5924 {\r
5925   OPENFILENAME openFileName;\r
5926   char buf1[MSG_SIZ];\r
5927   FILE *f;\r
5928 \r
5929   if (fileName == NULL) fileName = buf1;\r
5930   if (defName == NULL) {\r
5931     safeStrCpy(fileName, "*.", 3 );\r
5932     strcat(fileName, defExt);\r
5933   } else {\r
5934     safeStrCpy(fileName, defName, MSG_SIZ );\r
5935   }\r
5936     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5937   if (number) *number = 0;\r
5938 \r
5939   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5940   openFileName.hwndOwner         = hwnd;\r
5941   openFileName.hInstance         = (HANDLE) hInst;\r
5942   openFileName.lpstrFilter       = nameFilt;\r
5943   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5944   openFileName.nMaxCustFilter    = 0L;\r
5945   openFileName.nFilterIndex      = 1L;\r
5946   openFileName.lpstrFile         = fileName;\r
5947   openFileName.nMaxFile          = MSG_SIZ;\r
5948   openFileName.lpstrFileTitle    = fileTitle;\r
5949   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5950   openFileName.lpstrInitialDir   = NULL;\r
5951   openFileName.lpstrTitle        = dlgTitle;\r
5952   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5953     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5954     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5955     | (oldDialog ? 0 : OFN_EXPLORER);\r
5956   openFileName.nFileOffset       = 0;\r
5957   openFileName.nFileExtension    = 0;\r
5958   openFileName.lpstrDefExt       = defExt;\r
5959   openFileName.lCustData         = (LONG) number;\r
5960   openFileName.lpfnHook          = oldDialog ?\r
5961     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5962   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5963 \r
5964   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5965                         GetOpenFileName(&openFileName)) {\r
5966     /* open the file */\r
5967     f = fopen(openFileName.lpstrFile, write);\r
5968     if (f == NULL) {\r
5969       MessageBox(hwnd, _("File open failed"), NULL,\r
5970                  MB_OK|MB_ICONEXCLAMATION);\r
5971       return NULL;\r
5972     }\r
5973   } else {\r
5974     int err = CommDlgExtendedError();\r
5975     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5976     return FALSE;\r
5977   }\r
5978   return f;\r
5979 }\r
5980 \r
5981 \r
5982 \r
5983 VOID APIENTRY\r
5984 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5985 {\r
5986   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5987 \r
5988   /*\r
5989    * Get the first pop-up menu in the menu template. This is the\r
5990    * menu that TrackPopupMenu displays.\r
5991    */\r
5992   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5993   TranslateOneMenu(10, hmenuTrackPopup);\r
5994 \r
5995   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5996 \r
5997   /*\r
5998    * TrackPopup uses screen coordinates, so convert the\r
5999    * coordinates of the mouse click to screen coordinates.\r
6000    */\r
6001   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6002 \r
6003   /* Draw and track the floating pop-up menu. */\r
6004   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6005                  pt.x, pt.y, 0, hwnd, NULL);\r
6006 \r
6007   /* Destroy the menu.*/\r
6008   DestroyMenu(hmenu);\r
6009 }\r
6010    \r
6011 typedef struct {\r
6012   HWND hDlg, hText;\r
6013   int sizeX, sizeY, newSizeX, newSizeY;\r
6014   HDWP hdwp;\r
6015 } ResizeEditPlusButtonsClosure;\r
6016 \r
6017 BOOL CALLBACK\r
6018 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6019 {\r
6020   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6021   RECT rect;\r
6022   POINT pt;\r
6023 \r
6024   if (hChild == cl->hText) return TRUE;\r
6025   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6026   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6027   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6028   ScreenToClient(cl->hDlg, &pt);\r
6029   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6030     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6031   return TRUE;\r
6032 }\r
6033 \r
6034 /* Resize a dialog that has a (rich) edit field filling most of\r
6035    the top, with a row of buttons below */\r
6036 VOID\r
6037 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6038 {\r
6039   RECT rectText;\r
6040   int newTextHeight, newTextWidth;\r
6041   ResizeEditPlusButtonsClosure cl;\r
6042   \r
6043   /*if (IsIconic(hDlg)) return;*/\r
6044   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6045   \r
6046   cl.hdwp = BeginDeferWindowPos(8);\r
6047 \r
6048   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6049   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6050   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6051   if (newTextHeight < 0) {\r
6052     newSizeY += -newTextHeight;\r
6053     newTextHeight = 0;\r
6054   }\r
6055   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6056     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6057 \r
6058   cl.hDlg = hDlg;\r
6059   cl.hText = hText;\r
6060   cl.sizeX = sizeX;\r
6061   cl.sizeY = sizeY;\r
6062   cl.newSizeX = newSizeX;\r
6063   cl.newSizeY = newSizeY;\r
6064   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6065 \r
6066   EndDeferWindowPos(cl.hdwp);\r
6067 }\r
6068 \r
6069 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6070 {\r
6071     RECT    rChild, rParent;\r
6072     int     wChild, hChild, wParent, hParent;\r
6073     int     wScreen, hScreen, xNew, yNew;\r
6074     HDC     hdc;\r
6075 \r
6076     /* Get the Height and Width of the child window */\r
6077     GetWindowRect (hwndChild, &rChild);\r
6078     wChild = rChild.right - rChild.left;\r
6079     hChild = rChild.bottom - rChild.top;\r
6080 \r
6081     /* Get the Height and Width of the parent window */\r
6082     GetWindowRect (hwndParent, &rParent);\r
6083     wParent = rParent.right - rParent.left;\r
6084     hParent = rParent.bottom - rParent.top;\r
6085 \r
6086     /* Get the display limits */\r
6087     hdc = GetDC (hwndChild);\r
6088     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6089     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6090     ReleaseDC(hwndChild, hdc);\r
6091 \r
6092     /* Calculate new X position, then adjust for screen */\r
6093     xNew = rParent.left + ((wParent - wChild) /2);\r
6094     if (xNew < 0) {\r
6095         xNew = 0;\r
6096     } else if ((xNew+wChild) > wScreen) {\r
6097         xNew = wScreen - wChild;\r
6098     }\r
6099 \r
6100     /* Calculate new Y position, then adjust for screen */\r
6101     if( mode == 0 ) {\r
6102         yNew = rParent.top  + ((hParent - hChild) /2);\r
6103     }\r
6104     else {\r
6105         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6106     }\r
6107 \r
6108     if (yNew < 0) {\r
6109         yNew = 0;\r
6110     } else if ((yNew+hChild) > hScreen) {\r
6111         yNew = hScreen - hChild;\r
6112     }\r
6113 \r
6114     /* Set it, and return */\r
6115     return SetWindowPos (hwndChild, NULL,\r
6116                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6117 }\r
6118 \r
6119 /* Center one window over another */\r
6120 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6121 {\r
6122     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6123 }\r
6124 \r
6125 /*---------------------------------------------------------------------------*\\r
6126  *\r
6127  * Startup Dialog functions\r
6128  *\r
6129 \*---------------------------------------------------------------------------*/\r
6130 void\r
6131 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6132 {\r
6133   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6134 \r
6135   while (*cd != NULL) {\r
6136     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6137     cd++;\r
6138   }\r
6139 }\r
6140 \r
6141 void\r
6142 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6143 {\r
6144   char buf1[MAX_ARG_LEN];\r
6145   int len;\r
6146 \r
6147   if (str[0] == '@') {\r
6148     FILE* f = fopen(str + 1, "r");\r
6149     if (f == NULL) {\r
6150       DisplayFatalError(str + 1, errno, 2);\r
6151       return;\r
6152     }\r
6153     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6154     fclose(f);\r
6155     buf1[len] = NULLCHAR;\r
6156     str = buf1;\r
6157   }\r
6158 \r
6159   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6160 \r
6161   for (;;) {\r
6162     char buf[MSG_SIZ];\r
6163     char *end = strchr(str, '\n');\r
6164     if (end == NULL) return;\r
6165     memcpy(buf, str, end - str);\r
6166     buf[end - str] = NULLCHAR;\r
6167     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6168     str = end + 1;\r
6169   }\r
6170 }\r
6171 \r
6172 void\r
6173 SetStartupDialogEnables(HWND hDlg)\r
6174 {\r
6175   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6176     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6177     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6178   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6179     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6180   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6181     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6182   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6183     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6184   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6185     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6186     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6187     IsDlgButtonChecked(hDlg, OPT_View));\r
6188 }\r
6189 \r
6190 char *\r
6191 QuoteForFilename(char *filename)\r
6192 {\r
6193   int dquote, space;\r
6194   dquote = strchr(filename, '"') != NULL;\r
6195   space = strchr(filename, ' ') != NULL;\r
6196   if (dquote || space) {\r
6197     if (dquote) {\r
6198       return "'";\r
6199     } else {\r
6200       return "\"";\r
6201     }\r
6202   } else {\r
6203     return "";\r
6204   }\r
6205 }\r
6206 \r
6207 VOID\r
6208 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6209 {\r
6210   char buf[MSG_SIZ];\r
6211   char *q;\r
6212 \r
6213   InitComboStringsFromOption(hwndCombo, nthnames);\r
6214   q = QuoteForFilename(nthcp);\r
6215     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6216   if (*nthdir != NULLCHAR) {\r
6217     q = QuoteForFilename(nthdir);\r
6218       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6219   }\r
6220   if (*nthcp == NULLCHAR) {\r
6221     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\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 \r
6228 LRESULT CALLBACK\r
6229 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6230 {\r
6231   char buf[MSG_SIZ];\r
6232   HANDLE hwndCombo;\r
6233   char *p;\r
6234 \r
6235   switch (message) {\r
6236   case WM_INITDIALOG:\r
6237     /* Center the dialog */\r
6238     CenterWindow (hDlg, GetDesktopWindow());\r
6239     Translate(hDlg, DLG_Startup);\r
6240     /* Initialize the dialog items */\r
6241     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6242                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6243                   firstChessProgramNames);\r
6244     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6245                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6246                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6247     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6248     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6249       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6250     if (*appData.icsHelper != NULLCHAR) {\r
6251       char *q = QuoteForFilename(appData.icsHelper);\r
6252       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6253     }\r
6254     if (*appData.icsHost == NULLCHAR) {\r
6255       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6256       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6257     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6258       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6259       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6260     }\r
6261 \r
6262     if (appData.icsActive) {\r
6263       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6264     }\r
6265     else if (appData.noChessProgram) {\r
6266       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6267     }\r
6268     else {\r
6269       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6270     }\r
6271 \r
6272     SetStartupDialogEnables(hDlg);\r
6273     return TRUE;\r
6274 \r
6275   case WM_COMMAND:\r
6276     switch (LOWORD(wParam)) {\r
6277     case IDOK:\r
6278       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6279         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6280         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6281         p = buf;\r
6282         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6283         ParseArgs(StringGet, &p);\r
6284         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6285         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6286         p = buf;\r
6287         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6288         ParseArgs(StringGet, &p);\r
6289         SwapEngines(singleList); // ... and then make it 'second'\r
6290         appData.noChessProgram = FALSE;\r
6291         appData.icsActive = FALSE;\r
6292       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6293         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6294         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6295         p = buf;\r
6296         ParseArgs(StringGet, &p);\r
6297         if (appData.zippyPlay) {\r
6298           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6299           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6300           p = buf;\r
6301           ParseArgs(StringGet, &p);\r
6302         }\r
6303       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6304         appData.noChessProgram = TRUE;\r
6305         appData.icsActive = FALSE;\r
6306       } else {\r
6307         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6308                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6309         return TRUE;\r
6310       }\r
6311       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6312         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6313         p = buf;\r
6314         ParseArgs(StringGet, &p);\r
6315       }\r
6316       EndDialog(hDlg, TRUE);\r
6317       return TRUE;\r
6318 \r
6319     case IDCANCEL:\r
6320       ExitEvent(0);\r
6321       return TRUE;\r
6322 \r
6323     case IDM_HELPCONTENTS:\r
6324       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6325         MessageBox (GetFocus(),\r
6326                     _("Unable to activate help"),\r
6327                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6328       }\r
6329       break;\r
6330 \r
6331     default:\r
6332       SetStartupDialogEnables(hDlg);\r
6333       break;\r
6334     }\r
6335     break;\r
6336   }\r
6337   return FALSE;\r
6338 }\r
6339 \r
6340 /*---------------------------------------------------------------------------*\\r
6341  *\r
6342  * About box dialog functions\r
6343  *\r
6344 \*---------------------------------------------------------------------------*/\r
6345 \r
6346 /* Process messages for "About" dialog box */\r
6347 LRESULT CALLBACK\r
6348 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6349 {\r
6350   switch (message) {\r
6351   case WM_INITDIALOG: /* message: initialize dialog box */\r
6352     /* Center the dialog over the application window */\r
6353     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6354     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6355     Translate(hDlg, ABOUTBOX);\r
6356     JAWS_COPYRIGHT\r
6357     return (TRUE);\r
6358 \r
6359   case WM_COMMAND: /* message: received a command */\r
6360     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6361         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6362       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6363       return (TRUE);\r
6364     }\r
6365     break;\r
6366   }\r
6367   return (FALSE);\r
6368 }\r
6369 \r
6370 /*---------------------------------------------------------------------------*\\r
6371  *\r
6372  * Comment Dialog functions\r
6373  *\r
6374 \*---------------------------------------------------------------------------*/\r
6375 \r
6376 LRESULT CALLBACK\r
6377 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6378 {\r
6379   static HANDLE hwndText = NULL;\r
6380   int len, newSizeX, newSizeY, flags;\r
6381   static int sizeX, sizeY;\r
6382   char *str;\r
6383   RECT rect;\r
6384   MINMAXINFO *mmi;\r
6385 \r
6386   switch (message) {\r
6387   case WM_INITDIALOG: /* message: initialize dialog box */\r
6388     /* Initialize the dialog items */\r
6389     Translate(hDlg, DLG_EditComment);\r
6390     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6391     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6392     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6393     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6394     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6395     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6396     SetWindowText(hDlg, commentTitle);\r
6397     if (editComment) {\r
6398       SetFocus(hwndText);\r
6399     } else {\r
6400       SetFocus(GetDlgItem(hDlg, IDOK));\r
6401     }\r
6402     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6403                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6404                 MAKELPARAM(FALSE, 0));\r
6405     /* Size and position the dialog */\r
6406     if (!commentDialog) {\r
6407       commentDialog = hDlg;\r
6408       flags = SWP_NOZORDER;\r
6409       GetClientRect(hDlg, &rect);\r
6410       sizeX = rect.right;\r
6411       sizeY = rect.bottom;\r
6412       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6413           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6414         WINDOWPLACEMENT wp;\r
6415         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6416         wp.length = sizeof(WINDOWPLACEMENT);\r
6417         wp.flags = 0;\r
6418         wp.showCmd = SW_SHOW;\r
6419         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6420         wp.rcNormalPosition.left = wpComment.x;\r
6421         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6422         wp.rcNormalPosition.top = wpComment.y;\r
6423         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6424         SetWindowPlacement(hDlg, &wp);\r
6425 \r
6426         GetClientRect(hDlg, &rect);\r
6427         newSizeX = rect.right;\r
6428         newSizeY = rect.bottom;\r
6429         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6430                               newSizeX, newSizeY);\r
6431         sizeX = newSizeX;\r
6432         sizeY = newSizeY;\r
6433       }\r
6434     }\r
6435     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6436     return FALSE;\r
6437 \r
6438   case WM_COMMAND: /* message: received a command */\r
6439     switch (LOWORD(wParam)) {\r
6440     case IDOK:\r
6441       if (editComment) {\r
6442         char *p, *q;\r
6443         /* Read changed options from the dialog box */\r
6444         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6445         len = GetWindowTextLength(hwndText);\r
6446         str = (char *) malloc(len + 1);\r
6447         GetWindowText(hwndText, str, len + 1);\r
6448         p = q = str;\r
6449         while (*q) {\r
6450           if (*q == '\r')\r
6451             q++;\r
6452           else\r
6453             *p++ = *q++;\r
6454         }\r
6455         *p = NULLCHAR;\r
6456         ReplaceComment(commentIndex, str);\r
6457         free(str);\r
6458       }\r
6459       CommentPopDown();\r
6460       return TRUE;\r
6461 \r
6462     case IDCANCEL:\r
6463     case OPT_CancelComment:\r
6464       CommentPopDown();\r
6465       return TRUE;\r
6466 \r
6467     case OPT_ClearComment:\r
6468       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6469       break;\r
6470 \r
6471     case OPT_EditComment:\r
6472       EditCommentEvent();\r
6473       return TRUE;\r
6474 \r
6475     default:\r
6476       break;\r
6477     }\r
6478     break;\r
6479 \r
6480   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6481         if( wParam == OPT_CommentText ) {\r
6482             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6483 \r
6484             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6485                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6486                 POINTL pt;\r
6487                 LRESULT index;\r
6488 \r
6489                 pt.x = LOWORD( lpMF->lParam );\r
6490                 pt.y = HIWORD( lpMF->lParam );\r
6491 \r
6492                 if(lpMF->msg == WM_CHAR) {\r
6493                         CHARRANGE sel;\r
6494                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6495                         index = sel.cpMin;\r
6496                 } else\r
6497                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6498 \r
6499                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6500                 len = GetWindowTextLength(hwndText);\r
6501                 str = (char *) malloc(len + 1);\r
6502                 GetWindowText(hwndText, str, len + 1);\r
6503                 ReplaceComment(commentIndex, str);\r
6504                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6505                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6506                 free(str);\r
6507 \r
6508                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6509                 lpMF->msg = WM_USER;\r
6510 \r
6511                 return TRUE;\r
6512             }\r
6513         }\r
6514         break;\r
6515 \r
6516   case WM_SIZE:\r
6517     newSizeX = LOWORD(lParam);\r
6518     newSizeY = HIWORD(lParam);\r
6519     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6520     sizeX = newSizeX;\r
6521     sizeY = newSizeY;\r
6522     break;\r
6523 \r
6524   case WM_GETMINMAXINFO:\r
6525     /* Prevent resizing window too small */\r
6526     mmi = (MINMAXINFO *) lParam;\r
6527     mmi->ptMinTrackSize.x = 100;\r
6528     mmi->ptMinTrackSize.y = 100;\r
6529     break;\r
6530   }\r
6531   return FALSE;\r
6532 }\r
6533 \r
6534 VOID\r
6535 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6536 {\r
6537   FARPROC lpProc;\r
6538   char *p, *q;\r
6539 \r
6540   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6541 \r
6542   if (str == NULL) str = "";\r
6543   p = (char *) malloc(2 * strlen(str) + 2);\r
6544   q = p;\r
6545   while (*str) {\r
6546     if (*str == '\n') *q++ = '\r';\r
6547     *q++ = *str++;\r
6548   }\r
6549   *q = NULLCHAR;\r
6550   if (commentText != NULL) free(commentText);\r
6551 \r
6552   commentIndex = index;\r
6553   commentTitle = title;\r
6554   commentText = p;\r
6555   editComment = edit;\r
6556 \r
6557   if (commentDialog) {\r
6558     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6559     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6560   } else {\r
6561     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6562     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6563                  hwndMain, (DLGPROC)lpProc);\r
6564     FreeProcInstance(lpProc);\r
6565   }\r
6566   commentUp = TRUE;\r
6567 }\r
6568 \r
6569 \r
6570 /*---------------------------------------------------------------------------*\\r
6571  *\r
6572  * Type-in move dialog functions\r
6573  * \r
6574 \*---------------------------------------------------------------------------*/\r
6575 \r
6576 LRESULT CALLBACK\r
6577 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6578 {\r
6579   char move[MSG_SIZ];\r
6580   HWND hInput;\r
6581 \r
6582   switch (message) {\r
6583   case WM_INITDIALOG:\r
6584     move[0] = (char) lParam;\r
6585     move[1] = NULLCHAR;\r
6586     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6587     Translate(hDlg, DLG_TypeInMove);\r
6588     hInput = GetDlgItem(hDlg, OPT_Move);\r
6589     SetWindowText(hInput, move);\r
6590     SetFocus(hInput);\r
6591     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6592     return FALSE;\r
6593 \r
6594   case WM_COMMAND:\r
6595     switch (LOWORD(wParam)) {\r
6596     case IDOK:\r
6597 \r
6598       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6599       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6600       TypeInDoneEvent(move);\r
6601       EndDialog(hDlg, TRUE);\r
6602       return TRUE;\r
6603     case IDCANCEL:\r
6604       EndDialog(hDlg, FALSE);\r
6605       return TRUE;\r
6606     default:\r
6607       break;\r
6608     }\r
6609     break;\r
6610   }\r
6611   return FALSE;\r
6612 }\r
6613 \r
6614 VOID\r
6615 PopUpMoveDialog(char firstchar)\r
6616 {\r
6617     FARPROC lpProc;\r
6618 \r
6619       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6620       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6621         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6622       FreeProcInstance(lpProc);\r
6623 }\r
6624 \r
6625 /*---------------------------------------------------------------------------*\\r
6626  *\r
6627  * Type-in name dialog functions\r
6628  * \r
6629 \*---------------------------------------------------------------------------*/\r
6630 \r
6631 LRESULT CALLBACK\r
6632 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6633 {\r
6634   char move[MSG_SIZ];\r
6635   HWND hInput;\r
6636 \r
6637   switch (message) {\r
6638   case WM_INITDIALOG:\r
6639     move[0] = (char) lParam;\r
6640     move[1] = NULLCHAR;\r
6641     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6642     Translate(hDlg, DLG_TypeInName);\r
6643     hInput = GetDlgItem(hDlg, OPT_Name);\r
6644     SetWindowText(hInput, move);\r
6645     SetFocus(hInput);\r
6646     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6647     return FALSE;\r
6648 \r
6649   case WM_COMMAND:\r
6650     switch (LOWORD(wParam)) {\r
6651     case IDOK:\r
6652       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6653       appData.userName = strdup(move);\r
6654       SetUserLogo();\r
6655       SetGameInfo();\r
6656       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6657         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6658         DisplayTitle(move);\r
6659       }\r
6660 \r
6661 \r
6662       EndDialog(hDlg, TRUE);\r
6663       return TRUE;\r
6664     case IDCANCEL:\r
6665       EndDialog(hDlg, FALSE);\r
6666       return TRUE;\r
6667     default:\r
6668       break;\r
6669     }\r
6670     break;\r
6671   }\r
6672   return FALSE;\r
6673 }\r
6674 \r
6675 VOID\r
6676 PopUpNameDialog(char firstchar)\r
6677 {\r
6678     FARPROC lpProc;\r
6679     \r
6680       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6681       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6682         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6683       FreeProcInstance(lpProc);\r
6684 }\r
6685 \r
6686 /*---------------------------------------------------------------------------*\\r
6687  *\r
6688  *  Error dialogs\r
6689  * \r
6690 \*---------------------------------------------------------------------------*/\r
6691 \r
6692 /* Nonmodal error box */\r
6693 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6694                              WPARAM wParam, LPARAM lParam);\r
6695 \r
6696 VOID\r
6697 ErrorPopUp(char *title, char *content)\r
6698 {\r
6699   FARPROC lpProc;\r
6700   char *p, *q;\r
6701   BOOLEAN modal = hwndMain == NULL;\r
6702 \r
6703   p = content;\r
6704   q = errorMessage;\r
6705   while (*p) {\r
6706     if (*p == '\n') {\r
6707       if (modal) {\r
6708         *q++ = ' ';\r
6709         p++;\r
6710       } else {\r
6711         *q++ = '\r';\r
6712         *q++ = *p++;\r
6713       }\r
6714     } else {\r
6715       *q++ = *p++;\r
6716     }\r
6717   }\r
6718   *q = NULLCHAR;\r
6719   strncpy(errorTitle, title, sizeof(errorTitle));\r
6720   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6721   \r
6722   if (modal) {\r
6723     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6724   } else {\r
6725     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6726     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6727                  hwndMain, (DLGPROC)lpProc);\r
6728     FreeProcInstance(lpProc);\r
6729   }\r
6730 }\r
6731 \r
6732 VOID\r
6733 ErrorPopDown()\r
6734 {\r
6735   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6736   if (errorDialog == NULL) return;\r
6737   DestroyWindow(errorDialog);\r
6738   errorDialog = NULL;\r
6739   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6740 }\r
6741 \r
6742 LRESULT CALLBACK\r
6743 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6744 {\r
6745   HANDLE hwndText;\r
6746   RECT rChild;\r
6747 \r
6748   switch (message) {\r
6749   case WM_INITDIALOG:\r
6750     GetWindowRect(hDlg, &rChild);\r
6751 \r
6752     /*\r
6753     SetWindowPos(hDlg, NULL, rChild.left,\r
6754       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6755       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6756     */\r
6757 \r
6758     /* \r
6759         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6760         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6761         and it doesn't work when you resize the dialog.\r
6762         For now, just give it a default position.\r
6763     */\r
6764     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6765     Translate(hDlg, DLG_Error);\r
6766 \r
6767     errorDialog = hDlg;\r
6768     SetWindowText(hDlg, errorTitle);\r
6769     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6770     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6771     return FALSE;\r
6772 \r
6773   case WM_COMMAND:\r
6774     switch (LOWORD(wParam)) {\r
6775     case IDOK:\r
6776     case IDCANCEL:\r
6777       if (errorDialog == hDlg) errorDialog = NULL;\r
6778       DestroyWindow(hDlg);\r
6779       return TRUE;\r
6780 \r
6781     default:\r
6782       break;\r
6783     }\r
6784     break;\r
6785   }\r
6786   return FALSE;\r
6787 }\r
6788 \r
6789 #ifdef GOTHIC\r
6790 HWND gothicDialog = NULL;\r
6791 \r
6792 LRESULT CALLBACK\r
6793 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6794 {\r
6795   HANDLE hwndText;\r
6796   RECT rChild;\r
6797   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6798 \r
6799   switch (message) {\r
6800   case WM_INITDIALOG:\r
6801     GetWindowRect(hDlg, &rChild);\r
6802 \r
6803     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6804                                                              SWP_NOZORDER);\r
6805 \r
6806     /* \r
6807         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6808         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6809         and it doesn't work when you resize the dialog.\r
6810         For now, just give it a default position.\r
6811     */\r
6812     gothicDialog = hDlg;\r
6813     SetWindowText(hDlg, errorTitle);\r
6814     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6815     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6816     return FALSE;\r
6817 \r
6818   case WM_COMMAND:\r
6819     switch (LOWORD(wParam)) {\r
6820     case IDOK:\r
6821     case IDCANCEL:\r
6822       if (errorDialog == hDlg) errorDialog = NULL;\r
6823       DestroyWindow(hDlg);\r
6824       return TRUE;\r
6825 \r
6826     default:\r
6827       break;\r
6828     }\r
6829     break;\r
6830   }\r
6831   return FALSE;\r
6832 }\r
6833 \r
6834 VOID\r
6835 GothicPopUp(char *title, VariantClass variant)\r
6836 {\r
6837   FARPROC lpProc;\r
6838   static char *lastTitle;\r
6839 \r
6840   strncpy(errorTitle, title, sizeof(errorTitle));\r
6841   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6842 \r
6843   if(lastTitle != title && gothicDialog != NULL) {\r
6844     DestroyWindow(gothicDialog);\r
6845     gothicDialog = NULL;\r
6846   }\r
6847   if(variant != VariantNormal && gothicDialog == NULL) {\r
6848     title = lastTitle;\r
6849     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6850     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6851                  hwndMain, (DLGPROC)lpProc);\r
6852     FreeProcInstance(lpProc);\r
6853   }\r
6854 }\r
6855 #endif\r
6856 \r
6857 /*---------------------------------------------------------------------------*\\r
6858  *\r
6859  *  Ics Interaction console functions\r
6860  *\r
6861 \*---------------------------------------------------------------------------*/\r
6862 \r
6863 #define HISTORY_SIZE 64\r
6864 static char *history[HISTORY_SIZE];\r
6865 int histIn = 0, histP = 0;\r
6866 \r
6867 VOID\r
6868 SaveInHistory(char *cmd)\r
6869 {\r
6870   if (history[histIn] != NULL) {\r
6871     free(history[histIn]);\r
6872     history[histIn] = NULL;\r
6873   }\r
6874   if (*cmd == NULLCHAR) return;\r
6875   history[histIn] = StrSave(cmd);\r
6876   histIn = (histIn + 1) % HISTORY_SIZE;\r
6877   if (history[histIn] != NULL) {\r
6878     free(history[histIn]);\r
6879     history[histIn] = NULL;\r
6880   }\r
6881   histP = histIn;\r
6882 }\r
6883 \r
6884 char *\r
6885 PrevInHistory(char *cmd)\r
6886 {\r
6887   int newhp;\r
6888   if (histP == histIn) {\r
6889     if (history[histIn] != NULL) free(history[histIn]);\r
6890     history[histIn] = StrSave(cmd);\r
6891   }\r
6892   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6893   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6894   histP = newhp;\r
6895   return history[histP];\r
6896 }\r
6897 \r
6898 char *\r
6899 NextInHistory()\r
6900 {\r
6901   if (histP == histIn) return NULL;\r
6902   histP = (histP + 1) % HISTORY_SIZE;\r
6903   return history[histP];   \r
6904 }\r
6905 \r
6906 HMENU\r
6907 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6908 {\r
6909   HMENU hmenu, h;\r
6910   int i = 0;\r
6911   hmenu = LoadMenu(hInst, "TextMenu");\r
6912   h = GetSubMenu(hmenu, 0);\r
6913   while (e->item) {\r
6914     if (strcmp(e->item, "-") == 0) {\r
6915       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6916     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6917       int flags = MF_STRING, j = 0;\r
6918       if (e->item[0] == '|') {\r
6919         flags |= MF_MENUBARBREAK;\r
6920         j++;\r
6921       }\r
6922       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6923       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6924     }\r
6925     e++;\r
6926     i++;\r
6927   } \r
6928   return hmenu;\r
6929 }\r
6930 \r
6931 WNDPROC consoleTextWindowProc;\r
6932 \r
6933 void\r
6934 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6935 {\r
6936   char buf[MSG_SIZ], name[MSG_SIZ];\r
6937   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6938   CHARRANGE sel;\r
6939 \r
6940   if (!getname) {\r
6941     SetWindowText(hInput, command);\r
6942     if (immediate) {\r
6943       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6944     } else {\r
6945       sel.cpMin = 999999;\r
6946       sel.cpMax = 999999;\r
6947       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6948       SetFocus(hInput);\r
6949     }\r
6950     return;\r
6951   }    \r
6952   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6953   if (sel.cpMin == sel.cpMax) {\r
6954     /* Expand to surrounding word */\r
6955     TEXTRANGE tr;\r
6956     do {\r
6957       tr.chrg.cpMax = sel.cpMin;\r
6958       tr.chrg.cpMin = --sel.cpMin;\r
6959       if (sel.cpMin < 0) break;\r
6960       tr.lpstrText = name;\r
6961       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6962     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6963     sel.cpMin++;\r
6964 \r
6965     do {\r
6966       tr.chrg.cpMin = sel.cpMax;\r
6967       tr.chrg.cpMax = ++sel.cpMax;\r
6968       tr.lpstrText = name;\r
6969       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6970     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6971     sel.cpMax--;\r
6972 \r
6973     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6974       MessageBeep(MB_ICONEXCLAMATION);\r
6975       return;\r
6976     }\r
6977     tr.chrg = sel;\r
6978     tr.lpstrText = name;\r
6979     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6980   } else {\r
6981     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6982       MessageBeep(MB_ICONEXCLAMATION);\r
6983       return;\r
6984     }\r
6985     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6986   }\r
6987   if (immediate) {\r
6988     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6989     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6990     SetWindowText(hInput, buf);\r
6991     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6992   } else {\r
6993     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6994       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6995     SetWindowText(hInput, buf);\r
6996     sel.cpMin = 999999;\r
6997     sel.cpMax = 999999;\r
6998     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6999     SetFocus(hInput);\r
7000   }\r
7001 }\r
7002 \r
7003 LRESULT CALLBACK \r
7004 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7005 {\r
7006   HWND hInput;\r
7007   CHARRANGE sel;\r
7008 \r
7009   switch (message) {\r
7010   case WM_KEYDOWN:\r
7011     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7012     if(wParam=='R') return 0;\r
7013     switch (wParam) {\r
7014     case VK_PRIOR:\r
7015       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7016       return 0;\r
7017     case VK_NEXT:\r
7018       sel.cpMin = 999999;\r
7019       sel.cpMax = 999999;\r
7020       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7021       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7022       return 0;\r
7023     }\r
7024     break;\r
7025   case WM_CHAR:\r
7026    if(wParam != '\022') {\r
7027     if (wParam == '\t') {\r
7028       if (GetKeyState(VK_SHIFT) < 0) {\r
7029         /* shifted */\r
7030         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7031         if (buttonDesc[0].hwnd) {\r
7032           SetFocus(buttonDesc[0].hwnd);\r
7033         } else {\r
7034           SetFocus(hwndMain);\r
7035         }\r
7036       } else {\r
7037         /* unshifted */\r
7038         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7039       }\r
7040     } else {\r
7041       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7042       JAWS_DELETE( SetFocus(hInput); )\r
7043       SendMessage(hInput, message, wParam, lParam);\r
7044     }\r
7045     return 0;\r
7046    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7047    lParam = -1;\r
7048   case WM_RBUTTONDOWN:\r
7049     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7050       /* Move selection here if it was empty */\r
7051       POINT pt;\r
7052       pt.x = LOWORD(lParam);\r
7053       pt.y = HIWORD(lParam);\r
7054       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7055       if (sel.cpMin == sel.cpMax) {\r
7056         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7057         sel.cpMax = sel.cpMin;\r
7058         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7059       }\r
7060       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7061 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7062       POINT pt;\r
7063       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7064       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7065       if (sel.cpMin == sel.cpMax) {\r
7066         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7067         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7068       }\r
7069       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7070         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7071       }\r
7072       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7073       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7074       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7075       MenuPopup(hwnd, pt, hmenu, -1);\r
7076 }\r
7077     }\r
7078     return 0;\r
7079   case WM_RBUTTONUP:\r
7080     if (GetKeyState(VK_SHIFT) & ~1) {\r
7081       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7082         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7083     }\r
7084     return 0;\r
7085   case WM_PASTE:\r
7086     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7087     SetFocus(hInput);\r
7088     return SendMessage(hInput, message, wParam, lParam);\r
7089   case WM_MBUTTONDOWN:\r
7090     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7091   case WM_COMMAND:\r
7092     switch (LOWORD(wParam)) {\r
7093     case IDM_QuickPaste:\r
7094       {\r
7095         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7096         if (sel.cpMin == sel.cpMax) {\r
7097           MessageBeep(MB_ICONEXCLAMATION);\r
7098           return 0;\r
7099         }\r
7100         SendMessage(hwnd, WM_COPY, 0, 0);\r
7101         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7102         SendMessage(hInput, WM_PASTE, 0, 0);\r
7103         SetFocus(hInput);\r
7104         return 0;\r
7105       }\r
7106     case IDM_Cut:\r
7107       SendMessage(hwnd, WM_CUT, 0, 0);\r
7108       return 0;\r
7109     case IDM_Paste:\r
7110       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7111       return 0;\r
7112     case IDM_Copy:\r
7113       SendMessage(hwnd, WM_COPY, 0, 0);\r
7114       return 0;\r
7115     default:\r
7116       {\r
7117         int i = LOWORD(wParam) - IDM_CommandX;\r
7118         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7119             icsTextMenuEntry[i].command != NULL) {\r
7120           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7121                    icsTextMenuEntry[i].getname,\r
7122                    icsTextMenuEntry[i].immediate);\r
7123           return 0;\r
7124         }\r
7125       }\r
7126       break;\r
7127     }\r
7128     break;\r
7129   }\r
7130   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7131 }\r
7132 \r
7133 WNDPROC consoleInputWindowProc;\r
7134 \r
7135 LRESULT CALLBACK\r
7136 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7137 {\r
7138   char buf[MSG_SIZ];\r
7139   char *p;\r
7140   static BOOL sendNextChar = FALSE;\r
7141   static BOOL quoteNextChar = FALSE;\r
7142   InputSource *is = consoleInputSource;\r
7143   CHARFORMAT cf;\r
7144   CHARRANGE sel;\r
7145 \r
7146   switch (message) {\r
7147   case WM_CHAR:\r
7148     if (!appData.localLineEditing || sendNextChar) {\r
7149       is->buf[0] = (CHAR) wParam;\r
7150       is->count = 1;\r
7151       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7152       sendNextChar = FALSE;\r
7153       return 0;\r
7154     }\r
7155     if (quoteNextChar) {\r
7156       buf[0] = (char) wParam;\r
7157       buf[1] = NULLCHAR;\r
7158       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7159       quoteNextChar = FALSE;\r
7160       return 0;\r
7161     }\r
7162     switch (wParam) {\r
7163     case '\r':   /* Enter key */\r
7164       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7165       if (consoleEcho) SaveInHistory(is->buf);\r
7166       is->buf[is->count++] = '\n';\r
7167       is->buf[is->count] = NULLCHAR;\r
7168       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7169       if (consoleEcho) {\r
7170         ConsoleOutput(is->buf, is->count, TRUE);\r
7171       } else if (appData.localLineEditing) {\r
7172         ConsoleOutput("\n", 1, TRUE);\r
7173       }\r
7174       /* fall thru */\r
7175     case '\033': /* Escape key */\r
7176       SetWindowText(hwnd, "");\r
7177       cf.cbSize = sizeof(CHARFORMAT);\r
7178       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7179       if (consoleEcho) {\r
7180         cf.crTextColor = textAttribs[ColorNormal].color;\r
7181       } else {\r
7182         cf.crTextColor = COLOR_ECHOOFF;\r
7183       }\r
7184       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7185       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7186       return 0;\r
7187     case '\t':   /* Tab key */\r
7188       if (GetKeyState(VK_SHIFT) < 0) {\r
7189         /* shifted */\r
7190         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7191       } else {\r
7192         /* unshifted */\r
7193         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7194         if (buttonDesc[0].hwnd) {\r
7195           SetFocus(buttonDesc[0].hwnd);\r
7196         } else {\r
7197           SetFocus(hwndMain);\r
7198         }\r
7199       }\r
7200       return 0;\r
7201     case '\023': /* Ctrl+S */\r
7202       sendNextChar = TRUE;\r
7203       return 0;\r
7204     case '\021': /* Ctrl+Q */\r
7205       quoteNextChar = TRUE;\r
7206       return 0;\r
7207     JAWS_REPLAY\r
7208     default:\r
7209       break;\r
7210     }\r
7211     break;\r
7212   case WM_KEYDOWN:\r
7213     switch (wParam) {\r
7214     case VK_UP:\r
7215       GetWindowText(hwnd, buf, MSG_SIZ);\r
7216       p = PrevInHistory(buf);\r
7217       if (p != NULL) {\r
7218         SetWindowText(hwnd, p);\r
7219         sel.cpMin = 999999;\r
7220         sel.cpMax = 999999;\r
7221         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7222         return 0;\r
7223       }\r
7224       break;\r
7225     case VK_DOWN:\r
7226       p = NextInHistory();\r
7227       if (p != NULL) {\r
7228         SetWindowText(hwnd, p);\r
7229         sel.cpMin = 999999;\r
7230         sel.cpMax = 999999;\r
7231         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7232         return 0;\r
7233       }\r
7234       break;\r
7235     case VK_HOME:\r
7236     case VK_END:\r
7237       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7238       /* fall thru */\r
7239     case VK_PRIOR:\r
7240     case VK_NEXT:\r
7241       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7242       return 0;\r
7243     }\r
7244     break;\r
7245   case WM_MBUTTONDOWN:\r
7246     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7247       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7248     break;\r
7249   case WM_RBUTTONUP:\r
7250     if (GetKeyState(VK_SHIFT) & ~1) {\r
7251       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7252         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7253     } else {\r
7254       POINT pt;\r
7255       HMENU hmenu;\r
7256       hmenu = LoadMenu(hInst, "InputMenu");\r
7257       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7258       if (sel.cpMin == sel.cpMax) {\r
7259         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7260         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7261       }\r
7262       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7263         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7264       }\r
7265       pt.x = LOWORD(lParam);\r
7266       pt.y = HIWORD(lParam);\r
7267       MenuPopup(hwnd, pt, hmenu, -1);\r
7268     }\r
7269     return 0;\r
7270   case WM_COMMAND:\r
7271     switch (LOWORD(wParam)) { \r
7272     case IDM_Undo:\r
7273       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7274       return 0;\r
7275     case IDM_SelectAll:\r
7276       sel.cpMin = 0;\r
7277       sel.cpMax = -1; /*999999?*/\r
7278       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7279       return 0;\r
7280     case IDM_Cut:\r
7281       SendMessage(hwnd, WM_CUT, 0, 0);\r
7282       return 0;\r
7283     case IDM_Paste:\r
7284       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7285       return 0;\r
7286     case IDM_Copy:\r
7287       SendMessage(hwnd, WM_COPY, 0, 0);\r
7288       return 0;\r
7289     }\r
7290     break;\r
7291   }\r
7292   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7293 }\r
7294 \r
7295 #define CO_MAX  100000\r
7296 #define CO_TRIM   1000\r
7297 \r
7298 LRESULT CALLBACK\r
7299 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7300 {\r
7301   static SnapData sd;\r
7302   HWND hText, hInput;\r
7303   RECT rect;\r
7304   static int sizeX, sizeY;\r
7305   int newSizeX, newSizeY;\r
7306   MINMAXINFO *mmi;\r
7307   WORD wMask;\r
7308 \r
7309   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7310   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7311 \r
7312   switch (message) {\r
7313   case WM_NOTIFY:\r
7314     if (((NMHDR*)lParam)->code == EN_LINK)\r
7315     {\r
7316       ENLINK *pLink = (ENLINK*)lParam;\r
7317       if (pLink->msg == WM_LBUTTONUP)\r
7318       {\r
7319         TEXTRANGE tr;\r
7320 \r
7321         tr.chrg = pLink->chrg;\r
7322         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7323         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7324         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7325         free(tr.lpstrText);\r
7326       }\r
7327     }\r
7328     break;\r
7329   case WM_INITDIALOG: /* message: initialize dialog box */\r
7330     hwndConsole = hDlg;\r
7331     SetFocus(hInput);\r
7332     consoleTextWindowProc = (WNDPROC)\r
7333       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7334     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7335     consoleInputWindowProc = (WNDPROC)\r
7336       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7337     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7338     Colorize(ColorNormal, TRUE);\r
7339     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7340     ChangedConsoleFont();\r
7341     GetClientRect(hDlg, &rect);\r
7342     sizeX = rect.right;\r
7343     sizeY = rect.bottom;\r
7344     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7345         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7346       WINDOWPLACEMENT wp;\r
7347       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7348       wp.length = sizeof(WINDOWPLACEMENT);\r
7349       wp.flags = 0;\r
7350       wp.showCmd = SW_SHOW;\r
7351       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7352       wp.rcNormalPosition.left = wpConsole.x;\r
7353       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7354       wp.rcNormalPosition.top = wpConsole.y;\r
7355       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7356       SetWindowPlacement(hDlg, &wp);\r
7357     }\r
7358 \r
7359    // [HGM] Chessknight's change 2004-07-13\r
7360    else { /* Determine Defaults */\r
7361        WINDOWPLACEMENT wp;\r
7362        wpConsole.x = wpMain.width + 1;\r
7363        wpConsole.y = wpMain.y;\r
7364        wpConsole.width = screenWidth -  wpMain.width;\r
7365        wpConsole.height = wpMain.height;\r
7366        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7367        wp.length = sizeof(WINDOWPLACEMENT);\r
7368        wp.flags = 0;\r
7369        wp.showCmd = SW_SHOW;\r
7370        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7371        wp.rcNormalPosition.left = wpConsole.x;\r
7372        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7373        wp.rcNormalPosition.top = wpConsole.y;\r
7374        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7375        SetWindowPlacement(hDlg, &wp);\r
7376     }\r
7377 \r
7378    // Allow hText to highlight URLs and send notifications on them\r
7379    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7380    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7381    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7382    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7383 \r
7384     return FALSE;\r
7385 \r
7386   case WM_SETFOCUS:\r
7387     SetFocus(hInput);\r
7388     return 0;\r
7389 \r
7390   case WM_CLOSE:\r
7391     ExitEvent(0);\r
7392     /* not reached */\r
7393     break;\r
7394 \r
7395   case WM_SIZE:\r
7396     if (IsIconic(hDlg)) break;\r
7397     newSizeX = LOWORD(lParam);\r
7398     newSizeY = HIWORD(lParam);\r
7399     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7400       RECT rectText, rectInput;\r
7401       POINT pt;\r
7402       int newTextHeight, newTextWidth;\r
7403       GetWindowRect(hText, &rectText);\r
7404       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7405       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7406       if (newTextHeight < 0) {\r
7407         newSizeY += -newTextHeight;\r
7408         newTextHeight = 0;\r
7409       }\r
7410       SetWindowPos(hText, NULL, 0, 0,\r
7411         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7412       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7413       pt.x = rectInput.left;\r
7414       pt.y = rectInput.top + newSizeY - sizeY;\r
7415       ScreenToClient(hDlg, &pt);\r
7416       SetWindowPos(hInput, NULL, \r
7417         pt.x, pt.y, /* needs client coords */   \r
7418         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7419         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7420     }\r
7421     sizeX = newSizeX;\r
7422     sizeY = newSizeY;\r
7423     break;\r
7424 \r
7425   case WM_GETMINMAXINFO:\r
7426     /* Prevent resizing window too small */\r
7427     mmi = (MINMAXINFO *) lParam;\r
7428     mmi->ptMinTrackSize.x = 100;\r
7429     mmi->ptMinTrackSize.y = 100;\r
7430     break;\r
7431 \r
7432   /* [AS] Snapping */\r
7433   case WM_ENTERSIZEMOVE:\r
7434     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7435 \r
7436   case WM_SIZING:\r
7437     return OnSizing( &sd, hDlg, wParam, lParam );\r
7438 \r
7439   case WM_MOVING:\r
7440     return OnMoving( &sd, hDlg, wParam, lParam );\r
7441 \r
7442   case WM_EXITSIZEMOVE:\r
7443         UpdateICSWidth(hText);\r
7444     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7445   }\r
7446 \r
7447   return DefWindowProc(hDlg, message, wParam, lParam);\r
7448 }\r
7449 \r
7450 \r
7451 VOID\r
7452 ConsoleCreate()\r
7453 {\r
7454   HWND hCons;\r
7455   if (hwndConsole) return;\r
7456   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7457   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7458 }\r
7459 \r
7460 \r
7461 VOID\r
7462 ConsoleOutput(char* data, int length, int forceVisible)\r
7463 {\r
7464   HWND hText;\r
7465   int trim, exlen;\r
7466   char *p, *q;\r
7467   char buf[CO_MAX+1];\r
7468   POINT pEnd;\r
7469   RECT rect;\r
7470   static int delayLF = 0;\r
7471   CHARRANGE savesel, sel;\r
7472 \r
7473   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7474   p = data;\r
7475   q = buf;\r
7476   if (delayLF) {\r
7477     *q++ = '\r';\r
7478     *q++ = '\n';\r
7479     delayLF = 0;\r
7480   }\r
7481   while (length--) {\r
7482     if (*p == '\n') {\r
7483       if (*++p) {\r
7484         *q++ = '\r';\r
7485         *q++ = '\n';\r
7486       } else {\r
7487         delayLF = 1;\r
7488       }\r
7489     } else if (*p == '\007') {\r
7490        MyPlaySound(&sounds[(int)SoundBell]);\r
7491        p++;\r
7492     } else {\r
7493       *q++ = *p++;\r
7494     }\r
7495   }\r
7496   *q = NULLCHAR;\r
7497   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7498   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7499   /* Save current selection */\r
7500   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7501   exlen = GetWindowTextLength(hText);\r
7502   /* Find out whether current end of text is visible */\r
7503   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7504   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7505   /* Trim existing text if it's too long */\r
7506   if (exlen + (q - buf) > CO_MAX) {\r
7507     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7508     sel.cpMin = 0;\r
7509     sel.cpMax = trim;\r
7510     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7511     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7512     exlen -= trim;\r
7513     savesel.cpMin -= trim;\r
7514     savesel.cpMax -= trim;\r
7515     if (exlen < 0) exlen = 0;\r
7516     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7517     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7518   }\r
7519   /* Append the new text */\r
7520   sel.cpMin = exlen;\r
7521   sel.cpMax = exlen;\r
7522   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7523   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7524   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7525   if (forceVisible || exlen == 0 ||\r
7526       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7527        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7528     /* Scroll to make new end of text visible if old end of text\r
7529        was visible or new text is an echo of user typein */\r
7530     sel.cpMin = 9999999;\r
7531     sel.cpMax = 9999999;\r
7532     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7533     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7534     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7535     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7536   }\r
7537   if (savesel.cpMax == exlen || forceVisible) {\r
7538     /* Move insert point to new end of text if it was at the old\r
7539        end of text or if the new text is an echo of user typein */\r
7540     sel.cpMin = 9999999;\r
7541     sel.cpMax = 9999999;\r
7542     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7543   } else {\r
7544     /* Restore previous selection */\r
7545     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7546   }\r
7547   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7548 }\r
7549 \r
7550 /*---------*/\r
7551 \r
7552 \r
7553 void\r
7554 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7555 {\r
7556   char buf[100];\r
7557   char *str;\r
7558   COLORREF oldFg, oldBg;\r
7559   HFONT oldFont;\r
7560   RECT rect;\r
7561 \r
7562   if(copyNumber > 1)\r
7563     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7564 \r
7565   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7566   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7567   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7568 \r
7569   rect.left = x;\r
7570   rect.right = x + squareSize;\r
7571   rect.top  = y;\r
7572   rect.bottom = y + squareSize;\r
7573   str = buf;\r
7574 \r
7575   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7576                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7577              y, ETO_CLIPPED|ETO_OPAQUE,\r
7578              &rect, str, strlen(str), NULL);\r
7579 \r
7580   (void) SetTextColor(hdc, oldFg);\r
7581   (void) SetBkColor(hdc, oldBg);\r
7582   (void) SelectObject(hdc, oldFont);\r
7583 }\r
7584 \r
7585 void\r
7586 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7587               RECT *rect, char *color, char *flagFell)\r
7588 {\r
7589   char buf[100];\r
7590   char *str;\r
7591   COLORREF oldFg, oldBg;\r
7592   HFONT oldFont;\r
7593 \r
7594   if (twoBoards && partnerUp) return;\r
7595   if (appData.clockMode) {\r
7596     if (tinyLayout)\r
7597       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7598     else\r
7599       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7600     str = buf;\r
7601   } else {\r
7602     str = color;\r
7603   }\r
7604 \r
7605   if (highlight) {\r
7606     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7607     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7608   } else {\r
7609     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7610     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7611   }\r
7612   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7613 \r
7614   JAWS_SILENCE\r
7615 \r
7616   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7617              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7618              rect, str, strlen(str), NULL);\r
7619   if(logoHeight > 0 && appData.clockMode) {\r
7620       RECT r;\r
7621       str += strlen(color)+2;\r
7622       r.top = rect->top + logoHeight/2;\r
7623       r.left = rect->left;\r
7624       r.right = rect->right;\r
7625       r.bottom = rect->bottom;\r
7626       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7627                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7628                  &r, str, strlen(str), NULL);\r
7629   }\r
7630   (void) SetTextColor(hdc, oldFg);\r
7631   (void) SetBkColor(hdc, oldBg);\r
7632   (void) SelectObject(hdc, oldFont);\r
7633 }\r
7634 \r
7635 \r
7636 int\r
7637 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7638            OVERLAPPED *ovl)\r
7639 {\r
7640   int ok, err;\r
7641 \r
7642   /* [AS]  */\r
7643   if( count <= 0 ) {\r
7644     if (appData.debugMode) {\r
7645       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7646     }\r
7647 \r
7648     return ERROR_INVALID_USER_BUFFER;\r
7649   }\r
7650 \r
7651   ResetEvent(ovl->hEvent);\r
7652   ovl->Offset = ovl->OffsetHigh = 0;\r
7653   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7654   if (ok) {\r
7655     err = NO_ERROR;\r
7656   } else {\r
7657     err = GetLastError();\r
7658     if (err == ERROR_IO_PENDING) {\r
7659       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7660       if (ok)\r
7661         err = NO_ERROR;\r
7662       else\r
7663         err = GetLastError();\r
7664     }\r
7665   }\r
7666   return err;\r
7667 }\r
7668 \r
7669 int\r
7670 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7671             OVERLAPPED *ovl)\r
7672 {\r
7673   int ok, err;\r
7674 \r
7675   ResetEvent(ovl->hEvent);\r
7676   ovl->Offset = ovl->OffsetHigh = 0;\r
7677   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7678   if (ok) {\r
7679     err = NO_ERROR;\r
7680   } else {\r
7681     err = GetLastError();\r
7682     if (err == ERROR_IO_PENDING) {\r
7683       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7684       if (ok)\r
7685         err = NO_ERROR;\r
7686       else\r
7687         err = GetLastError();\r
7688     }\r
7689   }\r
7690   return err;\r
7691 }\r
7692 \r
7693 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7694 void CheckForInputBufferFull( InputSource * is )\r
7695 {\r
7696     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7697         /* Look for end of line */\r
7698         char * p = is->buf;\r
7699         \r
7700         while( p < is->next && *p != '\n' ) {\r
7701             p++;\r
7702         }\r
7703 \r
7704         if( p >= is->next ) {\r
7705             if (appData.debugMode) {\r
7706                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7707             }\r
7708 \r
7709             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7710             is->count = (DWORD) -1;\r
7711             is->next = is->buf;\r
7712         }\r
7713     }\r
7714 }\r
7715 \r
7716 DWORD\r
7717 InputThread(LPVOID arg)\r
7718 {\r
7719   InputSource *is;\r
7720   OVERLAPPED ovl;\r
7721 \r
7722   is = (InputSource *) arg;\r
7723   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7724   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7725   while (is->hThread != NULL) {\r
7726     is->error = DoReadFile(is->hFile, is->next,\r
7727                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7728                            &is->count, &ovl);\r
7729     if (is->error == NO_ERROR) {\r
7730       is->next += is->count;\r
7731     } else {\r
7732       if (is->error == ERROR_BROKEN_PIPE) {\r
7733         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7734         is->count = 0;\r
7735       } else {\r
7736         is->count = (DWORD) -1;\r
7737         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7738         break; \r
7739       }\r
7740     }\r
7741 \r
7742     CheckForInputBufferFull( is );\r
7743 \r
7744     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7745 \r
7746     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7747 \r
7748     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7749   }\r
7750 \r
7751   CloseHandle(ovl.hEvent);\r
7752   CloseHandle(is->hFile);\r
7753 \r
7754   if (appData.debugMode) {\r
7755     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7756   }\r
7757 \r
7758   return 0;\r
7759 }\r
7760 \r
7761 \r
7762 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7763 DWORD\r
7764 NonOvlInputThread(LPVOID arg)\r
7765 {\r
7766   InputSource *is;\r
7767   char *p, *q;\r
7768   int i;\r
7769   char prev;\r
7770 \r
7771   is = (InputSource *) arg;\r
7772   while (is->hThread != NULL) {\r
7773     is->error = ReadFile(is->hFile, is->next,\r
7774                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7775                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7776     if (is->error == NO_ERROR) {\r
7777       /* Change CRLF to LF */\r
7778       if (is->next > is->buf) {\r
7779         p = is->next - 1;\r
7780         i = is->count + 1;\r
7781       } else {\r
7782         p = is->next;\r
7783         i = is->count;\r
7784       }\r
7785       q = p;\r
7786       prev = NULLCHAR;\r
7787       while (i > 0) {\r
7788         if (prev == '\r' && *p == '\n') {\r
7789           *(q-1) = '\n';\r
7790           is->count--;\r
7791         } else { \r
7792           *q++ = *p;\r
7793         }\r
7794         prev = *p++;\r
7795         i--;\r
7796       }\r
7797       *q = NULLCHAR;\r
7798       is->next = q;\r
7799     } else {\r
7800       if (is->error == ERROR_BROKEN_PIPE) {\r
7801         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7802         is->count = 0; \r
7803       } else {\r
7804         is->count = (DWORD) -1;\r
7805       }\r
7806     }\r
7807 \r
7808     CheckForInputBufferFull( is );\r
7809 \r
7810     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7811 \r
7812     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7813 \r
7814     if (is->count < 0) break;  /* Quit on error */\r
7815   }\r
7816   CloseHandle(is->hFile);\r
7817   return 0;\r
7818 }\r
7819 \r
7820 DWORD\r
7821 SocketInputThread(LPVOID arg)\r
7822 {\r
7823   InputSource *is;\r
7824 \r
7825   is = (InputSource *) arg;\r
7826   while (is->hThread != NULL) {\r
7827     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7828     if ((int)is->count == SOCKET_ERROR) {\r
7829       is->count = (DWORD) -1;\r
7830       is->error = WSAGetLastError();\r
7831     } else {\r
7832       is->error = NO_ERROR;\r
7833       is->next += is->count;\r
7834       if (is->count == 0 && is->second == is) {\r
7835         /* End of file on stderr; quit with no message */\r
7836         break;\r
7837       }\r
7838     }\r
7839     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7840 \r
7841     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7842 \r
7843     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7844   }\r
7845   return 0;\r
7846 }\r
7847 \r
7848 VOID\r
7849 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7850 {\r
7851   InputSource *is;\r
7852 \r
7853   is = (InputSource *) lParam;\r
7854   if (is->lineByLine) {\r
7855     /* Feed in lines one by one */\r
7856     char *p = is->buf;\r
7857     char *q = p;\r
7858     while (q < is->next) {\r
7859       if (*q++ == '\n') {\r
7860         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7861         p = q;\r
7862       }\r
7863     }\r
7864     \r
7865     /* Move any partial line to the start of the buffer */\r
7866     q = is->buf;\r
7867     while (p < is->next) {\r
7868       *q++ = *p++;\r
7869     }\r
7870     is->next = q;\r
7871 \r
7872     if (is->error != NO_ERROR || is->count == 0) {\r
7873       /* Notify backend of the error.  Note: If there was a partial\r
7874          line at the end, it is not flushed through. */\r
7875       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7876     }\r
7877   } else {\r
7878     /* Feed in the whole chunk of input at once */\r
7879     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7880     is->next = is->buf;\r
7881   }\r
7882 }\r
7883 \r
7884 /*---------------------------------------------------------------------------*\\r
7885  *\r
7886  *  Menu enables. Used when setting various modes.\r
7887  *\r
7888 \*---------------------------------------------------------------------------*/\r
7889 \r
7890 typedef struct {\r
7891   int item;\r
7892   int flags;\r
7893 } Enables;\r
7894 \r
7895 VOID\r
7896 GreyRevert(Boolean grey)\r
7897 { // [HGM] vari: for retracting variations in local mode\r
7898   HMENU hmenu = GetMenu(hwndMain);\r
7899   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7900   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7901 }\r
7902 \r
7903 VOID\r
7904 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7905 {\r
7906   while (enab->item > 0) {\r
7907     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7908     enab++;\r
7909   }\r
7910 }\r
7911 \r
7912 Enables gnuEnables[] = {\r
7913   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7925   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7926 \r
7927   // Needed to switch from ncp to GNU mode on Engine Load\r
7928   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7929   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7930   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7931   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7932   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7934   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7935   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7936   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7937   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7938   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7939   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7940   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7941   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7942   { -1, -1 }\r
7943 };\r
7944 \r
7945 Enables icsEnables[] = {\r
7946   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7954   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7962   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7966   { -1, -1 }\r
7967 };\r
7968 \r
7969 #if ZIPPY\r
7970 Enables zippyEnables[] = {\r
7971   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7972   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7973   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7974   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7975   { -1, -1 }\r
7976 };\r
7977 #endif\r
7978 \r
7979 Enables ncpEnables[] = {\r
7980   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7981   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7986   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7987   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7988   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7989   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7990   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7991   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7992   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7993   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7994   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7995   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8001   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8002   { -1, -1 }\r
8003 };\r
8004 \r
8005 Enables trainingOnEnables[] = {\r
8006   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8015   { -1, -1 }\r
8016 };\r
8017 \r
8018 Enables trainingOffEnables[] = {\r
8019   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8020   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8021   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8022   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8023   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8024   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8025   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8026   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8027   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8028   { -1, -1 }\r
8029 };\r
8030 \r
8031 /* These modify either ncpEnables or gnuEnables */\r
8032 Enables cmailEnables[] = {\r
8033   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8034   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8035   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8036   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8040   { -1, -1 }\r
8041 };\r
8042 \r
8043 Enables machineThinkingEnables[] = {\r
8044   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8045   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8046   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8047   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8048   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8050   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8051   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8052   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8053   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8054   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8055   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8056   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8057 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8058   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8059   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8060   { -1, -1 }\r
8061 };\r
8062 \r
8063 Enables userThinkingEnables[] = {\r
8064   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8065   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8066   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8067   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8068   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8069   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8070   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8071   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8072   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8073   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8074   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8075   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8076   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8077 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8078   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8079   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8080   { -1, -1 }\r
8081 };\r
8082 \r
8083 /*---------------------------------------------------------------------------*\\r
8084  *\r
8085  *  Front-end interface functions exported by XBoard.\r
8086  *  Functions appear in same order as prototypes in frontend.h.\r
8087  * \r
8088 \*---------------------------------------------------------------------------*/\r
8089 VOID\r
8090 CheckMark(UINT item, int state)\r
8091 {\r
8092     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8093 }\r
8094 \r
8095 VOID\r
8096 ModeHighlight()\r
8097 {\r
8098   static UINT prevChecked = 0;\r
8099   static int prevPausing = 0;\r
8100   UINT nowChecked;\r
8101 \r
8102   if (pausing != prevPausing) {\r
8103     prevPausing = pausing;\r
8104     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8105                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8106     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8107   }\r
8108 \r
8109   switch (gameMode) {\r
8110   case BeginningOfGame:\r
8111     if (appData.icsActive)\r
8112       nowChecked = IDM_IcsClient;\r
8113     else if (appData.noChessProgram)\r
8114       nowChecked = IDM_EditGame;\r
8115     else\r
8116       nowChecked = IDM_MachineBlack;\r
8117     break;\r
8118   case MachinePlaysBlack:\r
8119     nowChecked = IDM_MachineBlack;\r
8120     break;\r
8121   case MachinePlaysWhite:\r
8122     nowChecked = IDM_MachineWhite;\r
8123     break;\r
8124   case TwoMachinesPlay:\r
8125     nowChecked = IDM_TwoMachines;\r
8126     break;\r
8127   case AnalyzeMode:\r
8128     nowChecked = IDM_AnalysisMode;\r
8129     break;\r
8130   case AnalyzeFile:\r
8131     nowChecked = IDM_AnalyzeFile;\r
8132     break;\r
8133   case EditGame:\r
8134     nowChecked = IDM_EditGame;\r
8135     break;\r
8136   case PlayFromGameFile:\r
8137     nowChecked = IDM_LoadGame;\r
8138     break;\r
8139   case EditPosition:\r
8140     nowChecked = IDM_EditPosition;\r
8141     break;\r
8142   case Training:\r
8143     nowChecked = IDM_Training;\r
8144     break;\r
8145   case IcsPlayingWhite:\r
8146   case IcsPlayingBlack:\r
8147   case IcsObserving:\r
8148   case IcsIdle:\r
8149     nowChecked = IDM_IcsClient;\r
8150     break;\r
8151   default:\r
8152   case EndOfGame:\r
8153     nowChecked = 0;\r
8154     break;\r
8155   }\r
8156   CheckMark(prevChecked, MF_UNCHECKED);\r
8157   CheckMark(nowChecked, MF_CHECKED);\r
8158   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8159 \r
8160   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8161     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8162                           MF_BYCOMMAND|MF_ENABLED);\r
8163   } else {\r
8164     (void) EnableMenuItem(GetMenu(hwndMain), \r
8165                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8166   }\r
8167 \r
8168   prevChecked = nowChecked;\r
8169 \r
8170   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8171   if (appData.icsActive) {\r
8172        if (appData.icsEngineAnalyze) {\r
8173                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8174        } else {\r
8175                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8176        }\r
8177   }\r
8178   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8179 }\r
8180 \r
8181 VOID\r
8182 SetICSMode()\r
8183 {\r
8184   HMENU hmenu = GetMenu(hwndMain);\r
8185   SetMenuEnables(hmenu, icsEnables);\r
8186   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8187     MF_BYCOMMAND|MF_ENABLED);\r
8188 #if ZIPPY\r
8189   if (appData.zippyPlay) {\r
8190     SetMenuEnables(hmenu, zippyEnables);\r
8191     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8192          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8193           MF_BYCOMMAND|MF_ENABLED);\r
8194   }\r
8195 #endif\r
8196 }\r
8197 \r
8198 VOID\r
8199 SetGNUMode()\r
8200 {\r
8201   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8202 }\r
8203 \r
8204 VOID\r
8205 SetNCPMode()\r
8206 {\r
8207   HMENU hmenu = GetMenu(hwndMain);\r
8208   SetMenuEnables(hmenu, ncpEnables);\r
8209     DrawMenuBar(hwndMain);\r
8210 }\r
8211 \r
8212 VOID\r
8213 SetCmailMode()\r
8214 {\r
8215   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8216 }\r
8217 \r
8218 VOID \r
8219 SetTrainingModeOn()\r
8220 {\r
8221   int i;\r
8222   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8223   for (i = 0; i < N_BUTTONS; i++) {\r
8224     if (buttonDesc[i].hwnd != NULL)\r
8225       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8226   }\r
8227   CommentPopDown();\r
8228 }\r
8229 \r
8230 VOID SetTrainingModeOff()\r
8231 {\r
8232   int i;\r
8233   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8234   for (i = 0; i < N_BUTTONS; i++) {\r
8235     if (buttonDesc[i].hwnd != NULL)\r
8236       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8237   }\r
8238 }\r
8239 \r
8240 \r
8241 VOID\r
8242 SetUserThinkingEnables()\r
8243 {\r
8244   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8245 }\r
8246 \r
8247 VOID\r
8248 SetMachineThinkingEnables()\r
8249 {\r
8250   HMENU hMenu = GetMenu(hwndMain);\r
8251   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8252 \r
8253   SetMenuEnables(hMenu, machineThinkingEnables);\r
8254 \r
8255   if (gameMode == MachinePlaysBlack) {\r
8256     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8257   } else if (gameMode == MachinePlaysWhite) {\r
8258     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8259   } else if (gameMode == TwoMachinesPlay) {\r
8260     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8261   }\r
8262 }\r
8263 \r
8264 \r
8265 VOID\r
8266 DisplayTitle(char *str)\r
8267 {\r
8268   char title[MSG_SIZ], *host;\r
8269   if (str[0] != NULLCHAR) {\r
8270     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8271   } else if (appData.icsActive) {\r
8272     if (appData.icsCommPort[0] != NULLCHAR)\r
8273       host = "ICS";\r
8274     else \r
8275       host = appData.icsHost;\r
8276       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8277   } else if (appData.noChessProgram) {\r
8278     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8279   } else {\r
8280     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8281     strcat(title, ": ");\r
8282     strcat(title, first.tidy);\r
8283   }\r
8284   SetWindowText(hwndMain, title);\r
8285 }\r
8286 \r
8287 \r
8288 VOID\r
8289 DisplayMessage(char *str1, char *str2)\r
8290 {\r
8291   HDC hdc;\r
8292   HFONT oldFont;\r
8293   int remain = MESSAGE_TEXT_MAX - 1;\r
8294   int len;\r
8295 \r
8296   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8297   messageText[0] = NULLCHAR;\r
8298   if (*str1) {\r
8299     len = strlen(str1);\r
8300     if (len > remain) len = remain;\r
8301     strncpy(messageText, str1, len);\r
8302     messageText[len] = NULLCHAR;\r
8303     remain -= len;\r
8304   }\r
8305   if (*str2 && remain >= 2) {\r
8306     if (*str1) {\r
8307       strcat(messageText, "  ");\r
8308       remain -= 2;\r
8309     }\r
8310     len = strlen(str2);\r
8311     if (len > remain) len = remain;\r
8312     strncat(messageText, str2, len);\r
8313   }\r
8314   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8315   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8316 \r
8317   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8318 \r
8319   SAYMACHINEMOVE();\r
8320 \r
8321   hdc = GetDC(hwndMain);\r
8322   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8323   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8324              &messageRect, messageText, strlen(messageText), NULL);\r
8325   (void) SelectObject(hdc, oldFont);\r
8326   (void) ReleaseDC(hwndMain, hdc);\r
8327 }\r
8328 \r
8329 VOID\r
8330 DisplayError(char *str, int error)\r
8331 {\r
8332   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8333   int len;\r
8334 \r
8335   if (error == 0) {\r
8336     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8337   } else {\r
8338     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8339                         NULL, error, LANG_NEUTRAL,\r
8340                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8341     if (len > 0) {\r
8342       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8343     } else {\r
8344       ErrorMap *em = errmap;\r
8345       while (em->err != 0 && em->err != error) em++;\r
8346       if (em->err != 0) {\r
8347         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8348       } else {\r
8349         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8350       }\r
8351     }\r
8352   }\r
8353   \r
8354   ErrorPopUp(_("Error"), buf);\r
8355 }\r
8356 \r
8357 \r
8358 VOID\r
8359 DisplayMoveError(char *str)\r
8360 {\r
8361   fromX = fromY = -1;\r
8362   ClearHighlights();\r
8363   DrawPosition(FALSE, NULL);\r
8364   if (appData.popupMoveErrors) {\r
8365     ErrorPopUp(_("Error"), str);\r
8366   } else {\r
8367     DisplayMessage(str, "");\r
8368     moveErrorMessageUp = TRUE;\r
8369   }\r
8370 }\r
8371 \r
8372 VOID\r
8373 DisplayFatalError(char *str, int error, int exitStatus)\r
8374 {\r
8375   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8376   int len;\r
8377   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8378 \r
8379   if (error != 0) {\r
8380     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8381                         NULL, error, LANG_NEUTRAL,\r
8382                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8383     if (len > 0) {\r
8384       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8385     } else {\r
8386       ErrorMap *em = errmap;\r
8387       while (em->err != 0 && em->err != error) em++;\r
8388       if (em->err != 0) {\r
8389         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8390       } else {\r
8391         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8392       }\r
8393     }\r
8394     str = buf;\r
8395   }\r
8396   if (appData.debugMode) {\r
8397     fprintf(debugFP, "%s: %s\n", label, str);\r
8398   }\r
8399   if (appData.popupExitMessage) {\r
8400     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8401                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8402   }\r
8403   ExitEvent(exitStatus);\r
8404 }\r
8405 \r
8406 \r
8407 VOID\r
8408 DisplayInformation(char *str)\r
8409 {\r
8410   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8411 }\r
8412 \r
8413 \r
8414 VOID\r
8415 DisplayNote(char *str)\r
8416 {\r
8417   ErrorPopUp(_("Note"), str);\r
8418 }\r
8419 \r
8420 \r
8421 typedef struct {\r
8422   char *title, *question, *replyPrefix;\r
8423   ProcRef pr;\r
8424 } QuestionParams;\r
8425 \r
8426 LRESULT CALLBACK\r
8427 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8428 {\r
8429   static QuestionParams *qp;\r
8430   char reply[MSG_SIZ];\r
8431   int len, err;\r
8432 \r
8433   switch (message) {\r
8434   case WM_INITDIALOG:\r
8435     qp = (QuestionParams *) lParam;\r
8436     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8437     Translate(hDlg, DLG_Question);\r
8438     SetWindowText(hDlg, qp->title);\r
8439     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8440     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8441     return FALSE;\r
8442 \r
8443   case WM_COMMAND:\r
8444     switch (LOWORD(wParam)) {\r
8445     case IDOK:\r
8446       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8447       if (*reply) strcat(reply, " ");\r
8448       len = strlen(reply);\r
8449       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8450       strcat(reply, "\n");\r
8451       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8452       EndDialog(hDlg, TRUE);\r
8453       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8454       return TRUE;\r
8455     case IDCANCEL:\r
8456       EndDialog(hDlg, FALSE);\r
8457       return TRUE;\r
8458     default:\r
8459       break;\r
8460     }\r
8461     break;\r
8462   }\r
8463   return FALSE;\r
8464 }\r
8465 \r
8466 VOID\r
8467 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8468 {\r
8469     QuestionParams qp;\r
8470     FARPROC lpProc;\r
8471     \r
8472     qp.title = title;\r
8473     qp.question = question;\r
8474     qp.replyPrefix = replyPrefix;\r
8475     qp.pr = pr;\r
8476     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8477     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8478       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8479     FreeProcInstance(lpProc);\r
8480 }\r
8481 \r
8482 /* [AS] Pick FRC position */\r
8483 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8484 {\r
8485     static int * lpIndexFRC;\r
8486     BOOL index_is_ok;\r
8487     char buf[16];\r
8488 \r
8489     switch( message )\r
8490     {\r
8491     case WM_INITDIALOG:\r
8492         lpIndexFRC = (int *) lParam;\r
8493 \r
8494         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8495         Translate(hDlg, DLG_NewGameFRC);\r
8496 \r
8497         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8498         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8499         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8500         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8501 \r
8502         break;\r
8503 \r
8504     case WM_COMMAND:\r
8505         switch( LOWORD(wParam) ) {\r
8506         case IDOK:\r
8507             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8508             EndDialog( hDlg, 0 );\r
8509             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8510             return TRUE;\r
8511         case IDCANCEL:\r
8512             EndDialog( hDlg, 1 );   \r
8513             return TRUE;\r
8514         case IDC_NFG_Edit:\r
8515             if( HIWORD(wParam) == EN_CHANGE ) {\r
8516                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8517 \r
8518                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8519             }\r
8520             return TRUE;\r
8521         case IDC_NFG_Random:\r
8522           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8523             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8524             return TRUE;\r
8525         }\r
8526 \r
8527         break;\r
8528     }\r
8529 \r
8530     return FALSE;\r
8531 }\r
8532 \r
8533 int NewGameFRC()\r
8534 {\r
8535     int result;\r
8536     int index = appData.defaultFrcPosition;\r
8537     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8538 \r
8539     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8540 \r
8541     if( result == 0 ) {\r
8542         appData.defaultFrcPosition = index;\r
8543     }\r
8544 \r
8545     return result;\r
8546 }\r
8547 \r
8548 /* [AS] Game list options. Refactored by HGM */\r
8549 \r
8550 HWND gameListOptionsDialog;\r
8551 \r
8552 // low-level front-end: clear text edit / list widget\r
8553 void\r
8554 GLT_ClearList()\r
8555 {\r
8556     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8557 }\r
8558 \r
8559 // low-level front-end: clear text edit / list widget\r
8560 void\r
8561 GLT_DeSelectList()\r
8562 {\r
8563     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8564 }\r
8565 \r
8566 // low-level front-end: append line to text edit / list widget\r
8567 void\r
8568 GLT_AddToList( char *name )\r
8569 {\r
8570     if( name != 0 ) {\r
8571             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8572     }\r
8573 }\r
8574 \r
8575 // low-level front-end: get line from text edit / list widget\r
8576 Boolean\r
8577 GLT_GetFromList( int index, char *name )\r
8578 {\r
8579     if( name != 0 ) {\r
8580             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8581                 return TRUE;\r
8582     }\r
8583     return FALSE;\r
8584 }\r
8585 \r
8586 void GLT_MoveSelection( HWND hDlg, int delta )\r
8587 {\r
8588     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8589     int idx2 = idx1 + delta;\r
8590     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8591 \r
8592     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8593         char buf[128];\r
8594 \r
8595         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8596         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8597         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8598         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8599     }\r
8600 }\r
8601 \r
8602 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8603 {\r
8604     switch( message )\r
8605     {\r
8606     case WM_INITDIALOG:\r
8607         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8608         \r
8609         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8610         Translate(hDlg, DLG_GameListOptions);\r
8611 \r
8612         /* Initialize list */\r
8613         GLT_TagsToList( lpUserGLT );\r
8614 \r
8615         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8616 \r
8617         break;\r
8618 \r
8619     case WM_COMMAND:\r
8620         switch( LOWORD(wParam) ) {\r
8621         case IDOK:\r
8622             GLT_ParseList();\r
8623             EndDialog( hDlg, 0 );\r
8624             return TRUE;\r
8625         case IDCANCEL:\r
8626             EndDialog( hDlg, 1 );\r
8627             return TRUE;\r
8628 \r
8629         case IDC_GLT_Default:\r
8630             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8631             return TRUE;\r
8632 \r
8633         case IDC_GLT_Restore:\r
8634             GLT_TagsToList( appData.gameListTags );\r
8635             return TRUE;\r
8636 \r
8637         case IDC_GLT_Up:\r
8638             GLT_MoveSelection( hDlg, -1 );\r
8639             return TRUE;\r
8640 \r
8641         case IDC_GLT_Down:\r
8642             GLT_MoveSelection( hDlg, +1 );\r
8643             return TRUE;\r
8644         }\r
8645 \r
8646         break;\r
8647     }\r
8648 \r
8649     return FALSE;\r
8650 }\r
8651 \r
8652 int GameListOptions()\r
8653 {\r
8654     int result;\r
8655     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8656 \r
8657       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8658 \r
8659     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8660 \r
8661     if( result == 0 ) {\r
8662         /* [AS] Memory leak here! */\r
8663         appData.gameListTags = strdup( lpUserGLT ); \r
8664     }\r
8665 \r
8666     return result;\r
8667 }\r
8668 \r
8669 VOID\r
8670 DisplayIcsInteractionTitle(char *str)\r
8671 {\r
8672   char consoleTitle[MSG_SIZ];\r
8673 \r
8674     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8675     SetWindowText(hwndConsole, consoleTitle);\r
8676 \r
8677     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8678       char buf[MSG_SIZ], *p = buf, *q;\r
8679         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8680       do {\r
8681         q = strchr(p, ';');\r
8682         if(q) *q++ = 0;\r
8683         if(*p) ChatPopUp(p);\r
8684       } while(p=q);\r
8685     }\r
8686 \r
8687     SetActiveWindow(hwndMain);\r
8688 }\r
8689 \r
8690 void\r
8691 DrawPosition(int fullRedraw, Board board)\r
8692 {\r
8693   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8694 }\r
8695 \r
8696 void NotifyFrontendLogin()\r
8697 {\r
8698         if (hwndConsole)\r
8699                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8700 }\r
8701 \r
8702 VOID\r
8703 ResetFrontEnd()\r
8704 {\r
8705   fromX = fromY = -1;\r
8706   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8707     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8708     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8709     dragInfo.lastpos = dragInfo.pos;\r
8710     dragInfo.start.x = dragInfo.start.y = -1;\r
8711     dragInfo.from = dragInfo.start;\r
8712     ReleaseCapture();\r
8713     DrawPosition(TRUE, NULL);\r
8714   }\r
8715   TagsPopDown();\r
8716 }\r
8717 \r
8718 \r
8719 VOID\r
8720 CommentPopUp(char *title, char *str)\r
8721 {\r
8722   HWND hwnd = GetActiveWindow();\r
8723   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8724   SAY(str);\r
8725   SetActiveWindow(hwnd);\r
8726 }\r
8727 \r
8728 VOID\r
8729 CommentPopDown(void)\r
8730 {\r
8731   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8732   if (commentDialog) {\r
8733     ShowWindow(commentDialog, SW_HIDE);\r
8734   }\r
8735   commentUp = FALSE;\r
8736 }\r
8737 \r
8738 VOID\r
8739 EditCommentPopUp(int index, char *title, char *str)\r
8740 {\r
8741   EitherCommentPopUp(index, title, str, TRUE);\r
8742 }\r
8743 \r
8744 \r
8745 VOID\r
8746 RingBell()\r
8747 {\r
8748   MyPlaySound(&sounds[(int)SoundMove]);\r
8749 }\r
8750 \r
8751 VOID PlayIcsWinSound()\r
8752 {\r
8753   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8754 }\r
8755 \r
8756 VOID PlayIcsLossSound()\r
8757 {\r
8758   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8759 }\r
8760 \r
8761 VOID PlayIcsDrawSound()\r
8762 {\r
8763   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8764 }\r
8765 \r
8766 VOID PlayIcsUnfinishedSound()\r
8767 {\r
8768   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8769 }\r
8770 \r
8771 VOID\r
8772 PlayAlarmSound()\r
8773 {\r
8774   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8775 }\r
8776 \r
8777 VOID\r
8778 PlayTellSound()\r
8779 {\r
8780   MyPlaySound(&textAttribs[ColorTell].sound);\r
8781 }\r
8782 \r
8783 \r
8784 VOID\r
8785 EchoOn()\r
8786 {\r
8787   HWND hInput;\r
8788   consoleEcho = TRUE;\r
8789   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8790   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8791   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8792 }\r
8793 \r
8794 \r
8795 VOID\r
8796 EchoOff()\r
8797 {\r
8798   CHARFORMAT cf;\r
8799   HWND hInput;\r
8800   consoleEcho = FALSE;\r
8801   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8802   /* This works OK: set text and background both to the same color */\r
8803   cf = consoleCF;\r
8804   cf.crTextColor = COLOR_ECHOOFF;\r
8805   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8806   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8807 }\r
8808 \r
8809 /* No Raw()...? */\r
8810 \r
8811 void Colorize(ColorClass cc, int continuation)\r
8812 {\r
8813   currentColorClass = cc;\r
8814   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8815   consoleCF.crTextColor = textAttribs[cc].color;\r
8816   consoleCF.dwEffects = textAttribs[cc].effects;\r
8817   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8818 }\r
8819 \r
8820 char *\r
8821 UserName()\r
8822 {\r
8823   static char buf[MSG_SIZ];\r
8824   DWORD bufsiz = MSG_SIZ;\r
8825 \r
8826   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8827         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8828   }\r
8829   if (!GetUserName(buf, &bufsiz)) {\r
8830     /*DisplayError("Error getting user name", GetLastError());*/\r
8831     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8832   }\r
8833   return buf;\r
8834 }\r
8835 \r
8836 char *\r
8837 HostName()\r
8838 {\r
8839   static char buf[MSG_SIZ];\r
8840   DWORD bufsiz = MSG_SIZ;\r
8841 \r
8842   if (!GetComputerName(buf, &bufsiz)) {\r
8843     /*DisplayError("Error getting host name", GetLastError());*/\r
8844     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8845   }\r
8846   return buf;\r
8847 }\r
8848 \r
8849 \r
8850 int\r
8851 ClockTimerRunning()\r
8852 {\r
8853   return clockTimerEvent != 0;\r
8854 }\r
8855 \r
8856 int\r
8857 StopClockTimer()\r
8858 {\r
8859   if (clockTimerEvent == 0) return FALSE;\r
8860   KillTimer(hwndMain, clockTimerEvent);\r
8861   clockTimerEvent = 0;\r
8862   return TRUE;\r
8863 }\r
8864 \r
8865 void\r
8866 StartClockTimer(long millisec)\r
8867 {\r
8868   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8869                              (UINT) millisec, NULL);\r
8870 }\r
8871 \r
8872 void\r
8873 DisplayWhiteClock(long timeRemaining, int highlight)\r
8874 {\r
8875   HDC hdc;\r
8876   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8877 \r
8878   if(appData.noGUI) return;\r
8879   hdc = GetDC(hwndMain);\r
8880   if (!IsIconic(hwndMain)) {\r
8881     DisplayAClock(hdc, timeRemaining, highlight, \r
8882                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8883   }\r
8884   if (highlight && iconCurrent == iconBlack) {\r
8885     iconCurrent = iconWhite;\r
8886     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8887     if (IsIconic(hwndMain)) {\r
8888       DrawIcon(hdc, 2, 2, iconCurrent);\r
8889     }\r
8890   }\r
8891   (void) ReleaseDC(hwndMain, hdc);\r
8892   if (hwndConsole)\r
8893     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8894 }\r
8895 \r
8896 void\r
8897 DisplayBlackClock(long timeRemaining, int highlight)\r
8898 {\r
8899   HDC hdc;\r
8900   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8901 \r
8902   if(appData.noGUI) return;\r
8903   hdc = GetDC(hwndMain);\r
8904   if (!IsIconic(hwndMain)) {\r
8905     DisplayAClock(hdc, timeRemaining, highlight, \r
8906                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8907   }\r
8908   if (highlight && iconCurrent == iconWhite) {\r
8909     iconCurrent = iconBlack;\r
8910     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8911     if (IsIconic(hwndMain)) {\r
8912       DrawIcon(hdc, 2, 2, iconCurrent);\r
8913     }\r
8914   }\r
8915   (void) ReleaseDC(hwndMain, hdc);\r
8916   if (hwndConsole)\r
8917     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8918 }\r
8919 \r
8920 \r
8921 int\r
8922 LoadGameTimerRunning()\r
8923 {\r
8924   return loadGameTimerEvent != 0;\r
8925 }\r
8926 \r
8927 int\r
8928 StopLoadGameTimer()\r
8929 {\r
8930   if (loadGameTimerEvent == 0) return FALSE;\r
8931   KillTimer(hwndMain, loadGameTimerEvent);\r
8932   loadGameTimerEvent = 0;\r
8933   return TRUE;\r
8934 }\r
8935 \r
8936 void\r
8937 StartLoadGameTimer(long millisec)\r
8938 {\r
8939   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8940                                 (UINT) millisec, NULL);\r
8941 }\r
8942 \r
8943 void\r
8944 AutoSaveGame()\r
8945 {\r
8946   char *defName;\r
8947   FILE *f;\r
8948   char fileTitle[MSG_SIZ];\r
8949 \r
8950   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8951   f = OpenFileDialog(hwndMain, "a", defName,\r
8952                      appData.oldSaveStyle ? "gam" : "pgn",\r
8953                      GAME_FILT, \r
8954                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8955   if (f != NULL) {\r
8956     SaveGame(f, 0, "");\r
8957     fclose(f);\r
8958   }\r
8959 }\r
8960 \r
8961 \r
8962 void\r
8963 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8964 {\r
8965   if (delayedTimerEvent != 0) {\r
8966     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8967       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8968     }\r
8969     KillTimer(hwndMain, delayedTimerEvent);\r
8970     delayedTimerEvent = 0;\r
8971     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8972     delayedTimerCallback();\r
8973   }\r
8974   delayedTimerCallback = cb;\r
8975   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8976                                 (UINT) millisec, NULL);\r
8977 }\r
8978 \r
8979 DelayedEventCallback\r
8980 GetDelayedEvent()\r
8981 {\r
8982   if (delayedTimerEvent) {\r
8983     return delayedTimerCallback;\r
8984   } else {\r
8985     return NULL;\r
8986   }\r
8987 }\r
8988 \r
8989 void\r
8990 CancelDelayedEvent()\r
8991 {\r
8992   if (delayedTimerEvent) {\r
8993     KillTimer(hwndMain, delayedTimerEvent);\r
8994     delayedTimerEvent = 0;\r
8995   }\r
8996 }\r
8997 \r
8998 DWORD GetWin32Priority(int nice)\r
8999 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9000 /*\r
9001 REALTIME_PRIORITY_CLASS     0x00000100\r
9002 HIGH_PRIORITY_CLASS         0x00000080\r
9003 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9004 NORMAL_PRIORITY_CLASS       0x00000020\r
9005 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9006 IDLE_PRIORITY_CLASS         0x00000040\r
9007 */\r
9008         if (nice < -15) return 0x00000080;\r
9009         if (nice < 0)   return 0x00008000;\r
9010         if (nice == 0)  return 0x00000020;\r
9011         if (nice < 15)  return 0x00004000;\r
9012         return 0x00000040;\r
9013 }\r
9014 \r
9015 void RunCommand(char *cmdLine)\r
9016 {\r
9017   /* Now create the child process. */\r
9018   STARTUPINFO siStartInfo;\r
9019   PROCESS_INFORMATION piProcInfo;\r
9020 \r
9021   siStartInfo.cb = sizeof(STARTUPINFO);\r
9022   siStartInfo.lpReserved = NULL;\r
9023   siStartInfo.lpDesktop = NULL;\r
9024   siStartInfo.lpTitle = NULL;\r
9025   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9026   siStartInfo.cbReserved2 = 0;\r
9027   siStartInfo.lpReserved2 = NULL;\r
9028   siStartInfo.hStdInput = NULL;\r
9029   siStartInfo.hStdOutput = NULL;\r
9030   siStartInfo.hStdError = NULL;\r
9031 \r
9032   CreateProcess(NULL,\r
9033                 cmdLine,           /* command line */\r
9034                 NULL,      /* process security attributes */\r
9035                 NULL,      /* primary thread security attrs */\r
9036                 TRUE,      /* handles are inherited */\r
9037                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9038                 NULL,      /* use parent's environment */\r
9039                 NULL,\r
9040                 &siStartInfo, /* STARTUPINFO pointer */\r
9041                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9042 \r
9043   CloseHandle(piProcInfo.hThread);\r
9044 }\r
9045 \r
9046 /* Start a child process running the given program.\r
9047    The process's standard output can be read from "from", and its\r
9048    standard input can be written to "to".\r
9049    Exit with fatal error if anything goes wrong.\r
9050    Returns an opaque pointer that can be used to destroy the process\r
9051    later.\r
9052 */\r
9053 int\r
9054 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9055 {\r
9056 #define BUFSIZE 4096\r
9057 \r
9058   HANDLE hChildStdinRd, hChildStdinWr,\r
9059     hChildStdoutRd, hChildStdoutWr;\r
9060   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9061   SECURITY_ATTRIBUTES saAttr;\r
9062   BOOL fSuccess;\r
9063   PROCESS_INFORMATION piProcInfo;\r
9064   STARTUPINFO siStartInfo;\r
9065   ChildProc *cp;\r
9066   char buf[MSG_SIZ];\r
9067   DWORD err;\r
9068 \r
9069   if (appData.debugMode) {\r
9070     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9071   }\r
9072 \r
9073   *pr = NoProc;\r
9074 \r
9075   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9076   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9077   saAttr.bInheritHandle = TRUE;\r
9078   saAttr.lpSecurityDescriptor = NULL;\r
9079 \r
9080   /*\r
9081    * The steps for redirecting child's STDOUT:\r
9082    *     1. Create anonymous pipe to be STDOUT for child.\r
9083    *     2. Create a noninheritable duplicate of read handle,\r
9084    *         and close the inheritable read handle.\r
9085    */\r
9086 \r
9087   /* Create a pipe for the child's STDOUT. */\r
9088   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9089     return GetLastError();\r
9090   }\r
9091 \r
9092   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9093   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9094                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9095                              FALSE,     /* not inherited */\r
9096                              DUPLICATE_SAME_ACCESS);\r
9097   if (! fSuccess) {\r
9098     return GetLastError();\r
9099   }\r
9100   CloseHandle(hChildStdoutRd);\r
9101 \r
9102   /*\r
9103    * The steps for redirecting child's STDIN:\r
9104    *     1. Create anonymous pipe to be STDIN for child.\r
9105    *     2. Create a noninheritable duplicate of write handle,\r
9106    *         and close the inheritable write handle.\r
9107    */\r
9108 \r
9109   /* Create a pipe for the child's STDIN. */\r
9110   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9111     return GetLastError();\r
9112   }\r
9113 \r
9114   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9115   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9116                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9117                              FALSE,     /* not inherited */\r
9118                              DUPLICATE_SAME_ACCESS);\r
9119   if (! fSuccess) {\r
9120     return GetLastError();\r
9121   }\r
9122   CloseHandle(hChildStdinWr);\r
9123 \r
9124   /* Arrange to (1) look in dir for the child .exe file, and\r
9125    * (2) have dir be the child's working directory.  Interpret\r
9126    * dir relative to the directory WinBoard loaded from. */\r
9127   GetCurrentDirectory(MSG_SIZ, buf);\r
9128   SetCurrentDirectory(installDir);\r
9129   SetCurrentDirectory(dir);\r
9130 \r
9131   /* Now create the child process. */\r
9132 \r
9133   siStartInfo.cb = sizeof(STARTUPINFO);\r
9134   siStartInfo.lpReserved = NULL;\r
9135   siStartInfo.lpDesktop = NULL;\r
9136   siStartInfo.lpTitle = NULL;\r
9137   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9138   siStartInfo.cbReserved2 = 0;\r
9139   siStartInfo.lpReserved2 = NULL;\r
9140   siStartInfo.hStdInput = hChildStdinRd;\r
9141   siStartInfo.hStdOutput = hChildStdoutWr;\r
9142   siStartInfo.hStdError = hChildStdoutWr;\r
9143 \r
9144   fSuccess = CreateProcess(NULL,\r
9145                            cmdLine,        /* command line */\r
9146                            NULL,           /* process security attributes */\r
9147                            NULL,           /* primary thread security attrs */\r
9148                            TRUE,           /* handles are inherited */\r
9149                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9150                            NULL,           /* use parent's environment */\r
9151                            NULL,\r
9152                            &siStartInfo, /* STARTUPINFO pointer */\r
9153                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9154 \r
9155   err = GetLastError();\r
9156   SetCurrentDirectory(buf); /* return to prev directory */\r
9157   if (! fSuccess) {\r
9158     return err;\r
9159   }\r
9160 \r
9161   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9162     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9163     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9164   }\r
9165 \r
9166   /* Close the handles we don't need in the parent */\r
9167   CloseHandle(piProcInfo.hThread);\r
9168   CloseHandle(hChildStdinRd);\r
9169   CloseHandle(hChildStdoutWr);\r
9170 \r
9171   /* Prepare return value */\r
9172   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9173   cp->kind = CPReal;\r
9174   cp->hProcess = piProcInfo.hProcess;\r
9175   cp->pid = piProcInfo.dwProcessId;\r
9176   cp->hFrom = hChildStdoutRdDup;\r
9177   cp->hTo = hChildStdinWrDup;\r
9178 \r
9179   *pr = (void *) cp;\r
9180 \r
9181   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9182      2000 where engines sometimes don't see the initial command(s)\r
9183      from WinBoard and hang.  I don't understand how that can happen,\r
9184      but the Sleep is harmless, so I've put it in.  Others have also\r
9185      reported what may be the same problem, so hopefully this will fix\r
9186      it for them too.  */\r
9187   Sleep(500);\r
9188 \r
9189   return NO_ERROR;\r
9190 }\r
9191 \r
9192 \r
9193 void\r
9194 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9195 {\r
9196   ChildProc *cp; int result;\r
9197 \r
9198   cp = (ChildProc *) pr;\r
9199   if (cp == NULL) return;\r
9200 \r
9201   switch (cp->kind) {\r
9202   case CPReal:\r
9203     /* TerminateProcess is considered harmful, so... */\r
9204     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9205     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9206     /* The following doesn't work because the chess program\r
9207        doesn't "have the same console" as WinBoard.  Maybe\r
9208        we could arrange for this even though neither WinBoard\r
9209        nor the chess program uses a console for stdio? */\r
9210     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9211 \r
9212     /* [AS] Special termination modes for misbehaving programs... */\r
9213     if( signal == 9 ) { \r
9214         result = TerminateProcess( cp->hProcess, 0 );\r
9215 \r
9216         if ( appData.debugMode) {\r
9217             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9218         }\r
9219     }\r
9220     else if( signal == 10 ) {\r
9221         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9222 \r
9223         if( dw != WAIT_OBJECT_0 ) {\r
9224             result = TerminateProcess( cp->hProcess, 0 );\r
9225 \r
9226             if ( appData.debugMode) {\r
9227                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9228             }\r
9229 \r
9230         }\r
9231     }\r
9232 \r
9233     CloseHandle(cp->hProcess);\r
9234     break;\r
9235 \r
9236   case CPComm:\r
9237     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9238     break;\r
9239 \r
9240   case CPSock:\r
9241     closesocket(cp->sock);\r
9242     WSACleanup();\r
9243     break;\r
9244 \r
9245   case CPRcmd:\r
9246     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9247     closesocket(cp->sock);\r
9248     closesocket(cp->sock2);\r
9249     WSACleanup();\r
9250     break;\r
9251   }\r
9252   free(cp);\r
9253 }\r
9254 \r
9255 void\r
9256 InterruptChildProcess(ProcRef pr)\r
9257 {\r
9258   ChildProc *cp;\r
9259 \r
9260   cp = (ChildProc *) pr;\r
9261   if (cp == NULL) return;\r
9262   switch (cp->kind) {\r
9263   case CPReal:\r
9264     /* The following doesn't work because the chess program\r
9265        doesn't "have the same console" as WinBoard.  Maybe\r
9266        we could arrange for this even though neither WinBoard\r
9267        nor the chess program uses a console for stdio */\r
9268     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9269     break;\r
9270 \r
9271   case CPComm:\r
9272   case CPSock:\r
9273     /* Can't interrupt */\r
9274     break;\r
9275 \r
9276   case CPRcmd:\r
9277     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9278     break;\r
9279   }\r
9280 }\r
9281 \r
9282 \r
9283 int\r
9284 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9285 {\r
9286   char cmdLine[MSG_SIZ];\r
9287 \r
9288   if (port[0] == NULLCHAR) {\r
9289     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9290   } else {\r
9291     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9292   }\r
9293   return StartChildProcess(cmdLine, "", pr);\r
9294 }\r
9295 \r
9296 \r
9297 /* Code to open TCP sockets */\r
9298 \r
9299 int\r
9300 OpenTCP(char *host, char *port, ProcRef *pr)\r
9301 {\r
9302   ChildProc *cp;\r
9303   int err;\r
9304   SOCKET s;\r
9305 \r
9306   struct sockaddr_in sa, mysa;\r
9307   struct hostent FAR *hp;\r
9308   unsigned short uport;\r
9309   WORD wVersionRequested;\r
9310   WSADATA wsaData;\r
9311 \r
9312   /* Initialize socket DLL */\r
9313   wVersionRequested = MAKEWORD(1, 1);\r
9314   err = WSAStartup(wVersionRequested, &wsaData);\r
9315   if (err != 0) return err;\r
9316 \r
9317   /* Make socket */\r
9318   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9319     err = WSAGetLastError();\r
9320     WSACleanup();\r
9321     return err;\r
9322   }\r
9323 \r
9324   /* Bind local address using (mostly) don't-care values.\r
9325    */\r
9326   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9327   mysa.sin_family = AF_INET;\r
9328   mysa.sin_addr.s_addr = INADDR_ANY;\r
9329   uport = (unsigned short) 0;\r
9330   mysa.sin_port = htons(uport);\r
9331   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9332       == SOCKET_ERROR) {\r
9333     err = WSAGetLastError();\r
9334     WSACleanup();\r
9335     return err;\r
9336   }\r
9337 \r
9338   /* Resolve remote host name */\r
9339   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9340   if (!(hp = gethostbyname(host))) {\r
9341     unsigned int b0, b1, b2, b3;\r
9342 \r
9343     err = WSAGetLastError();\r
9344 \r
9345     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9346       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9347       hp->h_addrtype = AF_INET;\r
9348       hp->h_length = 4;\r
9349       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9350       hp->h_addr_list[0] = (char *) malloc(4);\r
9351       hp->h_addr_list[0][0] = (char) b0;\r
9352       hp->h_addr_list[0][1] = (char) b1;\r
9353       hp->h_addr_list[0][2] = (char) b2;\r
9354       hp->h_addr_list[0][3] = (char) b3;\r
9355     } else {\r
9356       WSACleanup();\r
9357       return err;\r
9358     }\r
9359   }\r
9360   sa.sin_family = hp->h_addrtype;\r
9361   uport = (unsigned short) atoi(port);\r
9362   sa.sin_port = htons(uport);\r
9363   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9364 \r
9365   /* Make connection */\r
9366   if (connect(s, (struct sockaddr *) &sa,\r
9367               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9368     err = WSAGetLastError();\r
9369     WSACleanup();\r
9370     return err;\r
9371   }\r
9372 \r
9373   /* Prepare return value */\r
9374   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9375   cp->kind = CPSock;\r
9376   cp->sock = s;\r
9377   *pr = (ProcRef *) cp;\r
9378 \r
9379   return NO_ERROR;\r
9380 }\r
9381 \r
9382 int\r
9383 OpenCommPort(char *name, ProcRef *pr)\r
9384 {\r
9385   HANDLE h;\r
9386   COMMTIMEOUTS ct;\r
9387   ChildProc *cp;\r
9388   char fullname[MSG_SIZ];\r
9389 \r
9390   if (*name != '\\')\r
9391     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9392   else\r
9393     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9394 \r
9395   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9396                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9397   if (h == (HANDLE) -1) {\r
9398     return GetLastError();\r
9399   }\r
9400   hCommPort = h;\r
9401 \r
9402   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9403 \r
9404   /* Accumulate characters until a 100ms pause, then parse */\r
9405   ct.ReadIntervalTimeout = 100;\r
9406   ct.ReadTotalTimeoutMultiplier = 0;\r
9407   ct.ReadTotalTimeoutConstant = 0;\r
9408   ct.WriteTotalTimeoutMultiplier = 0;\r
9409   ct.WriteTotalTimeoutConstant = 0;\r
9410   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9411 \r
9412   /* Prepare return value */\r
9413   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9414   cp->kind = CPComm;\r
9415   cp->hFrom = h;\r
9416   cp->hTo = h;\r
9417   *pr = (ProcRef *) cp;\r
9418 \r
9419   return NO_ERROR;\r
9420 }\r
9421 \r
9422 int\r
9423 OpenLoopback(ProcRef *pr)\r
9424 {\r
9425   DisplayFatalError(_("Not implemented"), 0, 1);\r
9426   return NO_ERROR;\r
9427 }\r
9428 \r
9429 \r
9430 int\r
9431 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9432 {\r
9433   ChildProc *cp;\r
9434   int err;\r
9435   SOCKET s, s2, s3;\r
9436   struct sockaddr_in sa, mysa;\r
9437   struct hostent FAR *hp;\r
9438   unsigned short uport;\r
9439   WORD wVersionRequested;\r
9440   WSADATA wsaData;\r
9441   int fromPort;\r
9442   char stderrPortStr[MSG_SIZ];\r
9443 \r
9444   /* Initialize socket DLL */\r
9445   wVersionRequested = MAKEWORD(1, 1);\r
9446   err = WSAStartup(wVersionRequested, &wsaData);\r
9447   if (err != 0) return err;\r
9448 \r
9449   /* Resolve remote host name */\r
9450   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9451   if (!(hp = gethostbyname(host))) {\r
9452     unsigned int b0, b1, b2, b3;\r
9453 \r
9454     err = WSAGetLastError();\r
9455 \r
9456     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9457       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9458       hp->h_addrtype = AF_INET;\r
9459       hp->h_length = 4;\r
9460       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9461       hp->h_addr_list[0] = (char *) malloc(4);\r
9462       hp->h_addr_list[0][0] = (char) b0;\r
9463       hp->h_addr_list[0][1] = (char) b1;\r
9464       hp->h_addr_list[0][2] = (char) b2;\r
9465       hp->h_addr_list[0][3] = (char) b3;\r
9466     } else {\r
9467       WSACleanup();\r
9468       return err;\r
9469     }\r
9470   }\r
9471   sa.sin_family = hp->h_addrtype;\r
9472   uport = (unsigned short) 514;\r
9473   sa.sin_port = htons(uport);\r
9474   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9475 \r
9476   /* Bind local socket to unused "privileged" port address\r
9477    */\r
9478   s = INVALID_SOCKET;\r
9479   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9480   mysa.sin_family = AF_INET;\r
9481   mysa.sin_addr.s_addr = INADDR_ANY;\r
9482   for (fromPort = 1023;; fromPort--) {\r
9483     if (fromPort < 0) {\r
9484       WSACleanup();\r
9485       return WSAEADDRINUSE;\r
9486     }\r
9487     if (s == INVALID_SOCKET) {\r
9488       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9489         err = WSAGetLastError();\r
9490         WSACleanup();\r
9491         return err;\r
9492       }\r
9493     }\r
9494     uport = (unsigned short) fromPort;\r
9495     mysa.sin_port = htons(uport);\r
9496     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9497         == SOCKET_ERROR) {\r
9498       err = WSAGetLastError();\r
9499       if (err == WSAEADDRINUSE) continue;\r
9500       WSACleanup();\r
9501       return err;\r
9502     }\r
9503     if (connect(s, (struct sockaddr *) &sa,\r
9504       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9505       err = WSAGetLastError();\r
9506       if (err == WSAEADDRINUSE) {\r
9507         closesocket(s);\r
9508         s = -1;\r
9509         continue;\r
9510       }\r
9511       WSACleanup();\r
9512       return err;\r
9513     }\r
9514     break;\r
9515   }\r
9516 \r
9517   /* Bind stderr local socket to unused "privileged" port address\r
9518    */\r
9519   s2 = INVALID_SOCKET;\r
9520   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9521   mysa.sin_family = AF_INET;\r
9522   mysa.sin_addr.s_addr = INADDR_ANY;\r
9523   for (fromPort = 1023;; fromPort--) {\r
9524     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9525     if (fromPort < 0) {\r
9526       (void) closesocket(s);\r
9527       WSACleanup();\r
9528       return WSAEADDRINUSE;\r
9529     }\r
9530     if (s2 == INVALID_SOCKET) {\r
9531       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9532         err = WSAGetLastError();\r
9533         closesocket(s);\r
9534         WSACleanup();\r
9535         return err;\r
9536       }\r
9537     }\r
9538     uport = (unsigned short) fromPort;\r
9539     mysa.sin_port = htons(uport);\r
9540     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9541         == SOCKET_ERROR) {\r
9542       err = WSAGetLastError();\r
9543       if (err == WSAEADDRINUSE) continue;\r
9544       (void) closesocket(s);\r
9545       WSACleanup();\r
9546       return err;\r
9547     }\r
9548     if (listen(s2, 1) == SOCKET_ERROR) {\r
9549       err = WSAGetLastError();\r
9550       if (err == WSAEADDRINUSE) {\r
9551         closesocket(s2);\r
9552         s2 = INVALID_SOCKET;\r
9553         continue;\r
9554       }\r
9555       (void) closesocket(s);\r
9556       (void) closesocket(s2);\r
9557       WSACleanup();\r
9558       return err;\r
9559     }\r
9560     break;\r
9561   }\r
9562   prevStderrPort = fromPort; // remember port used\r
9563   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9564 \r
9565   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9566     err = WSAGetLastError();\r
9567     (void) closesocket(s);\r
9568     (void) closesocket(s2);\r
9569     WSACleanup();\r
9570     return err;\r
9571   }\r
9572 \r
9573   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9574     err = WSAGetLastError();\r
9575     (void) closesocket(s);\r
9576     (void) closesocket(s2);\r
9577     WSACleanup();\r
9578     return err;\r
9579   }\r
9580   if (*user == NULLCHAR) user = UserName();\r
9581   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9582     err = WSAGetLastError();\r
9583     (void) closesocket(s);\r
9584     (void) closesocket(s2);\r
9585     WSACleanup();\r
9586     return err;\r
9587   }\r
9588   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9589     err = WSAGetLastError();\r
9590     (void) closesocket(s);\r
9591     (void) closesocket(s2);\r
9592     WSACleanup();\r
9593     return err;\r
9594   }\r
9595 \r
9596   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9597     err = WSAGetLastError();\r
9598     (void) closesocket(s);\r
9599     (void) closesocket(s2);\r
9600     WSACleanup();\r
9601     return err;\r
9602   }\r
9603   (void) closesocket(s2);  /* Stop listening */\r
9604 \r
9605   /* Prepare return value */\r
9606   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9607   cp->kind = CPRcmd;\r
9608   cp->sock = s;\r
9609   cp->sock2 = s3;\r
9610   *pr = (ProcRef *) cp;\r
9611 \r
9612   return NO_ERROR;\r
9613 }\r
9614 \r
9615 \r
9616 InputSourceRef\r
9617 AddInputSource(ProcRef pr, int lineByLine,\r
9618                InputCallback func, VOIDSTAR closure)\r
9619 {\r
9620   InputSource *is, *is2 = NULL;\r
9621   ChildProc *cp = (ChildProc *) pr;\r
9622 \r
9623   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9624   is->lineByLine = lineByLine;\r
9625   is->func = func;\r
9626   is->closure = closure;\r
9627   is->second = NULL;\r
9628   is->next = is->buf;\r
9629   if (pr == NoProc) {\r
9630     is->kind = CPReal;\r
9631     consoleInputSource = is;\r
9632   } else {\r
9633     is->kind = cp->kind;\r
9634     /* \r
9635         [AS] Try to avoid a race condition if the thread is given control too early:\r
9636         we create all threads suspended so that the is->hThread variable can be\r
9637         safely assigned, then let the threads start with ResumeThread.\r
9638     */\r
9639     switch (cp->kind) {\r
9640     case CPReal:\r
9641       is->hFile = cp->hFrom;\r
9642       cp->hFrom = NULL; /* now owned by InputThread */\r
9643       is->hThread =\r
9644         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9645                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9646       break;\r
9647 \r
9648     case CPComm:\r
9649       is->hFile = cp->hFrom;\r
9650       cp->hFrom = NULL; /* now owned by InputThread */\r
9651       is->hThread =\r
9652         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9653                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9654       break;\r
9655 \r
9656     case CPSock:\r
9657       is->sock = cp->sock;\r
9658       is->hThread =\r
9659         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9660                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9661       break;\r
9662 \r
9663     case CPRcmd:\r
9664       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9665       *is2 = *is;\r
9666       is->sock = cp->sock;\r
9667       is->second = is2;\r
9668       is2->sock = cp->sock2;\r
9669       is2->second = is2;\r
9670       is->hThread =\r
9671         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9672                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9673       is2->hThread =\r
9674         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9675                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9676       break;\r
9677     }\r
9678 \r
9679     if( is->hThread != NULL ) {\r
9680         ResumeThread( is->hThread );\r
9681     }\r
9682 \r
9683     if( is2 != NULL && is2->hThread != NULL ) {\r
9684         ResumeThread( is2->hThread );\r
9685     }\r
9686   }\r
9687 \r
9688   return (InputSourceRef) is;\r
9689 }\r
9690 \r
9691 void\r
9692 RemoveInputSource(InputSourceRef isr)\r
9693 {\r
9694   InputSource *is;\r
9695 \r
9696   is = (InputSource *) isr;\r
9697   is->hThread = NULL;  /* tell thread to stop */\r
9698   CloseHandle(is->hThread);\r
9699   if (is->second != NULL) {\r
9700     is->second->hThread = NULL;\r
9701     CloseHandle(is->second->hThread);\r
9702   }\r
9703 }\r
9704 \r
9705 int no_wrap(char *message, int count)\r
9706 {\r
9707     ConsoleOutput(message, count, FALSE);\r
9708     return count;\r
9709 }\r
9710 \r
9711 int\r
9712 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9713 {\r
9714   DWORD dOutCount;\r
9715   int outCount = SOCKET_ERROR;\r
9716   ChildProc *cp = (ChildProc *) pr;\r
9717   static OVERLAPPED ovl;\r
9718   static int line = 0;\r
9719 \r
9720   if (pr == NoProc)\r
9721   {\r
9722     if (appData.noJoin || !appData.useInternalWrap)\r
9723       return no_wrap(message, count);\r
9724     else\r
9725     {\r
9726       int width = get_term_width();\r
9727       int len = wrap(NULL, message, count, width, &line);\r
9728       char *msg = malloc(len);\r
9729       int dbgchk;\r
9730 \r
9731       if (!msg)\r
9732         return no_wrap(message, count);\r
9733       else\r
9734       {\r
9735         dbgchk = wrap(msg, message, count, width, &line);\r
9736         if (dbgchk != len && appData.debugMode)\r
9737             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9738         ConsoleOutput(msg, len, FALSE);\r
9739         free(msg);\r
9740         return len;\r
9741       }\r
9742     }\r
9743   }\r
9744 \r
9745   if (ovl.hEvent == NULL) {\r
9746     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9747   }\r
9748   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9749 \r
9750   switch (cp->kind) {\r
9751   case CPSock:\r
9752   case CPRcmd:\r
9753     outCount = send(cp->sock, message, count, 0);\r
9754     if (outCount == SOCKET_ERROR) {\r
9755       *outError = WSAGetLastError();\r
9756     } else {\r
9757       *outError = NO_ERROR;\r
9758     }\r
9759     break;\r
9760 \r
9761   case CPReal:\r
9762     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9763                   &dOutCount, NULL)) {\r
9764       *outError = NO_ERROR;\r
9765       outCount = (int) dOutCount;\r
9766     } else {\r
9767       *outError = GetLastError();\r
9768     }\r
9769     break;\r
9770 \r
9771   case CPComm:\r
9772     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9773                             &dOutCount, &ovl);\r
9774     if (*outError == NO_ERROR) {\r
9775       outCount = (int) dOutCount;\r
9776     }\r
9777     break;\r
9778   }\r
9779   return outCount;\r
9780 }\r
9781 \r
9782 void\r
9783 DoSleep(int n)\r
9784 {\r
9785     if(n != 0) Sleep(n);\r
9786 }\r
9787 \r
9788 int\r
9789 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9790                        long msdelay)\r
9791 {\r
9792   /* Ignore delay, not implemented for WinBoard */\r
9793   return OutputToProcess(pr, message, count, outError);\r
9794 }\r
9795 \r
9796 \r
9797 void\r
9798 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9799                         char *buf, int count, int error)\r
9800 {\r
9801   DisplayFatalError(_("Not implemented"), 0, 1);\r
9802 }\r
9803 \r
9804 /* see wgamelist.c for Game List functions */\r
9805 /* see wedittags.c for Edit Tags functions */\r
9806 \r
9807 \r
9808 int\r
9809 ICSInitScript()\r
9810 {\r
9811   FILE *f;\r
9812   char buf[MSG_SIZ];\r
9813   char *dummy;\r
9814 \r
9815   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9816     f = fopen(buf, "r");\r
9817     if (f != NULL) {\r
9818       ProcessICSInitScript(f);\r
9819       fclose(f);\r
9820       return TRUE;\r
9821     }\r
9822   }\r
9823   return FALSE;\r
9824 }\r
9825 \r
9826 \r
9827 VOID\r
9828 StartAnalysisClock()\r
9829 {\r
9830   if (analysisTimerEvent) return;\r
9831   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9832                                         (UINT) 2000, NULL);\r
9833 }\r
9834 \r
9835 VOID\r
9836 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9837 {\r
9838   highlightInfo.sq[0].x = fromX;\r
9839   highlightInfo.sq[0].y = fromY;\r
9840   highlightInfo.sq[1].x = toX;\r
9841   highlightInfo.sq[1].y = toY;\r
9842 }\r
9843 \r
9844 VOID\r
9845 ClearHighlights()\r
9846 {\r
9847   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9848     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9849 }\r
9850 \r
9851 VOID\r
9852 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9853 {\r
9854   premoveHighlightInfo.sq[0].x = fromX;\r
9855   premoveHighlightInfo.sq[0].y = fromY;\r
9856   premoveHighlightInfo.sq[1].x = toX;\r
9857   premoveHighlightInfo.sq[1].y = toY;\r
9858 }\r
9859 \r
9860 VOID\r
9861 ClearPremoveHighlights()\r
9862 {\r
9863   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9864     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9865 }\r
9866 \r
9867 VOID\r
9868 ShutDownFrontEnd()\r
9869 {\r
9870   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9871   DeleteClipboardTempFiles();\r
9872 }\r
9873 \r
9874 void\r
9875 BoardToTop()\r
9876 {\r
9877     if (IsIconic(hwndMain))\r
9878       ShowWindow(hwndMain, SW_RESTORE);\r
9879 \r
9880     SetActiveWindow(hwndMain);\r
9881 }\r
9882 \r
9883 /*\r
9884  * Prototypes for animation support routines\r
9885  */\r
9886 static void ScreenSquare(int column, int row, POINT * pt);\r
9887 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9888      POINT frames[], int * nFrames);\r
9889 \r
9890 \r
9891 #define kFactor 4\r
9892 \r
9893 void\r
9894 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9895 {       // [HGM] atomic: animate blast wave\r
9896         int i;\r
9897 \r
9898         explodeInfo.fromX = fromX;\r
9899         explodeInfo.fromY = fromY;\r
9900         explodeInfo.toX = toX;\r
9901         explodeInfo.toY = toY;\r
9902         for(i=1; i<4*kFactor; i++) {\r
9903             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9904             DrawPosition(FALSE, board);\r
9905             Sleep(appData.animSpeed);\r
9906         }\r
9907         explodeInfo.radius = 0;\r
9908         DrawPosition(TRUE, board);\r
9909 }\r
9910 \r
9911 void\r
9912 AnimateMove(board, fromX, fromY, toX, toY)\r
9913      Board board;\r
9914      int fromX;\r
9915      int fromY;\r
9916      int toX;\r
9917      int toY;\r
9918 {\r
9919   ChessSquare piece;\r
9920   POINT start, finish, mid;\r
9921   POINT frames[kFactor * 2 + 1];\r
9922   int nFrames, n;\r
9923 \r
9924   if (!appData.animate) return;\r
9925   if (doingSizing) return;\r
9926   if (fromY < 0 || fromX < 0) return;\r
9927   piece = board[fromY][fromX];\r
9928   if (piece >= EmptySquare) return;\r
9929 \r
9930   ScreenSquare(fromX, fromY, &start);\r
9931   ScreenSquare(toX, toY, &finish);\r
9932 \r
9933   /* All moves except knight jumps move in straight line */\r
9934   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9935     mid.x = start.x + (finish.x - start.x) / 2;\r
9936     mid.y = start.y + (finish.y - start.y) / 2;\r
9937   } else {\r
9938     /* Knight: make straight movement then diagonal */\r
9939     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9940        mid.x = start.x + (finish.x - start.x) / 2;\r
9941        mid.y = start.y;\r
9942      } else {\r
9943        mid.x = start.x;\r
9944        mid.y = start.y + (finish.y - start.y) / 2;\r
9945      }\r
9946   }\r
9947   \r
9948   /* Don't use as many frames for very short moves */\r
9949   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9950     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9951   else\r
9952     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9953 \r
9954   animInfo.from.x = fromX;\r
9955   animInfo.from.y = fromY;\r
9956   animInfo.to.x = toX;\r
9957   animInfo.to.y = toY;\r
9958   animInfo.lastpos = start;\r
9959   animInfo.piece = piece;\r
9960   for (n = 0; n < nFrames; n++) {\r
9961     animInfo.pos = frames[n];\r
9962     DrawPosition(FALSE, NULL);\r
9963     animInfo.lastpos = animInfo.pos;\r
9964     Sleep(appData.animSpeed);\r
9965   }\r
9966   animInfo.pos = finish;\r
9967   DrawPosition(FALSE, NULL);\r
9968   animInfo.piece = EmptySquare;\r
9969   Explode(board, fromX, fromY, toX, toY);\r
9970 }\r
9971 \r
9972 /*      Convert board position to corner of screen rect and color       */\r
9973 \r
9974 static void\r
9975 ScreenSquare(column, row, pt)\r
9976      int column; int row; POINT * pt;\r
9977 {\r
9978   if (flipView) {\r
9979     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9980     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9981   } else {\r
9982     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9983     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9984   }\r
9985 }\r
9986 \r
9987 /*      Generate a series of frame coords from start->mid->finish.\r
9988         The movement rate doubles until the half way point is\r
9989         reached, then halves back down to the final destination,\r
9990         which gives a nice slow in/out effect. The algorithmn\r
9991         may seem to generate too many intermediates for short\r
9992         moves, but remember that the purpose is to attract the\r
9993         viewers attention to the piece about to be moved and\r
9994         then to where it ends up. Too few frames would be less\r
9995         noticeable.                                             */\r
9996 \r
9997 static void\r
9998 Tween(start, mid, finish, factor, frames, nFrames)\r
9999      POINT * start; POINT * mid;\r
10000      POINT * finish; int factor;\r
10001      POINT frames[]; int * nFrames;\r
10002 {\r
10003   int n, fraction = 1, count = 0;\r
10004 \r
10005   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10006   for (n = 0; n < factor; n++)\r
10007     fraction *= 2;\r
10008   for (n = 0; n < factor; n++) {\r
10009     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10010     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10011     count ++;\r
10012     fraction = fraction / 2;\r
10013   }\r
10014   \r
10015   /* Midpoint */\r
10016   frames[count] = *mid;\r
10017   count ++;\r
10018   \r
10019   /* Slow out, stepping 1/2, then 1/4, ... */\r
10020   fraction = 2;\r
10021   for (n = 0; n < factor; n++) {\r
10022     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10023     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10024     count ++;\r
10025     fraction = fraction * 2;\r
10026   }\r
10027   *nFrames = count;\r
10028 }\r
10029 \r
10030 void\r
10031 SettingsPopUp(ChessProgramState *cps)\r
10032 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10033       EngineOptionsPopup(savedHwnd, cps);\r
10034 }\r
10035 \r
10036 int flock(int fid, int code)\r
10037 {\r
10038     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10039     OVERLAPPED ov;\r
10040     ov.hEvent = NULL;\r
10041     ov.Offset = 0;\r
10042     ov.OffsetHigh = 0;\r
10043     switch(code) {\r
10044       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10045       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10046       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10047       default: return -1;\r
10048     }\r
10049     return 0;\r
10050 }\r
10051 \r
10052 char *\r
10053 Col2Text (int n)\r
10054 {\r
10055     static int i=0;\r
10056     static char col[8][20];\r
10057     COLORREF color = *(COLORREF *) colorVariable[n];\r
10058     i = i+1 & 7;\r
10059     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10060     return col[i];\r
10061 }\r
10062 \r
10063 void\r
10064 ActivateTheme (int new)\r
10065 {   // Redo initialization of features depending on options that can occur in themes\r
10066    InitTextures();\r
10067    if(new) InitDrawingColors();\r
10068    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10069    InitDrawingSizes(boardSize, 0);\r
10070    InvalidateRect(hwndMain, NULL, TRUE);\r
10071 }\r