519f9240d1d29117b8bfc918e292703e9a5b2902
[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, 2014, 2015, 2016 Free\r
9  * Software Foundation, Inc.\r
10  *\r
11  * Enhancements Copyright 2005 Alessandro Scotti\r
12  *\r
13  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
14  * which was written and is copyrighted by Wayne Christopher.\r
15  *\r
16  * The following terms apply to Digital Equipment Corporation's copyright\r
17  * interest in XBoard:\r
18  * ------------------------------------------------------------------------\r
19  * All Rights Reserved\r
20  *\r
21  * Permission to use, copy, modify, and distribute this software and its\r
22  * documentation for any purpose and without fee is hereby granted,\r
23  * provided that the above copyright notice appear in all copies and that\r
24  * both that copyright notice and this permission notice appear in\r
25  * supporting documentation, and that the name of Digital not be\r
26  * used in advertising or publicity pertaining to distribution of the\r
27  * software without specific, written prior permission.\r
28  *\r
29  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
30  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
31  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
32  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
33  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
34  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
35  * SOFTWARE.\r
36  * ------------------------------------------------------------------------\r
37  *\r
38  * The following terms apply to the enhanced version of XBoard\r
39  * distributed by the Free Software Foundation:\r
40  * ------------------------------------------------------------------------\r
41  *\r
42  * GNU XBoard is free software: you can redistribute it and/or modify\r
43  * it under the terms of the GNU General Public License as published by\r
44  * the Free Software Foundation, either version 3 of the License, or (at\r
45  * your option) any later version.\r
46  *\r
47  * GNU XBoard is distributed in the hope that it will be useful, but\r
48  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
49  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
50  * General Public License for more details.\r
51  *\r
52  * You should have received a copy of the GNU General Public License\r
53  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
54  *\r
55  *------------------------------------------------------------------------\r
56  ** See the file ChangeLog for a revision history.  */\r
57 \r
58 #ifndef WINVER\r
59 #define WINVER 0x0500\r
60 #endif\r
61 \r
62 #include "config.h"\r
63 \r
64 #include <windows.h>\r
65 #include <winuser.h>\r
66 #include <winsock.h>\r
67 #include <commctrl.h>\r
68 \r
69 #include <stdio.h>\r
70 #include <stdlib.h>\r
71 #include <time.h>\r
72 #include <malloc.h>\r
73 #include <sys/stat.h>\r
74 #include <fcntl.h>\r
75 #include <math.h>\r
76 #include <commdlg.h>\r
77 #include <dlgs.h>\r
78 #include <richedit.h>\r
79 #include <mmsystem.h>\r
80 #include <ctype.h>\r
81 #include <io.h>\r
82 \r
83 #if __GNUC__\r
84 #include <errno.h>\r
85 #include <string.h>\r
86 #endif\r
87 \r
88 #include "common.h"\r
89 #include "frontend.h"\r
90 #include "backend.h"\r
91 #include "winboard.h"\r
92 #include "moves.h"\r
93 #include "wclipbrd.h"\r
94 #include "woptions.h"\r
95 #include "wsockerr.h"\r
96 #include "defaults.h"\r
97 #include "help.h"\r
98 #include "wsnap.h"\r
99 \r
100 #define SLASH '/'\r
101 #define DATADIR "~~"\r
102 \r
103 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
104 \r
105   int myrandom(void);\r
106   void mysrandom(unsigned int seed);\r
107 \r
108 extern int whiteFlag, blackFlag;\r
109 Boolean flipClock = FALSE;\r
110 extern HANDLE chatHandle[];\r
111 extern enum ICS_TYPE ics_type;\r
112 \r
113 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
114 int  MyGetFullPathName P((char *name, char *fullname));\r
115 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
116 VOID NewVariantPopup(HWND hwnd);\r
117 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
118                    /*char*/int promoChar));\r
119 void DisplayMove P((int moveNumber));\r
120 void ChatPopUp P((char *s));\r
121 typedef struct {\r
122   ChessSquare piece;  \r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126   POINT to;       /* board coordinates of the piece's new pos */\r
127 } AnimInfo;\r
128 \r
129 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
130 \r
131 typedef struct {\r
132   POINT start;    /* window coordinates of start pos */\r
133   POINT pos;      /* window coordinates of current pos */\r
134   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
135   POINT from;     /* board coordinates of the piece's orig pos */\r
136   ChessSquare piece;\r
137 } DragInfo;\r
138 \r
139 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
140 \r
141 typedef struct {\r
142   POINT sq[2];    /* board coordinates of from, to squares */\r
143 } HighlightInfo;\r
144 \r
145 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
146 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
147 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
148 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
149 \r
150 typedef struct { // [HGM] atomic\r
151   int fromX, fromY, toX, toY, radius;\r
152 } ExplodeInfo;\r
153 \r
154 static ExplodeInfo explodeInfo;\r
155 \r
156 /* Window class names */\r
157 char szAppName[] = "WinBoard";\r
158 char szConsoleName[] = "WBConsole";\r
159 \r
160 /* Title bar text */\r
161 char szTitle[] = "WinBoard";\r
162 char szConsoleTitle[] = "I C S Interaction";\r
163 \r
164 char *programName;\r
165 char *settingsFileName;\r
166 Boolean saveSettingsOnExit;\r
167 char installDir[MSG_SIZ];\r
168 int errorExitStatus;\r
169 \r
170 BoardSize boardSize;\r
171 Boolean chessProgram;\r
172 //static int boardX, boardY;\r
173 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
174 int squareSize, lineGap, minorSize;\r
175 static int winW, winH;\r
176 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
177 static int logoHeight = 0;\r
178 static char messageText[MESSAGE_TEXT_MAX];\r
179 static int clockTimerEvent = 0;\r
180 static int loadGameTimerEvent = 0;\r
181 static int analysisTimerEvent = 0;\r
182 static DelayedEventCallback delayedTimerCallback;\r
183 static int delayedTimerEvent = 0;\r
184 static int buttonCount = 2;\r
185 char *icsTextMenuString;\r
186 char *icsNames;\r
187 char *firstChessProgramNames;\r
188 char *secondChessProgramNames;\r
189 \r
190 #define PALETTESIZE 256\r
191 \r
192 HINSTANCE hInst;          /* current instance */\r
193 Boolean alwaysOnTop = FALSE;\r
194 RECT boardRect;\r
195 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
196   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
197 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
198 HPALETTE hPal;\r
199 ColorClass currentColorClass;\r
200 \r
201 static HWND savedHwnd;\r
202 HWND hCommPort = NULL;    /* currently open comm port */\r
203 static HWND hwndPause;    /* pause button */\r
204 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
205 static HBRUSH lightSquareBrush, darkSquareBrush,\r
206   blackSquareBrush, /* [HGM] for band between board and holdings */\r
207   explodeBrush,     /* [HGM] atomic */\r
208   markerBrush[8],   /* [HGM] markers */\r
209   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
210 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
211 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
212 static HPEN gridPen = NULL;\r
213 static HPEN highlightPen = NULL;\r
214 static HPEN premovePen = NULL;\r
215 static NPLOGPALETTE pLogPal;\r
216 static BOOL paletteChanged = FALSE;\r
217 static HICON iconWhite, iconBlack, iconCurrent;\r
218 static int doingSizing = FALSE;\r
219 static int lastSizing = 0;\r
220 static int prevStderrPort;\r
221 static HBITMAP userLogo;\r
222 \r
223 static HBITMAP liteBackTexture = NULL;\r
224 static HBITMAP darkBackTexture = NULL;\r
225 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
226 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
227 static int backTextureSquareSize = 0;\r
228 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
229 \r
230 #if __GNUC__ && !defined(_winmajor)\r
231 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
232 #else\r
233 \r
234 #if defined(_winmajor)\r
235 #define oldDialog (_winmajor < 4)\r
236 #else\r
237 #define oldDialog 0\r
238 #endif\r
239 #endif\r
240 \r
241 #define INTERNATIONAL\r
242 \r
243 #ifdef INTERNATIONAL\r
244 #  define _(s) T_(s)\r
245 #  define N_(s) s\r
246 #else\r
247 #  define _(s) s\r
248 #  define N_(s) s\r
249 #  define T_(s) s\r
250 #  define Translate(x, y)\r
251 #  define LoadLanguageFile(s)\r
252 #endif\r
253 \r
254 #ifdef INTERNATIONAL\r
255 \r
256 Boolean barbaric; // flag indicating if translation is needed\r
257 \r
258 // list of item numbers used in each dialog (used to alter language at run time)\r
259 \r
260 #define ABOUTBOX -1  /* not sure why these are needed */\r
261 #define ABOUTBOX2 -1\r
262 \r
263 int dialogItems[][42] = {\r
264 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
265 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
266   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
267 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
268   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
269   OPT_Ranget, IDOK, IDCANCEL }, \r
270 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
271   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
272 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
273 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
274   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
275 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
276 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
277   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
278 { ABOUTBOX2, IDC_ChessBoard }, \r
279 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
280   OPT_GameListClose, IDC_GameListDoFilter }, \r
281 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
282 { DLG_Error, IDOK }, \r
283 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
284   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
285 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
286 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
287   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
288   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
289 { DLG_IndexNumber, IDC_Index }, \r
290 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
291 { DLG_TypeInName, IDOK, IDCANCEL }, \r
292 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
293   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
294 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
295   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
296   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
297   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
298   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
299   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
300   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
301 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
302   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
303   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
304   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
305   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
306   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
307   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
308   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
309   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
310 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
311   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
312   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
313   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
314   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
315   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
316   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
317   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
318 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
319   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
320   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
321   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
322   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
323   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
324   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
325   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
326   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
327 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
328   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
329   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
330   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
331   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
332 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
333 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
334   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
335 { DLG_MoveHistory }, \r
336 { DLG_EvalGraph }, \r
337 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
338 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
339 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
340   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
341   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
342   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
343 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
344   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
345   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
346 { 0 }\r
347 };\r
348 \r
349 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
350 static int lastChecked;\r
351 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
352 extern int tinyLayout;\r
353 extern char * menuBarText[][10];\r
354 \r
355 void\r
356 LoadLanguageFile(char *name)\r
357 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
358     FILE *f;\r
359     int i=0, j=0, n=0, k;\r
360     char buf[MSG_SIZ];\r
361 \r
362     if(!name || name[0] == NULLCHAR) return;\r
363       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
364     appData.language = oldLanguage;\r
365     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
366     if((f = fopen(buf, "r")) == NULL) return;\r
367     while((k = fgetc(f)) != EOF) {\r
368         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
369         languageBuf[i] = k;\r
370         if(k == '\n') {\r
371             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
372                 char *p;\r
373                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
374                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
375                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
376                         english[j] = languageBuf + n + 1; *p = 0;\r
377                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
378 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
379                     }\r
380                 }\r
381             }\r
382             n = i + 1;\r
383         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
384             switch(k) {\r
385               case 'n': k = '\n'; break;\r
386               case 'r': k = '\r'; break;\r
387               case 't': k = '\t'; break;\r
388             }\r
389             languageBuf[--i] = k;\r
390         }\r
391         i++;\r
392     }\r
393     fclose(f);\r
394     barbaric = (j != 0);\r
395     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
396 }\r
397 \r
398 char *\r
399 T_(char *s)\r
400 {   // return the translation of the given string\r
401     // efficiency can be improved a lot...\r
402     int i=0;\r
403     static char buf[MSG_SIZ];\r
404 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
405     if(!barbaric) return s;\r
406     if(!s) return ""; // sanity\r
407     while(english[i]) {\r
408         if(!strcmp(s, english[i])) return foreign[i];\r
409         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
410             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
411             return buf;\r
412         }\r
413         i++;\r
414     }\r
415     return s;\r
416 }\r
417 \r
418 void\r
419 Translate(HWND hDlg, int dialogID)\r
420 {   // translate all text items in the given dialog\r
421     int i=0, j, k;\r
422     char buf[MSG_SIZ], *s;\r
423     if(!barbaric) return;\r
424     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
425     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
426     GetWindowText( hDlg, buf, MSG_SIZ );\r
427     s = T_(buf);\r
428     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
429     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
430         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
431         if(strlen(buf) == 0) continue;\r
432         s = T_(buf);\r
433         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
434     }\r
435 }\r
436 \r
437 HMENU\r
438 TranslateOneMenu(int i, HMENU subMenu)\r
439 {\r
440     int j;\r
441     static MENUITEMINFO info;\r
442 \r
443     info.cbSize = sizeof(MENUITEMINFO);\r
444     info.fMask = MIIM_STATE | MIIM_TYPE;\r
445           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
446             char buf[MSG_SIZ];\r
447             info.dwTypeData = buf;\r
448             info.cch = sizeof(buf);\r
449             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
450             if(i < 10) {\r
451                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
452                 else menuText[i][j] = strdup(buf); // remember original on first change\r
453             }\r
454             if(buf[0] == NULLCHAR) continue;\r
455             info.dwTypeData = T_(buf);\r
456             info.cch = strlen(buf)+1;\r
457             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
458           }\r
459     return subMenu;\r
460 }\r
461 \r
462 void\r
463 TranslateMenus(int addLanguage)\r
464 {\r
465     int i;\r
466     WIN32_FIND_DATA fileData;\r
467     HANDLE hFind;\r
468 #define IDM_English 1970\r
469     if(1) {\r
470         HMENU mainMenu = GetMenu(hwndMain);\r
471         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
472           HMENU subMenu = GetSubMenu(mainMenu, i);\r
473           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
474                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
475           TranslateOneMenu(i, subMenu);\r
476         }\r
477         DrawMenuBar(hwndMain);\r
478     }\r
479 \r
480     if(!addLanguage) return;\r
481     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
482         HMENU mainMenu = GetMenu(hwndMain);\r
483         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
484         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
485         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
486         i = 0; lastChecked = IDM_English;\r
487         do {\r
488             char *p, *q = fileData.cFileName;\r
489             int checkFlag = MF_UNCHECKED;\r
490             languageFile[i] = strdup(q);\r
491             if(barbaric && !strcmp(oldLanguage, q)) {\r
492                 checkFlag = MF_CHECKED;\r
493                 lastChecked = IDM_English + i + 1;\r
494                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
495             }\r
496             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
497             p = strstr(fileData.cFileName, ".lng");\r
498             if(p) *p = 0;\r
499             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
500         } while(FindNextFile(hFind, &fileData));\r
501         FindClose(hFind);\r
502     }\r
503 }\r
504 \r
505 #endif\r
506 \r
507 #define IDM_RecentEngines 3000\r
508 \r
509 void\r
510 RecentEngineMenu (char *s)\r
511 {\r
512     if(appData.icsActive) return;\r
513     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
514         HMENU mainMenu = GetMenu(hwndMain);\r
515         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
516         int i=IDM_RecentEngines;\r
517         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
518         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
519         while(*s) {\r
520           char *p = strchr(s, '\n');\r
521           if(p == NULL) return; // malformed!\r
522           *p = NULLCHAR;\r
523           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
524           *p = '\n';\r
525           s = p+1;\r
526         }\r
527     }\r
528 }\r
529 \r
530 \r
531 typedef struct {\r
532   char *name;\r
533   int squareSize;\r
534   int lineGap;\r
535   int smallLayout;\r
536   int tinyLayout;\r
537   int cliWidth, cliHeight;\r
538 } SizeInfo;\r
539 \r
540 SizeInfo sizeInfo[] = \r
541 {\r
542   { "tiny",     21, 0, 1, 2, 0, 0 },\r
543   { "teeny",    25, 1, 1, 2, 0, 0 },\r
544   { "dinky",    29, 1, 1, 2, 0, 0 },\r
545   { "petite",   33, 1, 1, 2, 0, 0 },\r
546   { "slim",     37, 2, 1, 1, 0, 0 },\r
547   { "small",    40, 2, 1, 1, 0, 0 },\r
548   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
549   { "middling", 49, 2, 0, 0, 0, 0 },\r
550   { "average",  54, 2, 0, 0, 0, 0 },\r
551   { "moderate", 58, 3, 0, 0, 0, 0 },\r
552   { "medium",   64, 3, 0, 0, 0, 0 },\r
553   { "bulky",    72, 3, 0, 0, 0, 0 },\r
554   { "large",    80, 3, 0, 0, 0, 0 },\r
555   { "big",      87, 3, 0, 0, 0, 0 },\r
556   { "huge",     95, 3, 0, 0, 0, 0 },\r
557   { "giant",    108, 3, 0, 0, 0, 0 },\r
558   { "colossal", 116, 4, 0, 0, 0, 0 },\r
559   { "titanic",  129, 4, 0, 0, 0, 0 },\r
560   { NULL, 0, 0, 0, 0, 0, 0 }\r
561 };\r
562 \r
563 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
564 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
565 {\r
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576   { 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
577   { 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
578   { 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
579   { 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
580   { 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
581   { 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
582   { 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
583   { 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
584 };\r
585 \r
586 MyFont *font[NUM_SIZES][NUM_FONTS];\r
587 \r
588 typedef struct {\r
589   char *label;\r
590   int id;\r
591   HWND hwnd;\r
592   WNDPROC wndproc;\r
593 } MyButtonDesc;\r
594 \r
595 #define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)\r
596 #define N_BUTTONS 5\r
597 \r
598 MyButtonDesc buttonDesc[N_BUTTONS] =\r
599 {\r
600   {"<<", IDM_ToStart, NULL, NULL},\r
601   {"<", IDM_Backward, NULL, NULL},\r
602   {"P", IDM_Pause, NULL, NULL},\r
603   {">", IDM_Forward, NULL, NULL},\r
604   {">>", IDM_ToEnd, NULL, NULL},\r
605 };\r
606 \r
607 int tinyLayout = 0, smallLayout = 0;\r
608 #define MENU_BAR_ITEMS 9\r
609 char *menuBarText[3][MENU_BAR_ITEMS+1] = {\r
610   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
611   { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },\r
612   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
613 };\r
614 \r
615 \r
616 MySound sounds[(int)NSoundClasses];\r
617 MyTextAttribs textAttribs[(int)NColorClasses];\r
618 \r
619 MyColorizeAttribs colorizeAttribs[] = {\r
620   { (COLORREF)0, 0, N_("Shout Text") },\r
621   { (COLORREF)0, 0, N_("SShout/CShout") },\r
622   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
623   { (COLORREF)0, 0, N_("Channel Text") },\r
624   { (COLORREF)0, 0, N_("Kibitz Text") },\r
625   { (COLORREF)0, 0, N_("Tell Text") },\r
626   { (COLORREF)0, 0, N_("Challenge Text") },\r
627   { (COLORREF)0, 0, N_("Request Text") },\r
628   { (COLORREF)0, 0, N_("Seek Text") },\r
629   { (COLORREF)0, 0, N_("Normal Text") },\r
630   { (COLORREF)0, 0, N_("None") }\r
631 };\r
632 \r
633 \r
634 \r
635 static char *commentTitle;\r
636 static char *commentText;\r
637 static int commentIndex;\r
638 static Boolean editComment = FALSE;\r
639 \r
640 \r
641 char errorTitle[MSG_SIZ];\r
642 char errorMessage[2*MSG_SIZ];\r
643 HWND errorDialog = NULL;\r
644 BOOLEAN moveErrorMessageUp = FALSE;\r
645 BOOLEAN consoleEcho = TRUE;\r
646 CHARFORMAT consoleCF;\r
647 COLORREF consoleBackgroundColor;\r
648 \r
649 char *programVersion;\r
650 \r
651 #define CPReal 1\r
652 #define CPComm 2\r
653 #define CPSock 3\r
654 #define CPRcmd 4\r
655 typedef int CPKind;\r
656 \r
657 typedef struct {\r
658   CPKind kind;\r
659   HANDLE hProcess;\r
660   DWORD pid;\r
661   HANDLE hTo;\r
662   HANDLE hFrom;\r
663   SOCKET sock;\r
664   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
665 } ChildProc;\r
666 \r
667 #define INPUT_SOURCE_BUF_SIZE 4096\r
668 \r
669 typedef struct _InputSource {\r
670   CPKind kind;\r
671   HANDLE hFile;\r
672   SOCKET sock;\r
673   int lineByLine;\r
674   HANDLE hThread;\r
675   DWORD id;\r
676   char buf[INPUT_SOURCE_BUF_SIZE];\r
677   char *next;\r
678   DWORD count;\r
679   int error;\r
680   InputCallback func;\r
681   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
682   VOIDSTAR closure;\r
683 } InputSource;\r
684 \r
685 InputSource *consoleInputSource;\r
686 \r
687 DCB dcb;\r
688 \r
689 /* forward */\r
690 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
691 VOID ConsoleCreate();\r
692 LRESULT CALLBACK\r
693   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
694 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
695 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
696 VOID ParseCommSettings(char *arg, DCB *dcb);\r
697 LRESULT CALLBACK\r
698   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
699 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
700 void ParseIcsTextMenu(char *icsTextMenuString);\r
701 VOID PopUpNameDialog(char firstchar);\r
702 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
703 \r
704 /* [AS] */\r
705 int NewGameFRC();\r
706 int GameListOptions();\r
707 \r
708 int dummy; // [HGM] for obsolete args\r
709 \r
710 HWND hwndMain = NULL;        /* root window*/\r
711 HWND hwndConsole = NULL;\r
712 HWND commentDialog = NULL;\r
713 HWND moveHistoryDialog = NULL;\r
714 HWND evalGraphDialog = NULL;\r
715 HWND engineOutputDialog = NULL;\r
716 HWND gameListDialog = NULL;\r
717 HWND editTagsDialog = NULL;\r
718 \r
719 int commentUp = FALSE;\r
720 \r
721 WindowPlacement wpMain;\r
722 WindowPlacement wpConsole;\r
723 WindowPlacement wpComment;\r
724 WindowPlacement wpMoveHistory;\r
725 WindowPlacement wpEvalGraph;\r
726 WindowPlacement wpEngineOutput;\r
727 WindowPlacement wpGameList;\r
728 WindowPlacement wpTags;\r
729 \r
730 VOID EngineOptionsPopup(); // [HGM] settings\r
731 \r
732 VOID GothicPopUp(char *title, VariantClass variant);\r
733 /*\r
734  * Setting "frozen" should disable all user input other than deleting\r
735  * the window.  We do this while engines are initializing themselves.\r
736  */\r
737 static int frozen = 0;\r
738 static int oldMenuItemState[MENU_BAR_ITEMS];\r
739 void FreezeUI()\r
740 {\r
741   HMENU hmenu;\r
742   int i;\r
743 \r
744   if (frozen) return;\r
745   frozen = 1;\r
746   hmenu = GetMenu(hwndMain);\r
747   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
748     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
749   }\r
750   DrawMenuBar(hwndMain);\r
751 }\r
752 \r
753 /* Undo a FreezeUI */\r
754 void ThawUI()\r
755 {\r
756   HMENU hmenu;\r
757   int i;\r
758 \r
759   if (!frozen) return;\r
760   frozen = 0;\r
761   hmenu = GetMenu(hwndMain);\r
762   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
763     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
764   }\r
765   DrawMenuBar(hwndMain);\r
766 }\r
767 \r
768 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
769 \r
770 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
771 #ifdef JAWS\r
772 #include "jaws.c"\r
773 #else\r
774 #define JAWS_INIT\r
775 #define JAWS_ARGS\r
776 #define JAWS_ALT_INTERCEPT\r
777 #define JAWS_KBUP_NAVIGATION\r
778 #define JAWS_KBDOWN_NAVIGATION\r
779 #define JAWS_MENU_ITEMS\r
780 #define JAWS_SILENCE\r
781 #define JAWS_REPLAY\r
782 #define JAWS_ACCEL\r
783 #define JAWS_COPYRIGHT\r
784 #define JAWS_DELETE(X) X\r
785 #define SAYMACHINEMOVE()\r
786 #define SAY(X)\r
787 #endif\r
788 \r
789 /*---------------------------------------------------------------------------*\\r
790  *\r
791  * WinMain\r
792  *\r
793 \*---------------------------------------------------------------------------*/\r
794 \r
795 static void HandleMessage P((MSG *message));\r
796 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
797 \r
798 int APIENTRY\r
799 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
800         LPSTR lpCmdLine, int nCmdShow)\r
801 {\r
802   MSG msg;\r
803 //  INITCOMMONCONTROLSEX ex;\r
804 \r
805   debugFP = stderr;\r
806 \r
807   LoadLibrary("RICHED32.DLL");\r
808   consoleCF.cbSize = sizeof(CHARFORMAT);\r
809 \r
810   if (!InitApplication(hInstance)) {\r
811     return (FALSE);\r
812   }\r
813   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
814     return (FALSE);\r
815   }\r
816 \r
817   JAWS_INIT\r
818   TranslateMenus(1);\r
819 \r
820 //  InitCommonControlsEx(&ex);\r
821   InitCommonControls();\r
822 \r
823   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
824   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
825   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
826 \r
827   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
828 \r
829   while (GetMessage(&msg, /* message structure */\r
830                     NULL, /* handle of window receiving the message */\r
831                     0,    /* lowest message to examine */\r
832                     0))   /* highest message to examine */\r
833     {\r
834         HandleMessage(&msg);\r
835     }\r
836 \r
837 \r
838   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
839 }\r
840 \r
841 static void\r
842 HandleMessage (MSG *message)\r
843 {\r
844     MSG msg = *message;\r
845 \r
846       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
847         // [HGM] navigate: switch between all windows with tab\r
848         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
849         int i, currentElement = 0;\r
850 \r
851         // first determine what element of the chain we come from (if any)\r
852         if(appData.icsActive) {\r
853             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
854             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
855         }\r
856         if(engineOutputDialog && EngineOutputIsUp()) {\r
857             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
858             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
859         }\r
860         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
861             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
862         }\r
863         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
864         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
865         if(msg.hwnd == e1)                 currentElement = 2; else\r
866         if(msg.hwnd == e2)                 currentElement = 3; else\r
867         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
868         if(msg.hwnd == mh)                currentElement = 4; else\r
869         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
870         if(msg.hwnd == hText)  currentElement = 5; else\r
871         if(msg.hwnd == hInput) currentElement = 6; else\r
872         for (i = 0; i < N_BUTTONS; i++) {\r
873             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
874         }\r
875 \r
876         // determine where to go to\r
877         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
878           do {\r
879             currentElement = (currentElement + direction) % 7;\r
880             switch(currentElement) {\r
881                 case 0:\r
882                   h = hwndMain; break; // passing this case always makes the loop exit\r
883                 case 1:\r
884                   h = buttonDesc[0].hwnd; break; // could be NULL\r
885                 case 2:\r
886                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
887                   h = e1; break;\r
888                 case 3:\r
889                   if(!EngineOutputIsUp()) continue;\r
890                   h = e2; break;\r
891                 case 4:\r
892                   if(!MoveHistoryIsUp()) continue;\r
893                   h = mh; break;\r
894 //              case 6: // input to eval graph does not seem to get here!\r
895 //                if(!EvalGraphIsUp()) continue;\r
896 //                h = evalGraphDialog; break;\r
897                 case 5:\r
898                   if(!appData.icsActive) continue;\r
899                   SAY("display");\r
900                   h = hText; break;\r
901                 case 6:\r
902                   if(!appData.icsActive) continue;\r
903                   SAY("input");\r
904                   h = hInput; break;\r
905             }\r
906           } while(h == 0);\r
907 \r
908           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
909           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
910           SetFocus(h);\r
911 \r
912           return; // this message now has been processed\r
913         }\r
914       }\r
915 \r
916       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
917           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
918           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
919           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
920           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
921           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
922           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
923           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
924           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
925           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
926         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
927         for(i=0; i<MAX_CHAT; i++) \r
928             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
929                 done = 1; break;\r
930         }\r
931         if(done) return; // [HGM] chat: end patch\r
932         TranslateMessage(&msg); /* Translates virtual key codes */\r
933         DispatchMessage(&msg);  /* Dispatches message to window */\r
934       }\r
935 }\r
936 \r
937 void\r
938 DoEvents ()\r
939 { /* Dispatch pending messages */\r
940   MSG msg;\r
941   while (PeekMessage(&msg, /* message structure */\r
942                      NULL, /* handle of window receiving the message */\r
943                      0,    /* lowest message to examine */\r
944                      0,    /* highest message to examine */\r
945                      PM_REMOVE))\r
946     {\r
947         HandleMessage(&msg);\r
948     }\r
949 }\r
950 \r
951 /*---------------------------------------------------------------------------*\\r
952  *\r
953  * Initialization functions\r
954  *\r
955 \*---------------------------------------------------------------------------*/\r
956 \r
957 void\r
958 SetUserLogo()\r
959 {   // update user logo if necessary\r
960     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
961 \r
962     if(appData.autoLogo) {\r
963           curName = UserName();\r
964           if(strcmp(curName, oldUserName)) {\r
965                 GetCurrentDirectory(MSG_SIZ, dir);\r
966                 SetCurrentDirectory(installDir);\r
967                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
968                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
969                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
970                 if(userLogo == NULL)\r
971                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
972                 SetCurrentDirectory(dir); /* return to prev directory */\r
973           }\r
974     }\r
975 }\r
976 \r
977 BOOL\r
978 InitApplication(HINSTANCE hInstance)\r
979 {\r
980   WNDCLASS wc;\r
981 \r
982   /* Fill in window class structure with parameters that describe the */\r
983   /* main window. */\r
984 \r
985   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
986   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
987   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
988   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
989   wc.hInstance     = hInstance;         /* Owner of this class */\r
990   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
991   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
992   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
993   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
994   wc.lpszClassName = szAppName;                 /* Name to register as */\r
995 \r
996   /* Register the window class and return success/failure code. */\r
997   if (!RegisterClass(&wc)) return FALSE;\r
998 \r
999   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
1000   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
1001   wc.cbClsExtra    = 0;\r
1002   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
1003   wc.hInstance     = hInstance;\r
1004   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
1005   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1006   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1007   wc.lpszMenuName  = NULL;\r
1008   wc.lpszClassName = szConsoleName;\r
1009 \r
1010   if (!RegisterClass(&wc)) return FALSE;\r
1011   return TRUE;\r
1012 }\r
1013 \r
1014 \r
1015 /* Set by InitInstance, used by EnsureOnScreen */\r
1016 int screenHeight, screenWidth;\r
1017 RECT screenGeometry;\r
1018 \r
1019 void\r
1020 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1021 {\r
1022 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1023   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1024   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1025   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1026   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1027   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1028 }\r
1029 \r
1030 VOID\r
1031 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1032 {\r
1033   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1034   GetCurrentDirectory(MSG_SIZ, dir);\r
1035   SetCurrentDirectory(installDir);\r
1036   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1037       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1038 \r
1039       if (cps->programLogo == NULL && appData.debugMode) {\r
1040           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1041       }\r
1042   } else if(appData.autoLogo) {\r
1043       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1044         char *opponent = "";\r
1045         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1046         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1047         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1048         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1049             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1050             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1051         }\r
1052       } else\r
1053       if(appData.directory[n] && appData.directory[n][0]) {\r
1054         SetCurrentDirectory(appData.directory[n]);\r
1055         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1056       }\r
1057   }\r
1058   SetCurrentDirectory(dir); /* return to prev directory */\r
1059 }\r
1060 \r
1061 VOID\r
1062 InitTextures()\r
1063 {\r
1064   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1065   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1066   \r
1067   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1068       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1069       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1070       liteBackTextureMode = appData.liteBackTextureMode;\r
1071 \r
1072       if (liteBackTexture == NULL && appData.debugMode) {\r
1073           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1074       }\r
1075   }\r
1076   \r
1077   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1078       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1079       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1080       darkBackTextureMode = appData.darkBackTextureMode;\r
1081 \r
1082       if (darkBackTexture == NULL && appData.debugMode) {\r
1083           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1084       }\r
1085   }\r
1086 }\r
1087 \r
1088 #ifndef SM_CXVIRTUALSCREEN\r
1089 #define SM_CXVIRTUALSCREEN 78\r
1090 #endif\r
1091 #ifndef SM_CYVIRTUALSCREEN\r
1092 #define SM_CYVIRTUALSCREEN 79\r
1093 #endif\r
1094 #ifndef SM_XVIRTUALSCREEN \r
1095 #define SM_XVIRTUALSCREEN 76\r
1096 #endif\r
1097 #ifndef SM_YVIRTUALSCREEN \r
1098 #define SM_YVIRTUALSCREEN 77\r
1099 #endif\r
1100 \r
1101 VOID\r
1102 InitGeometry()\r
1103 {\r
1104   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1105   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1106   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1107   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1108   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1109   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1110   screenGeometry.right = screenGeometry.left + screenWidth;\r
1111   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1112 }\r
1113 \r
1114 ChessProgramState broadcast;\r
1115 \r
1116 BOOL\r
1117 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1118 {\r
1119   HWND hwnd; /* Main window handle. */\r
1120   int ibs;\r
1121   WINDOWPLACEMENT wp;\r
1122   char *filepart;\r
1123 \r
1124   hInst = hInstance;    /* Store instance handle in our global variable */\r
1125   programName = szAppName;\r
1126 \r
1127   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1128     *filepart = NULLCHAR;\r
1129     SetCurrentDirectory(installDir);\r
1130   } else {\r
1131     GetCurrentDirectory(MSG_SIZ, installDir);\r
1132   }\r
1133   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1134   InitGeometry();\r
1135   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1136   /* xboard, and older WinBoards, controlled the move sound with the\r
1137      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1138      always turn the option on (so that the backend will call us),\r
1139      then let the user turn the sound off by setting it to silence if\r
1140      desired.  To accommodate old winboard.ini files saved by old\r
1141      versions of WinBoard, we also turn off the sound if the option\r
1142      was initially set to false. [HGM] taken out of InitAppData */\r
1143   if (!appData.ringBellAfterMoves) {\r
1144     sounds[(int)SoundMove].name = strdup("");\r
1145     appData.ringBellAfterMoves = TRUE;\r
1146   }\r
1147   if (appData.debugMode) {\r
1148     char *c = appData.nameOfDebugFile;\r
1149     if(strstr(c, "///") == c) {\r
1150       broadcast.which = "broadcaster";\r
1151       broadcast.pr   = NoProc;\r
1152       broadcast.isr  = NULL;\r
1153       broadcast.program = c + 3;\r
1154       broadcast.dir  = ".";\r
1155       broadcast.host = "localhost";\r
1156       StartChessProgram(&broadcast);\r
1157       debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");\r
1158     } else\r
1159     debugFP = fopen(c, "w");\r
1160     setbuf(debugFP, NULL);\r
1161   }\r
1162 \r
1163   LoadLanguageFile(appData.language);\r
1164 \r
1165   InitBackEnd1();\r
1166 \r
1167 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1168 //  InitEngineUCI( installDir, &second );\r
1169 \r
1170   /* Create a main window for this application instance. */\r
1171   hwnd = CreateWindow(szAppName, szTitle,\r
1172                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1173                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1174                       NULL, NULL, hInstance, NULL);\r
1175   hwndMain = hwnd;\r
1176 \r
1177   /* If window could not be created, return "failure" */\r
1178   if (!hwnd) {\r
1179     return (FALSE);\r
1180   }\r
1181 \r
1182   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1183   LoadLogo(&first, 0, FALSE);\r
1184   LoadLogo(&second, 1, appData.icsActive);\r
1185 \r
1186   SetUserLogo();\r
1187 \r
1188   iconWhite = LoadIcon(hInstance, "icon_white");\r
1189   iconBlack = LoadIcon(hInstance, "icon_black");\r
1190   iconCurrent = iconWhite;\r
1191   InitDrawingColors();\r
1192 \r
1193   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1194   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1195     /* Compute window size for each board size, and use the largest\r
1196        size that fits on this screen as the default. */\r
1197     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1198     if (boardSize == (BoardSize)-1 &&\r
1199         winH <= screenHeight\r
1200            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1201         && winW <= screenWidth) {\r
1202       boardSize = (BoardSize)ibs;\r
1203     }\r
1204   }\r
1205 \r
1206   InitDrawingSizes(boardSize, 0);\r
1207   RecentEngineMenu(appData.recentEngineList);\r
1208   InitMenuChecks();\r
1209   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1210 \r
1211   /* [AS] Load textures if specified */\r
1212   InitTextures();\r
1213 \r
1214   mysrandom( (unsigned) time(NULL) );\r
1215 \r
1216   /* [AS] Restore layout */\r
1217   if( wpMoveHistory.visible ) {\r
1218       MoveHistoryPopUp();\r
1219   }\r
1220 \r
1221   if( wpEvalGraph.visible ) {\r
1222       EvalGraphPopUp();\r
1223   }\r
1224 \r
1225   if( wpEngineOutput.visible ) {\r
1226       EngineOutputPopUp();\r
1227   }\r
1228 \r
1229   /* Make the window visible; update its client area; and return "success" */\r
1230   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1231   wp.length = sizeof(WINDOWPLACEMENT);\r
1232   wp.flags = 0;\r
1233   wp.showCmd = nCmdShow;\r
1234   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1235   wp.rcNormalPosition.left = wpMain.x;\r
1236   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1237   wp.rcNormalPosition.top = wpMain.y;\r
1238   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1239   SetWindowPlacement(hwndMain, &wp);\r
1240 \r
1241   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1242 \r
1243   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1244                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1245 \r
1246   if (hwndConsole) {\r
1247 #if AOT_CONSOLE\r
1248     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1249                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1250 #endif\r
1251     ShowWindow(hwndConsole, nCmdShow);\r
1252     SetActiveWindow(hwndConsole);\r
1253   }\r
1254   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1255   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1256 \r
1257   return TRUE;\r
1258 \r
1259 }\r
1260 \r
1261 VOID\r
1262 InitMenuChecks()\r
1263 {\r
1264   HMENU hmenu = GetMenu(hwndMain);\r
1265 \r
1266   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1267                         MF_BYCOMMAND|((appData.icsActive &&\r
1268                                        *appData.icsCommPort != NULLCHAR) ?\r
1269                                       MF_ENABLED : MF_GRAYED));\r
1270   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1271                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1272                                      MF_CHECKED : MF_UNCHECKED));\r
1273   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1274 }\r
1275 \r
1276 //---------------------------------------------------------------------------------------------------------\r
1277 \r
1278 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1279 #define XBOARD FALSE\r
1280 \r
1281 #define OPTCHAR "/"\r
1282 #define SEPCHAR "="\r
1283 #define TOPLEVEL 0\r
1284 \r
1285 #include "args.h"\r
1286 \r
1287 // front-end part of option handling\r
1288 \r
1289 VOID\r
1290 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1291 {\r
1292   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1293   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1294   DeleteDC(hdc);\r
1295   lf->lfWidth = 0;\r
1296   lf->lfEscapement = 0;\r
1297   lf->lfOrientation = 0;\r
1298   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1299   lf->lfItalic = mfp->italic;\r
1300   lf->lfUnderline = mfp->underline;\r
1301   lf->lfStrikeOut = mfp->strikeout;\r
1302   lf->lfCharSet = mfp->charset;\r
1303   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1304 \r
1305 \r
1306 \r
1307   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1308   lf->lfQuality = DEFAULT_QUALITY;\r
1309   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1310     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1311 }\r
1312 \r
1313 void\r
1314 CreateFontInMF(MyFont *mf)\r
1315\r
1316   LFfromMFP(&mf->lf, &mf->mfp);\r
1317   if (mf->hf) DeleteObject(mf->hf);\r
1318   mf->hf = CreateFontIndirect(&mf->lf);\r
1319 }\r
1320 \r
1321 // [HGM] This platform-dependent table provides the location for storing the color info\r
1322 void *\r
1323 colorVariable[] = {\r
1324   &whitePieceColor, \r
1325   &blackPieceColor, \r
1326   &lightSquareColor,\r
1327   &darkSquareColor, \r
1328   &highlightSquareColor,\r
1329   &premoveHighlightColor,\r
1330   NULL,\r
1331   &consoleBackgroundColor,\r
1332   &appData.fontForeColorWhite,\r
1333   &appData.fontBackColorWhite,\r
1334   &appData.fontForeColorBlack,\r
1335   &appData.fontBackColorBlack,\r
1336   &appData.evalHistColorWhite,\r
1337   &appData.evalHistColorBlack,\r
1338   &appData.highlightArrowColor,\r
1339 };\r
1340 \r
1341 /* Command line font name parser.  NULL name means do nothing.\r
1342    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1343    For backward compatibility, syntax without the colon is also\r
1344    accepted, but font names with digits in them won't work in that case.\r
1345 */\r
1346 VOID\r
1347 ParseFontName(char *name, MyFontParams *mfp)\r
1348 {\r
1349   char *p, *q;\r
1350   if (name == NULL) return;\r
1351   p = name;\r
1352   q = strchr(p, ':');\r
1353   if (q) {\r
1354     if (q - p >= sizeof(mfp->faceName))\r
1355       ExitArgError(_("Font name too long:"), name, TRUE);\r
1356     memcpy(mfp->faceName, p, q - p);\r
1357     mfp->faceName[q - p] = NULLCHAR;\r
1358     p = q + 1;\r
1359   } else {\r
1360     q = mfp->faceName;\r
1361 \r
1362     while (*p && !isdigit(*p)) {\r
1363       *q++ = *p++;\r
1364       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1365         ExitArgError(_("Font name too long:"), name, TRUE);\r
1366     }\r
1367     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1368     *q = NULLCHAR;\r
1369   }\r
1370   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1371   mfp->pointSize = (float) atof(p);\r
1372   mfp->bold = (strchr(p, 'b') != NULL);\r
1373   mfp->italic = (strchr(p, 'i') != NULL);\r
1374   mfp->underline = (strchr(p, 'u') != NULL);\r
1375   mfp->strikeout = (strchr(p, 's') != NULL);\r
1376   mfp->charset = DEFAULT_CHARSET;\r
1377   q = strchr(p, 'c');\r
1378   if (q)\r
1379     mfp->charset = (BYTE) atoi(q+1);\r
1380 }\r
1381 \r
1382 void\r
1383 ParseFont(char *name, int number)\r
1384 { // wrapper to shield back-end from 'font'\r
1385   ParseFontName(name, &font[boardSize][number]->mfp);\r
1386 }\r
1387 \r
1388 void\r
1389 SetFontDefaults()\r
1390 { // in WB  we have a 2D array of fonts; this initializes their description\r
1391   int i, j;\r
1392   /* Point font array elements to structures and\r
1393      parse default font names */\r
1394   for (i=0; i<NUM_FONTS; i++) {\r
1395     for (j=0; j<NUM_SIZES; j++) {\r
1396       font[j][i] = &fontRec[j][i];\r
1397       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1398     }\r
1399   }\r
1400 }\r
1401 \r
1402 void\r
1403 CreateFonts()\r
1404 { // here we create the actual fonts from the selected descriptions\r
1405   int i, j;\r
1406   for (i=0; i<NUM_FONTS; i++) {\r
1407     for (j=0; j<NUM_SIZES; j++) {\r
1408       CreateFontInMF(font[j][i]);\r
1409     }\r
1410   }\r
1411 }\r
1412 /* Color name parser.\r
1413    X version accepts X color names, but this one\r
1414    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1415 COLORREF\r
1416 ParseColorName(char *name)\r
1417 {\r
1418   int red, green, blue, count;\r
1419   char buf[MSG_SIZ];\r
1420 \r
1421   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1422   if (count != 3) {\r
1423     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1424       &red, &green, &blue);\r
1425   }\r
1426   if (count != 3) {\r
1427     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1428     DisplayError(buf, 0);\r
1429     return RGB(0, 0, 0);\r
1430   }\r
1431   return PALETTERGB(red, green, blue);\r
1432 }\r
1433 \r
1434 void\r
1435 ParseColor(int n, char *name)\r
1436 { // for WinBoard the color is an int, which needs to be derived from the string\r
1437   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1438 }\r
1439 \r
1440 void\r
1441 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1442 {\r
1443   char *e = argValue;\r
1444   int eff = 0;\r
1445 \r
1446   while (*e) {\r
1447     if (*e == 'b')      eff |= CFE_BOLD;\r
1448     else if (*e == 'i') eff |= CFE_ITALIC;\r
1449     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1450     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1451     else if (*e == '#' || isdigit(*e)) break;\r
1452     e++;\r
1453   }\r
1454   *effects = eff;\r
1455   *color   = ParseColorName(e);\r
1456 }\r
1457 \r
1458 void\r
1459 ParseTextAttribs(ColorClass cc, char *s)\r
1460 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1461     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1462     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1463 }\r
1464 \r
1465 void\r
1466 ParseBoardSize(void *addr, char *name)\r
1467 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1468   BoardSize bs = SizeTiny;\r
1469   while (sizeInfo[bs].name != NULL) {\r
1470     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1471         *(BoardSize *)addr = bs;\r
1472         return;\r
1473     }\r
1474     bs++;\r
1475   }\r
1476   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1477 }\r
1478 \r
1479 void\r
1480 LoadAllSounds()\r
1481 { // [HGM] import name from appData first\r
1482   ColorClass cc;\r
1483   SoundClass sc;\r
1484   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1485     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1486     textAttribs[cc].sound.data = NULL;\r
1487     MyLoadSound(&textAttribs[cc].sound);\r
1488   }\r
1489   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1490     textAttribs[cc].sound.name = strdup("");\r
1491     textAttribs[cc].sound.data = NULL;\r
1492   }\r
1493   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1494     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1495     sounds[sc].data = NULL;\r
1496     MyLoadSound(&sounds[sc]);\r
1497   }\r
1498 }\r
1499 \r
1500 void\r
1501 SetCommPortDefaults()\r
1502 {\r
1503    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1504   dcb.DCBlength = sizeof(DCB);\r
1505   dcb.BaudRate = 9600;\r
1506   dcb.fBinary = TRUE;\r
1507   dcb.fParity = FALSE;\r
1508   dcb.fOutxCtsFlow = FALSE;\r
1509   dcb.fOutxDsrFlow = FALSE;\r
1510   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1511   dcb.fDsrSensitivity = FALSE;\r
1512   dcb.fTXContinueOnXoff = TRUE;\r
1513   dcb.fOutX = FALSE;\r
1514   dcb.fInX = FALSE;\r
1515   dcb.fNull = FALSE;\r
1516   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1517   dcb.fAbortOnError = FALSE;\r
1518   dcb.ByteSize = 7;\r
1519   dcb.Parity = SPACEPARITY;\r
1520   dcb.StopBits = ONESTOPBIT;\r
1521 }\r
1522 \r
1523 // [HGM] args: these three cases taken out to stay in front-end\r
1524 void\r
1525 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1526 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1527         // while the curent board size determines the element. This system should be ported to XBoard.\r
1528         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1529         int bs;\r
1530         for (bs=0; bs<NUM_SIZES; bs++) {\r
1531           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1532           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1533           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1534             ad->argName, mfp->faceName, mfp->pointSize,\r
1535             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1536             mfp->bold ? "b" : "",\r
1537             mfp->italic ? "i" : "",\r
1538             mfp->underline ? "u" : "",\r
1539             mfp->strikeout ? "s" : "",\r
1540             (int)mfp->charset);\r
1541         }\r
1542       }\r
1543 \r
1544 void\r
1545 ExportSounds()\r
1546 { // [HGM] copy the names from the internal WB variables to appData\r
1547   ColorClass cc;\r
1548   SoundClass sc;\r
1549   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1550     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1551   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1552     (&appData.soundMove)[sc] = sounds[sc].name;\r
1553 }\r
1554 \r
1555 void\r
1556 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1557 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1558         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1559         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1560           (ta->effects & CFE_BOLD) ? "b" : "",\r
1561           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1562           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1563           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1564           (ta->effects) ? " " : "",\r
1565           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1566       }\r
1567 \r
1568 void\r
1569 SaveColor(FILE *f, ArgDescriptor *ad)\r
1570 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1571         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1572         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1573           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1574 }\r
1575 \r
1576 void\r
1577 SaveBoardSize(FILE *f, char *name, void *addr)\r
1578 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1579   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1580 }\r
1581 \r
1582 void\r
1583 ParseCommPortSettings(char *s)\r
1584 { // wrapper to keep dcb from back-end\r
1585   ParseCommSettings(s, &dcb);\r
1586 }\r
1587 \r
1588 void\r
1589 GetWindowCoords()\r
1590 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1591   GetActualPlacement(hwndMain, &wpMain);\r
1592   GetActualPlacement(hwndConsole, &wpConsole);\r
1593   GetActualPlacement(commentDialog, &wpComment);\r
1594   GetActualPlacement(editTagsDialog, &wpTags);\r
1595   GetActualPlacement(gameListDialog, &wpGameList);\r
1596   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1597   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1598   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1599 }\r
1600 \r
1601 void\r
1602 PrintCommPortSettings(FILE *f, char *name)\r
1603 { // wrapper to shield back-end from DCB\r
1604       PrintCommSettings(f, name, &dcb);\r
1605 }\r
1606 \r
1607 int\r
1608 MySearchPath(char *installDir, char *name, char *fullname)\r
1609 {\r
1610   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1611   if(name[0]== '%') {\r
1612     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1613     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1614       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1615       *strchr(buf, '%') = 0;\r
1616       strcat(fullname, getenv(buf));\r
1617       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1618     }\r
1619     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1620     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1621     return (int) strlen(fullname);\r
1622   }\r
1623   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1624 }\r
1625 \r
1626 int\r
1627 MyGetFullPathName(char *name, char *fullname)\r
1628 {\r
1629   char *dummy;\r
1630   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1631 }\r
1632 \r
1633 int\r
1634 MainWindowUp()\r
1635 { // [HGM] args: allows testing if main window is realized from back-end\r
1636   return hwndMain != NULL;\r
1637 }\r
1638 \r
1639 void\r
1640 PopUpStartupDialog()\r
1641 {\r
1642     FARPROC lpProc;\r
1643     \r
1644     LoadLanguageFile(appData.language);\r
1645     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1646     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1647     FreeProcInstance(lpProc);\r
1648 }\r
1649 \r
1650 /*---------------------------------------------------------------------------*\\r
1651  *\r
1652  * GDI board drawing routines\r
1653  *\r
1654 \*---------------------------------------------------------------------------*/\r
1655 \r
1656 /* [AS] Draw square using background texture */\r
1657 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1658 {\r
1659     XFORM   x;\r
1660 \r
1661     if( mode == 0 ) {\r
1662         return; /* Should never happen! */\r
1663     }\r
1664 \r
1665     SetGraphicsMode( dst, GM_ADVANCED );\r
1666 \r
1667     switch( mode ) {\r
1668     case 1:\r
1669         /* Identity */\r
1670         break;\r
1671     case 2:\r
1672         /* X reflection */\r
1673         x.eM11 = -1.0;\r
1674         x.eM12 = 0;\r
1675         x.eM21 = 0;\r
1676         x.eM22 = 1.0;\r
1677         x.eDx = (FLOAT) dw + dx - 1;\r
1678         x.eDy = 0;\r
1679         dx = 0;\r
1680         SetWorldTransform( dst, &x );\r
1681         break;\r
1682     case 3:\r
1683         /* Y reflection */\r
1684         x.eM11 = 1.0;\r
1685         x.eM12 = 0;\r
1686         x.eM21 = 0;\r
1687         x.eM22 = -1.0;\r
1688         x.eDx = 0;\r
1689         x.eDy = (FLOAT) dh + dy - 1;\r
1690         dy = 0;\r
1691         SetWorldTransform( dst, &x );\r
1692         break;\r
1693     case 4:\r
1694         /* X/Y flip */\r
1695         x.eM11 = 0;\r
1696         x.eM12 = 1.0;\r
1697         x.eM21 = 1.0;\r
1698         x.eM22 = 0;\r
1699         x.eDx = (FLOAT) dx;\r
1700         x.eDy = (FLOAT) dy;\r
1701         dx = 0;\r
1702         dy = 0;\r
1703         SetWorldTransform( dst, &x );\r
1704         break;\r
1705     }\r
1706 \r
1707     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1708 \r
1709     x.eM11 = 1.0;\r
1710     x.eM12 = 0;\r
1711     x.eM21 = 0;\r
1712     x.eM22 = 1.0;\r
1713     x.eDx = 0;\r
1714     x.eDy = 0;\r
1715     SetWorldTransform( dst, &x );\r
1716 \r
1717     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1718 }\r
1719 \r
1720 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1721 enum {\r
1722     PM_WP = (int) WhitePawn, \r
1723     PM_WN = (int) WhiteKnight, \r
1724     PM_WB = (int) WhiteBishop, \r
1725     PM_WR = (int) WhiteRook, \r
1726     PM_WQ = (int) WhiteQueen, \r
1727     PM_WF = (int) WhiteFerz, \r
1728     PM_WW = (int) WhiteWazir, \r
1729     PM_WE = (int) WhiteAlfil, \r
1730     PM_WM = (int) WhiteMan, \r
1731     PM_WO = (int) WhiteCannon, \r
1732     PM_WU = (int) WhiteUnicorn, \r
1733     PM_WH = (int) WhiteNightrider, \r
1734     PM_WA = (int) WhiteAngel, \r
1735     PM_WC = (int) WhiteMarshall, \r
1736     PM_WAB = (int) WhiteCardinal, \r
1737     PM_WD = (int) WhiteDragon, \r
1738     PM_WL = (int) WhiteLance, \r
1739     PM_WS = (int) WhiteCobra, \r
1740     PM_WV = (int) WhiteFalcon, \r
1741     PM_WSG = (int) WhiteSilver, \r
1742     PM_WG = (int) WhiteGrasshopper, \r
1743     PM_WK = (int) WhiteKing,\r
1744     PM_BP = (int) BlackPawn, \r
1745     PM_BN = (int) BlackKnight, \r
1746     PM_BB = (int) BlackBishop, \r
1747     PM_BR = (int) BlackRook, \r
1748     PM_BQ = (int) BlackQueen, \r
1749     PM_BF = (int) BlackFerz, \r
1750     PM_BW = (int) BlackWazir, \r
1751     PM_BE = (int) BlackAlfil, \r
1752     PM_BM = (int) BlackMan,\r
1753     PM_BO = (int) BlackCannon, \r
1754     PM_BU = (int) BlackUnicorn, \r
1755     PM_BH = (int) BlackNightrider, \r
1756     PM_BA = (int) BlackAngel, \r
1757     PM_BC = (int) BlackMarshall, \r
1758     PM_BG = (int) BlackGrasshopper, \r
1759     PM_BAB = (int) BlackCardinal,\r
1760     PM_BD = (int) BlackDragon,\r
1761     PM_BL = (int) BlackLance,\r
1762     PM_BS = (int) BlackCobra,\r
1763     PM_BV = (int) BlackFalcon,\r
1764     PM_BSG = (int) BlackSilver,\r
1765     PM_BK = (int) BlackKing\r
1766 };\r
1767 \r
1768 static HFONT hPieceFont = NULL;\r
1769 static HBITMAP hPieceMask[(int) EmptySquare];\r
1770 static HBITMAP hPieceFace[(int) EmptySquare];\r
1771 static int fontBitmapSquareSize = 0;\r
1772 static char pieceToFontChar[(int) EmptySquare] =\r
1773                               { 'p', 'n', 'b', 'r', 'q', \r
1774                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1775                       'k', 'o', 'm', 'v', 't', 'w', \r
1776                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1777                                                               'l' };\r
1778 \r
1779 extern BOOL SetCharTable( char *table, const char * map );\r
1780 /* [HGM] moved to backend.c */\r
1781 \r
1782 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1783 {\r
1784     HBRUSH hbrush;\r
1785     BYTE r1 = GetRValue( color );\r
1786     BYTE g1 = GetGValue( color );\r
1787     BYTE b1 = GetBValue( color );\r
1788     BYTE r2 = r1 / 2;\r
1789     BYTE g2 = g1 / 2;\r
1790     BYTE b2 = b1 / 2;\r
1791     RECT rc;\r
1792 \r
1793     /* Create a uniform background first */\r
1794     hbrush = CreateSolidBrush( color );\r
1795     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1796     FillRect( hdc, &rc, hbrush );\r
1797     DeleteObject( hbrush );\r
1798     \r
1799     if( mode == 1 ) {\r
1800         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1801         int steps = squareSize / 2;\r
1802         int i;\r
1803 \r
1804         for( i=0; i<steps; i++ ) {\r
1805             BYTE r = r1 - (r1-r2) * i / steps;\r
1806             BYTE g = g1 - (g1-g2) * i / steps;\r
1807             BYTE b = b1 - (b1-b2) * i / steps;\r
1808 \r
1809             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1810             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1811             FillRect( hdc, &rc, hbrush );\r
1812             DeleteObject(hbrush);\r
1813         }\r
1814     }\r
1815     else if( mode == 2 ) {\r
1816         /* Diagonal gradient, good more or less for every piece */\r
1817         POINT triangle[3];\r
1818         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1819         HBRUSH hbrush_old;\r
1820         int steps = squareSize;\r
1821         int i;\r
1822 \r
1823         triangle[0].x = squareSize - steps;\r
1824         triangle[0].y = squareSize;\r
1825         triangle[1].x = squareSize;\r
1826         triangle[1].y = squareSize;\r
1827         triangle[2].x = squareSize;\r
1828         triangle[2].y = squareSize - steps;\r
1829 \r
1830         for( i=0; i<steps; i++ ) {\r
1831             BYTE r = r1 - (r1-r2) * i / steps;\r
1832             BYTE g = g1 - (g1-g2) * i / steps;\r
1833             BYTE b = b1 - (b1-b2) * i / steps;\r
1834 \r
1835             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1836             hbrush_old = SelectObject( hdc, hbrush );\r
1837             Polygon( hdc, triangle, 3 );\r
1838             SelectObject( hdc, hbrush_old );\r
1839             DeleteObject(hbrush);\r
1840             triangle[0].x++;\r
1841             triangle[2].y++;\r
1842         }\r
1843 \r
1844         SelectObject( hdc, hpen );\r
1845     }\r
1846 }\r
1847 \r
1848 /*\r
1849     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1850     seems to work ok. The main problem here is to find the "inside" of a chess\r
1851     piece: follow the steps as explained below.\r
1852 */\r
1853 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1854 {\r
1855     HBITMAP hbm;\r
1856     HBITMAP hbm_old;\r
1857     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1858     RECT rc;\r
1859     SIZE sz;\r
1860 \r
1861 \r
1862     POINT pt;\r
1863     int backColor = whitePieceColor; \r
1864     int foreColor = blackPieceColor;\r
1865     \r
1866     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1867         backColor = appData.fontBackColorWhite;\r
1868         foreColor = appData.fontForeColorWhite;\r
1869     }\r
1870     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1871         backColor = appData.fontBackColorBlack;\r
1872         foreColor = appData.fontForeColorBlack;\r
1873     }\r
1874 \r
1875     /* Mask */\r
1876     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1877 \r
1878     hbm_old = SelectObject( hdc, hbm );\r
1879 \r
1880     rc.left = 0;\r
1881     rc.top = 0;\r
1882     rc.right = squareSize;\r
1883     rc.bottom = squareSize;\r
1884 \r
1885     /* Step 1: background is now black */\r
1886     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1887 \r
1888     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1889 \r
1890     pt.x = (squareSize - sz.cx) / 2;\r
1891     pt.y = (squareSize - sz.cy) / 2;\r
1892 \r
1893     SetBkMode( hdc, TRANSPARENT );\r
1894     SetTextColor( hdc, chroma );\r
1895     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1896     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1897 \r
1898     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1899     /* Step 3: the area outside the piece is filled with white */\r
1900 //    FloodFill( hdc, 0, 0, chroma );\r
1901     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1902     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1903     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1904     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1905     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1906     /* \r
1907         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1908         but if the start point is not inside the piece we're lost!\r
1909         There should be a better way to do this... if we could create a region or path\r
1910         from the fill operation we would be fine for example.\r
1911     */\r
1912 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1913     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1914 \r
1915     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1916         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1917         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1918 \r
1919         SelectObject( dc2, bm2 );\r
1920         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1921         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1922         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1923         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1924         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1925 \r
1926         DeleteDC( dc2 );\r
1927         DeleteObject( bm2 );\r
1928     }\r
1929 \r
1930     SetTextColor( hdc, 0 );\r
1931     /* \r
1932         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1933         draw the piece again in black for safety.\r
1934     */\r
1935     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1936 \r
1937     SelectObject( hdc, hbm_old );\r
1938 \r
1939     if( hPieceMask[index] != NULL ) {\r
1940         DeleteObject( hPieceMask[index] );\r
1941     }\r
1942 \r
1943     hPieceMask[index] = hbm;\r
1944 \r
1945     /* Face */\r
1946     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1947 \r
1948     SelectObject( hdc, hbm );\r
1949 \r
1950     {\r
1951         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1952         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1953         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1954 \r
1955         SelectObject( dc1, hPieceMask[index] );\r
1956         SelectObject( dc2, bm2 );\r
1957         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1958         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1959         \r
1960         /* \r
1961             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1962             the piece background and deletes (makes transparent) the rest.\r
1963             Thanks to that mask, we are free to paint the background with the greates\r
1964             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1965             We use this, to make gradients and give the pieces a "roundish" look.\r
1966         */\r
1967         SetPieceBackground( hdc, backColor, 2 );\r
1968         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1969 \r
1970         DeleteDC( dc2 );\r
1971         DeleteDC( dc1 );\r
1972         DeleteObject( bm2 );\r
1973     }\r
1974 \r
1975     SetTextColor( hdc, foreColor );\r
1976     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1977 \r
1978     SelectObject( hdc, hbm_old );\r
1979 \r
1980     if( hPieceFace[index] != NULL ) {\r
1981         DeleteObject( hPieceFace[index] );\r
1982     }\r
1983 \r
1984     hPieceFace[index] = hbm;\r
1985 }\r
1986 \r
1987 static int TranslatePieceToFontPiece( int piece )\r
1988 {\r
1989     switch( piece ) {\r
1990     case BlackPawn:\r
1991         return PM_BP;\r
1992     case BlackKnight:\r
1993         return PM_BN;\r
1994     case BlackBishop:\r
1995         return PM_BB;\r
1996     case BlackRook:\r
1997         return PM_BR;\r
1998     case BlackQueen:\r
1999         return PM_BQ;\r
2000     case BlackKing:\r
2001         return PM_BK;\r
2002     case WhitePawn:\r
2003         return PM_WP;\r
2004     case WhiteKnight:\r
2005         return PM_WN;\r
2006     case WhiteBishop:\r
2007         return PM_WB;\r
2008     case WhiteRook:\r
2009         return PM_WR;\r
2010     case WhiteQueen:\r
2011         return PM_WQ;\r
2012     case WhiteKing:\r
2013         return PM_WK;\r
2014 \r
2015     case BlackAngel:\r
2016         return PM_BA;\r
2017     case BlackMarshall:\r
2018         return PM_BC;\r
2019     case BlackFerz:\r
2020         return PM_BF;\r
2021     case BlackNightrider:\r
2022         return PM_BH;\r
2023     case BlackAlfil:\r
2024         return PM_BE;\r
2025     case BlackWazir:\r
2026         return PM_BW;\r
2027     case BlackUnicorn:\r
2028         return PM_BU;\r
2029     case BlackCannon:\r
2030         return PM_BO;\r
2031     case BlackGrasshopper:\r
2032         return PM_BG;\r
2033     case BlackMan:\r
2034         return PM_BM;\r
2035     case BlackSilver:\r
2036         return PM_BSG;\r
2037     case BlackLance:\r
2038         return PM_BL;\r
2039     case BlackFalcon:\r
2040         return PM_BV;\r
2041     case BlackCobra:\r
2042         return PM_BS;\r
2043     case BlackCardinal:\r
2044         return PM_BAB;\r
2045     case BlackDragon:\r
2046         return PM_BD;\r
2047 \r
2048     case WhiteAngel:\r
2049         return PM_WA;\r
2050     case WhiteMarshall:\r
2051         return PM_WC;\r
2052     case WhiteFerz:\r
2053         return PM_WF;\r
2054     case WhiteNightrider:\r
2055         return PM_WH;\r
2056     case WhiteAlfil:\r
2057         return PM_WE;\r
2058     case WhiteWazir:\r
2059         return PM_WW;\r
2060     case WhiteUnicorn:\r
2061         return PM_WU;\r
2062     case WhiteCannon:\r
2063         return PM_WO;\r
2064     case WhiteGrasshopper:\r
2065         return PM_WG;\r
2066     case WhiteMan:\r
2067         return PM_WM;\r
2068     case WhiteSilver:\r
2069         return PM_WSG;\r
2070     case WhiteLance:\r
2071         return PM_WL;\r
2072     case WhiteFalcon:\r
2073         return PM_WV;\r
2074     case WhiteCobra:\r
2075         return PM_WS;\r
2076     case WhiteCardinal:\r
2077         return PM_WAB;\r
2078     case WhiteDragon:\r
2079         return PM_WD;\r
2080     }\r
2081 \r
2082     return 0;\r
2083 }\r
2084 \r
2085 void CreatePiecesFromFont()\r
2086 {\r
2087     LOGFONT lf;\r
2088     HDC hdc_window = NULL;\r
2089     HDC hdc = NULL;\r
2090     HFONT hfont_old;\r
2091     int fontHeight;\r
2092     int i;\r
2093 \r
2094     if( fontBitmapSquareSize < 0 ) {\r
2095         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2096         return;\r
2097     }\r
2098 \r
2099     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2100             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2101         fontBitmapSquareSize = -1;\r
2102         return;\r
2103     }\r
2104 \r
2105     if( fontBitmapSquareSize != squareSize ) {\r
2106         hdc_window = GetDC( hwndMain );\r
2107         hdc = CreateCompatibleDC( hdc_window );\r
2108 \r
2109         if( hPieceFont != NULL ) {\r
2110             DeleteObject( hPieceFont );\r
2111         }\r
2112         else {\r
2113             for( i=0; i<=(int)BlackKing; i++ ) {\r
2114                 hPieceMask[i] = NULL;\r
2115                 hPieceFace[i] = NULL;\r
2116             }\r
2117         }\r
2118 \r
2119         fontHeight = 75;\r
2120 \r
2121         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2122             fontHeight = appData.fontPieceSize;\r
2123         }\r
2124 \r
2125         fontHeight = (fontHeight * squareSize) / 100;\r
2126 \r
2127         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2128         lf.lfWidth = 0;\r
2129         lf.lfEscapement = 0;\r
2130         lf.lfOrientation = 0;\r
2131         lf.lfWeight = FW_NORMAL;\r
2132         lf.lfItalic = 0;\r
2133         lf.lfUnderline = 0;\r
2134         lf.lfStrikeOut = 0;\r
2135         lf.lfCharSet = DEFAULT_CHARSET;\r
2136         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2137         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2138         lf.lfQuality = PROOF_QUALITY;\r
2139         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2140         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2141         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2142 \r
2143         hPieceFont = CreateFontIndirect( &lf );\r
2144 \r
2145         if( hPieceFont == NULL ) {\r
2146             fontBitmapSquareSize = -2;\r
2147         }\r
2148         else {\r
2149             /* Setup font-to-piece character table */\r
2150             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2151                 /* No (or wrong) global settings, try to detect the font */\r
2152                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2153                     /* Alpha */\r
2154                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2155                 }\r
2156                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2157                     /* DiagramTT* family */\r
2158                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2159                 }\r
2160                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2161                     /* Fairy symbols */\r
2162                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2163                 }\r
2164                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2165                     /* Good Companion (Some characters get warped as literal :-( */\r
2166                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2167                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2168                     SetCharTable(pieceToFontChar, s);\r
2169                 }\r
2170                 else {\r
2171                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2172                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2173                 }\r
2174             }\r
2175 \r
2176             /* Create bitmaps */\r
2177             hfont_old = SelectObject( hdc, hPieceFont );\r
2178             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2179                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2180                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2181 \r
2182             SelectObject( hdc, hfont_old );\r
2183 \r
2184             fontBitmapSquareSize = squareSize;\r
2185         }\r
2186     }\r
2187 \r
2188     if( hdc != NULL ) {\r
2189         DeleteDC( hdc );\r
2190     }\r
2191 \r
2192     if( hdc_window != NULL ) {\r
2193         ReleaseDC( hwndMain, hdc_window );\r
2194     }\r
2195 }\r
2196 \r
2197 HBITMAP\r
2198 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2199 {\r
2200   char name[128], buf[MSG_SIZ], *ids = "pnbrqfeicwmohajgdvlsukaacvdklnwpwnwlwswolfgnuzebracameltowersword", *p;\r
2201 \r
2202     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2203   if(appData.pieceDirectory[0]) {\r
2204     HBITMAP res;\r
2205     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2206     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2207     if(res) return res;\r
2208     p = strstr(ids, piece);\r
2209     if(p) { // if we could reconstruct canonical piece number, try the pieceNNN_ format before falling back on built-ins\r
2210       int n = p - ids;\r
2211       switch(n) {\r
2212         case 21: n = WhiteKing; break;\r
2213         case 22: n = WhiteAngel; break;\r
2214         case 24: n = WhiteSilver; break;\r
2215         case 26: n = WhiteDragon; break;\r
2216         case 28: n = WhiteLion; break;\r
2217         case 30: n = WhiteTokin; break;\r
2218         case 32: n = WhitePKnight; break;\r
2219         case 34: n = WhitePLance; break;\r
2220         case 36: n = WhitePSilver; break;\r
2221         case 38: n = WhiteWolf; break;\r
2222         case 42: n = WhiteGnu; break;\r
2223         case 45: n = WhiteZebra; break;\r
2224         case 50: n = WhiteCamel; break;\r
2225         case 55: n = WhiteTower; break;\r
2226         case 60: n = WhiteSword; break;\r
2227       }\r
2228       snprintf(buf, MSG_SIZ, "%s\\piece%d_%d%s.bmp", appData.pieceDirectory, n, squareSize, suffix);\r
2229       res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2230       if(res) return res;\r
2231     }\r
2232   }\r
2233   if (gameInfo.event &&\r
2234       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2235       strcmp(name, "k80s") == 0) {\r
2236     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2237   }\r
2238   return LoadBitmap(hinst, name);\r
2239 }\r
2240 \r
2241 \r
2242 /* Insert a color into the program's logical palette\r
2243    structure.  This code assumes the given color is\r
2244    the result of the RGB or PALETTERGB macro, and it\r
2245    knows how those macros work (which is documented).\r
2246 */\r
2247 VOID\r
2248 InsertInPalette(COLORREF color)\r
2249 {\r
2250   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2251 \r
2252   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2253     DisplayFatalError(_("Too many colors"), 0, 1);\r
2254     pLogPal->palNumEntries--;\r
2255     return;\r
2256   }\r
2257 \r
2258   pe->peFlags = (char) 0;\r
2259   pe->peRed = (char) (0xFF & color);\r
2260   pe->peGreen = (char) (0xFF & (color >> 8));\r
2261   pe->peBlue = (char) (0xFF & (color >> 16));\r
2262   return;\r
2263 }\r
2264 \r
2265 \r
2266 VOID\r
2267 InitDrawingColors()\r
2268 {\r
2269   int i;\r
2270   if (pLogPal == NULL) {\r
2271     /* Allocate enough memory for a logical palette with\r
2272      * PALETTESIZE entries and set the size and version fields\r
2273      * of the logical palette structure.\r
2274      */\r
2275     pLogPal = (NPLOGPALETTE)\r
2276       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2277                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2278     pLogPal->palVersion    = 0x300;\r
2279   }\r
2280   pLogPal->palNumEntries = 0;\r
2281 \r
2282   InsertInPalette(lightSquareColor);\r
2283   InsertInPalette(darkSquareColor);\r
2284   InsertInPalette(whitePieceColor);\r
2285   InsertInPalette(blackPieceColor);\r
2286   InsertInPalette(highlightSquareColor);\r
2287   InsertInPalette(premoveHighlightColor);\r
2288 \r
2289   /*  create a logical color palette according the information\r
2290    *  in the LOGPALETTE structure.\r
2291    */\r
2292   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2293 \r
2294   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2295   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2296   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2297   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2298   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2299   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2300   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2301     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2302 \r
2303    /* [AS] Force rendering of the font-based pieces */\r
2304   if( fontBitmapSquareSize > 0 ) {\r
2305     fontBitmapSquareSize = 0;\r
2306   }\r
2307 }\r
2308 \r
2309 \r
2310 int\r
2311 BoardWidth(int boardSize, int n)\r
2312 { /* [HGM] argument n added to allow different width and height */\r
2313   int lineGap = sizeInfo[boardSize].lineGap;\r
2314 \r
2315   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2316       lineGap = appData.overrideLineGap;\r
2317   }\r
2318 \r
2319   return (n + 1) * lineGap +\r
2320           n * sizeInfo[boardSize].squareSize;\r
2321 }\r
2322 \r
2323 /* Respond to board resize by dragging edge */\r
2324 VOID\r
2325 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2326 {\r
2327   BoardSize newSize = NUM_SIZES - 1;\r
2328   static int recurse = 0;\r
2329   if (IsIconic(hwndMain)) return;\r
2330   if (recurse > 0) return;\r
2331   recurse++;\r
2332   while (newSize > 0) {\r
2333         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2334         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2335            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2336     newSize--;\r
2337   } \r
2338   boardSize = newSize;\r
2339   InitDrawingSizes(boardSize, flags);\r
2340   recurse--;\r
2341 }\r
2342 \r
2343 \r
2344 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2345 \r
2346 VOID\r
2347 InitDrawingSizes(BoardSize boardSize, int flags)\r
2348 {\r
2349   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2350   ChessSquare piece;\r
2351   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2352   HDC hdc;\r
2353   SIZE clockSize, messageSize;\r
2354   HFONT oldFont;\r
2355   char buf[MSG_SIZ];\r
2356   char *str;\r
2357   HMENU hmenu = GetMenu(hwndMain);\r
2358   RECT crect, wrect, oldRect;\r
2359   int offby;\r
2360   LOGBRUSH logbrush;\r
2361   VariantClass v = gameInfo.variant;\r
2362 \r
2363   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2364   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2365 \r
2366   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2367   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2368   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2369   oldBoardSize = boardSize;\r
2370 \r
2371   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2372   { // correct board size to one where built-in pieces exist\r
2373     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2374        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2375 \r
2376       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2377       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2378       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2379       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2380       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2381                                    boardSize = SizeMiddling;\r
2382     }\r
2383   }\r
2384   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2385 \r
2386   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2387   oldRect.top = wpMain.y;\r
2388   oldRect.right = wpMain.x + wpMain.width;\r
2389   oldRect.bottom = wpMain.y + wpMain.height;\r
2390 \r
2391   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2392   smallLayout = sizeInfo[boardSize].smallLayout;\r
2393   squareSize = sizeInfo[boardSize].squareSize;\r
2394   lineGap = sizeInfo[boardSize].lineGap;\r
2395   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2396   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2397 \r
2398   // [HGM] decide on tininess based on total board width rather than square size\r
2399   tinyLayout = squareSize * (BOARD_WIDTH);\r
2400   tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;\r
2401 \r
2402   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2403       lineGap = appData.overrideLineGap;\r
2404   }\r
2405 \r
2406   if (tinyLayout != oldTinyLayout) {\r
2407     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2408     if (tinyLayout == 2) {\r
2409       style &= ~WS_SYSMENU;\r
2410       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2411                  "&Minimize\tCtrl+F4");\r
2412     } else {\r
2413       style |= WS_SYSMENU;\r
2414       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2415     }\r
2416     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2417 \r
2418     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2419       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2420         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2421     }\r
2422     DrawMenuBar(hwndMain);\r
2423   }\r
2424 \r
2425   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2426   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2427 \r
2428   /* Get text area sizes */\r
2429   hdc = GetDC(hwndMain);\r
2430   if (appData.clockMode) {\r
2431     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2432   } else {\r
2433     snprintf(buf, MSG_SIZ, _("White"));\r
2434   }\r
2435   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2436   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2437   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2438   str = _("We only care about the height here");\r
2439   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2440   SelectObject(hdc, oldFont);\r
2441   ReleaseDC(hwndMain, hdc);\r
2442 \r
2443   /* Compute where everything goes */\r
2444   if((first.programLogo || second.programLogo) && tinyLayout != 2) {\r
2445         /* [HGM] logo: if either logo is on, reserve space for it */\r
2446         logoHeight =  2*clockSize.cy;\r
2447         leftLogoRect.left   = OUTER_MARGIN;\r
2448         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2449         leftLogoRect.top    = OUTER_MARGIN;\r
2450         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2451 \r
2452         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2453         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2454         rightLogoRect.top    = OUTER_MARGIN;\r
2455         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2456 \r
2457 \r
2458     whiteRect.left = leftLogoRect.right;\r
2459     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2460     whiteRect.top = OUTER_MARGIN;\r
2461     whiteRect.bottom = whiteRect.top + logoHeight;\r
2462 \r
2463     blackRect.right = rightLogoRect.left;\r
2464     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2465     blackRect.top = whiteRect.top;\r
2466     blackRect.bottom = whiteRect.bottom;\r
2467   } else {\r
2468     whiteRect.left = OUTER_MARGIN;\r
2469     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2470     whiteRect.top = OUTER_MARGIN;\r
2471     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2472 \r
2473     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2474     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2475     blackRect.top = whiteRect.top;\r
2476     blackRect.bottom = whiteRect.bottom;\r
2477 \r
2478     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2479   }\r
2480 \r
2481   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2482   if (appData.showButtonBar) {\r
2483     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2484       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2485   } else {\r
2486     messageRect.right = OUTER_MARGIN + boardWidth;\r
2487   }\r
2488   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2489   messageRect.bottom = messageRect.top + messageSize.cy;\r
2490 \r
2491   boardRect.left = OUTER_MARGIN;\r
2492   boardRect.right = boardRect.left + boardWidth;\r
2493   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2494   boardRect.bottom = boardRect.top + boardHeight;\r
2495 \r
2496   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2497   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2498   oldTinyLayout = tinyLayout;\r
2499   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2500   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2501     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2502   winW *= 1 + twoBoards;\r
2503   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2504   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2505   wpMain.height = winH; //       without disturbing window attachments\r
2506   GetWindowRect(hwndMain, &wrect);\r
2507   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2508                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2509 \r
2510   // [HGM] placement: let attached windows follow size change.\r
2511   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2512   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2513   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2514   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2515   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2516 \r
2517   /* compensate if menu bar wrapped */\r
2518   GetClientRect(hwndMain, &crect);\r
2519   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2520   wpMain.height += offby;\r
2521   switch (flags) {\r
2522   case WMSZ_TOPLEFT:\r
2523     SetWindowPos(hwndMain, NULL, \r
2524                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2525                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2526     break;\r
2527 \r
2528   case WMSZ_TOPRIGHT:\r
2529   case WMSZ_TOP:\r
2530     SetWindowPos(hwndMain, NULL, \r
2531                  wrect.left, wrect.bottom - wpMain.height, \r
2532                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2533     break;\r
2534 \r
2535   case WMSZ_BOTTOMLEFT:\r
2536   case WMSZ_LEFT:\r
2537     SetWindowPos(hwndMain, NULL, \r
2538                  wrect.right - wpMain.width, wrect.top, \r
2539                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2540     break;\r
2541 \r
2542   case WMSZ_BOTTOMRIGHT:\r
2543   case WMSZ_BOTTOM:\r
2544   case WMSZ_RIGHT:\r
2545   default:\r
2546     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2547                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2548     break;\r
2549   }\r
2550 \r
2551   hwndPause = NULL;\r
2552   for (i = 0; i < N_BUTTONS; i++) {\r
2553     if (buttonDesc[i].hwnd != NULL) {\r
2554       DestroyWindow(buttonDesc[i].hwnd);\r
2555       buttonDesc[i].hwnd = NULL;\r
2556     }\r
2557     if (appData.showButtonBar) {\r
2558       buttonDesc[i].hwnd =\r
2559         CreateWindow("BUTTON", buttonDesc[i].label,\r
2560                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2561                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2562                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2563                      (HMENU) buttonDesc[i].id,\r
2564                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2565       if (tinyLayout == 2) {\r
2566         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2567                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2568                     MAKELPARAM(FALSE, 0));\r
2569       }\r
2570       if (buttonDesc[i].id == IDM_Pause)\r
2571         hwndPause = buttonDesc[i].hwnd;\r
2572       buttonDesc[i].wndproc = (WNDPROC)\r
2573         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2574     }\r
2575   }\r
2576   if (gridPen != NULL) DeleteObject(gridPen);\r
2577   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2578   if (premovePen != NULL) DeleteObject(premovePen);\r
2579   if (lineGap != 0) {\r
2580     logbrush.lbStyle = BS_SOLID;\r
2581     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2582     gridPen =\r
2583       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2584                    lineGap, &logbrush, 0, NULL);\r
2585     logbrush.lbColor = highlightSquareColor;\r
2586     highlightPen =\r
2587       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2588                    lineGap, &logbrush, 0, NULL);\r
2589 \r
2590     logbrush.lbColor = premoveHighlightColor; \r
2591     premovePen =\r
2592       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2593                    lineGap, &logbrush, 0, NULL);\r
2594 \r
2595     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2596     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2597       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2598       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2599         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2600       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2601         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2602       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2603     }\r
2604     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2605       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2606       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2607         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2608         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2609       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2610         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2611       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2612     }\r
2613   }\r
2614 \r
2615   /* [HGM] Licensing requirement */\r
2616 #ifdef GOTHIC\r
2617   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2618 #endif\r
2619 #ifdef FALCON\r
2620   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2621 #endif\r
2622   GothicPopUp( "", VariantNormal);\r
2623 \r
2624 \r
2625 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2626 \r
2627   /* Load piece bitmaps for this board size */\r
2628   for (i=0; i<=2; i++) {\r
2629     for (piece = WhitePawn;\r
2630          (int) piece < (int) BlackPawn;\r
2631          piece = (ChessSquare) ((int) piece + 1)) {\r
2632       if (pieceBitmap[i][piece] != NULL)\r
2633         DeleteObject(pieceBitmap[i][piece]);\r
2634       pieceBitmap[i][piece] = NULL;\r
2635     }\r
2636   }\r
2637 \r
2638   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2639 \r
2640   // Orthodox Chess pieces\r
2641   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2642   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2643   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2644   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2645   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2646   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2647   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2648   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2649   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2650   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2651   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2652   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2653   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2654   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2655   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2656   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2657     // in Shogi, Hijack the unused Queen for Lance\r
2658     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2659     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2660     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2661   } else {\r
2662     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2663     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2664     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2665   }\r
2666 \r
2667   if(squareSize <= 72 && squareSize >= 33) { \r
2668     /* A & C are available in most sizes now */\r
2669     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2670       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2671       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2672       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2673       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2674       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2675       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2676       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2677       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2678       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2679       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2680       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2681       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2682     } else { // Smirf-like\r
2683       if(gameInfo.variant == VariantSChess) {\r
2684         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2685         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2686         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2687       } else {\r
2688         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2689         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2690         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2691       }\r
2692     }\r
2693     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2694       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2695       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2696       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2697     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2698       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2699       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2700       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2701     } else { // WinBoard standard\r
2702       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2703       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2704       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2705     }\r
2706   }\r
2707 \r
2708 \r
2709   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2710     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2711     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2712     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2713     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2714     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2715     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2716     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2717     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2718     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2719     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2720     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2721     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2722     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2723     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2724     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2725     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2726     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2727     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2728     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2729     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2730     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2731     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2732     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2733     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2734     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2735     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2736     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2737     pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2738     pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2739     pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2740     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2741     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2742     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2743     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2744     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2745     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2746     pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2747     pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2748     pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2749     pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");\r
2750     pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");\r
2751     pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");\r
2752     pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");\r
2753     pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");\r
2754     pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");\r
2755     pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");\r
2756     pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");\r
2757     pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2758     pieceBitmap[0][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "s");\r
2759     pieceBitmap[1][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "o");\r
2760     pieceBitmap[2][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "w");\r
2761     pieceBitmap[0][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "s");\r
2762     pieceBitmap[1][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "o");\r
2763     pieceBitmap[2][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "w");\r
2764     pieceBitmap[0][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "s");\r
2765     pieceBitmap[1][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "o");\r
2766     pieceBitmap[2][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "w");\r
2767 \r
2768     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
2769       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2770       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2771       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2772       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2773       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2774       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2775       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2776       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2777       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2778       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2779       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2780       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2781     } else {\r
2782       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2783       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2784       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2785       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2786       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2787       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2788       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2789       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2790       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2791       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2792       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2793       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2794     }\r
2795 \r
2796   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2797     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2798     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2799     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2800     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2801     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2802     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2803     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2804     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2805     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2806     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2807     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2808     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2809     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2810     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2811   }\r
2812 \r
2813 \r
2814   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2815   /* special Shogi support in this size */\r
2816   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2817       for (piece = WhitePawn;\r
2818            (int) piece < (int) BlackPawn;\r
2819            piece = (ChessSquare) ((int) piece + 1)) {\r
2820         if (pieceBitmap[i][piece] != NULL)\r
2821           DeleteObject(pieceBitmap[i][piece]);\r
2822       }\r
2823     }\r
2824   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2825   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2826   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2827   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2828   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2829   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2830   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2831   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2832   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2833   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2834   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2835   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2836   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2837   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2838   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2839   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2840   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2841   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2842   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2843   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2844   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2845   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2846   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2847   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2848   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2849   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2850   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2851   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2852   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2853   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2854   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2855   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2856   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2857   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2858   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2859   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2860   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2861   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2862   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2863   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2864   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2865   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2866   minorSize = 0;\r
2867   }\r
2868 \r
2869   if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention\r
2870     char buf[MSG_SIZ];\r
2871     if(pieceBitmap[0][i]) continue;\r
2872     snprintf(buf, MSG_SIZ, "piece%d_", i);\r
2873     pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");\r
2874     pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");\r
2875     pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");\r
2876   }\r
2877 }\r
2878 \r
2879 HBITMAP\r
2880 PieceBitmap(ChessSquare p, int kind)\r
2881 {\r
2882   if ((int) p >= (int) BlackPawn)\r
2883     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2884 \r
2885   return pieceBitmap[kind][(int) p];\r
2886 }\r
2887 \r
2888 /***************************************************************/\r
2889 \r
2890 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2891 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2892 /*\r
2893 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2894 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2895 */\r
2896 \r
2897 VOID\r
2898 SquareToPos(int row, int column, int * x, int * y)\r
2899 {\r
2900   if (flipView) {\r
2901     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2902     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2903   } else {\r
2904     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2905     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2906   }\r
2907 }\r
2908 \r
2909 VOID\r
2910 DrawCoordsOnDC(HDC hdc)\r
2911 {\r
2912   static char files[] = "0123456789012345678901221098765432109876543210";\r
2913   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2914   char str[2] = { NULLCHAR, NULLCHAR };\r
2915   int oldMode, oldAlign, x, y, start, i;\r
2916   HFONT oldFont;\r
2917   HBRUSH oldBrush;\r
2918 \r
2919   if (!appData.showCoords)\r
2920     return;\r
2921 \r
2922   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2923 \r
2924   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2925   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2926   oldAlign = GetTextAlign(hdc);\r
2927   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2928 \r
2929   y = boardRect.top + lineGap;\r
2930   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2931 \r
2932   if(border) {\r
2933     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2934     x += border - lineGap - 4; y += squareSize - 6;\r
2935   } else\r
2936   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2937   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2938     str[0] = files[start + i];\r
2939     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2940     y += squareSize + lineGap;\r
2941   }\r
2942 \r
2943   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2944 \r
2945   if(border) {\r
2946     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2947     x += -border + 4; y += border - squareSize + 6;\r
2948   } else\r
2949   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2950   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2951     str[0] = ranks[start + i];\r
2952     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2953     x += squareSize + lineGap;\r
2954   }    \r
2955 \r
2956   SelectObject(hdc, oldBrush);\r
2957   SetBkMode(hdc, oldMode);\r
2958   SetTextAlign(hdc, oldAlign);\r
2959   SelectObject(hdc, oldFont);\r
2960 }\r
2961 \r
2962 VOID\r
2963 DrawGridOnDC(HDC hdc)\r
2964 {\r
2965   HPEN oldPen;\r
2966  \r
2967   if (lineGap != 0) {\r
2968     oldPen = SelectObject(hdc, gridPen);\r
2969     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2970     SelectObject(hdc, oldPen);\r
2971   }\r
2972 }\r
2973 \r
2974 #define HIGHLIGHT_PEN 0\r
2975 #define PREMOVE_PEN   1\r
2976 \r
2977 VOID\r
2978 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2979 {\r
2980   int x1, y1;\r
2981   HPEN oldPen, hPen;\r
2982   if (lineGap == 0) return;\r
2983   if (flipView) {\r
2984     x1 = boardRect.left +\r
2985       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2986     y1 = boardRect.top +\r
2987       lineGap/2 + y * (squareSize + lineGap) + border;\r
2988   } else {\r
2989     x1 = boardRect.left +\r
2990       lineGap/2 + x * (squareSize + lineGap) + border;\r
2991     y1 = boardRect.top +\r
2992       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2993   }\r
2994   hPen = pen ? premovePen : highlightPen;\r
2995   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2996   MoveToEx(hdc, x1, y1, NULL);\r
2997   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2998   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2999   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3000   LineTo(hdc, x1, y1);\r
3001   SelectObject(hdc, oldPen);\r
3002 }\r
3003 \r
3004 VOID\r
3005 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
3006 {\r
3007   int i;\r
3008   for (i=0; i<2; i++) {\r
3009     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
3010       DrawHighlightOnDC(hdc, TRUE,\r
3011                         h->sq[i].x, h->sq[i].y,\r
3012                         pen);\r
3013   }\r
3014 }\r
3015 \r
3016 /* Note: sqcolor is used only in monoMode */\r
3017 /* Note that this code is largely duplicated in woptions.c,\r
3018    function DrawSampleSquare, so that needs to be updated too */\r
3019 VOID\r
3020 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3021 {\r
3022   HBITMAP oldBitmap;\r
3023   HBRUSH oldBrush = NULL;\r
3024   int tmpSize;\r
3025 \r
3026   if (appData.blindfold) return;\r
3027 \r
3028   /* [AS] Use font-based pieces if needed */\r
3029   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
3030     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3031     CreatePiecesFromFont();\r
3032 \r
3033     if( fontBitmapSquareSize == squareSize ) {\r
3034         int index = TranslatePieceToFontPiece(piece);\r
3035 \r
3036         SelectObject( tmphdc, hPieceMask[ index ] );\r
3037 \r
3038       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3039         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
3040       else\r
3041         BitBlt( hdc,\r
3042             x, y,\r
3043             squareSize, squareSize,\r
3044             tmphdc,\r
3045             0, 0,\r
3046             SRCAND );\r
3047 \r
3048         SelectObject( tmphdc, hPieceFace[ index ] );\r
3049 \r
3050       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3051         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
3052       else\r
3053         BitBlt( hdc,\r
3054             x, y,\r
3055             squareSize, squareSize,\r
3056             tmphdc,\r
3057             0, 0,\r
3058             SRCPAINT );\r
3059 \r
3060         return;\r
3061     }\r
3062   }\r
3063 \r
3064   if (appData.monoMode) {\r
3065     SelectObject(tmphdc, PieceBitmap(piece, \r
3066       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3067     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3068            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3069   } else {\r
3070     HBRUSH xBrush = whitePieceBrush;\r
3071     tmpSize = squareSize;\r
3072     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
3073     if(minorSize &&\r
3074         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3075          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3076       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3077       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3078       x += (squareSize - minorSize)>>1;\r
3079       y += squareSize - minorSize - 2;\r
3080       tmpSize = minorSize;\r
3081     }\r
3082 #if WINVER >= 0x0500\r
3083     HBITMAP pbm = PieceBitmap(piece, color ? OUTLINE_PIECE : SOLID_PIECE);\r
3084     BITMAP b;\r
3085     GetObject(pbm, sizeof(BITMAP), &b);\r
3086     if(b.bmBitsPixel == 32) { // for now this is a kludge to indicate bitmaps with alpha channel\r
3087         BLENDFUNCTION bf;\r
3088         bf.BlendOp = AC_SRC_OVER;\r
3089         bf.BlendFlags = 0;\r
3090         bf.SourceConstantAlpha = 0xFF;\r
3091         bf.AlphaFormat = AC_SRC_ALPHA;\r
3092         oldBitmap = SelectObject(tmphdc, pbm);\r
3093         AlphaBlend(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, bf);\r
3094     } else\r
3095 #endif\r
3096     if (color || appData.allWhite ) {\r
3097       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3098       if( color )\r
3099               oldBrush = SelectObject(hdc, xBrush);\r
3100       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3101       if(appData.upsideDown && color==flipView)\r
3102         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3103       else\r
3104         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3105       /* Use black for outline of white pieces */\r
3106       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3107       if(appData.upsideDown && color==flipView)\r
3108         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3109       else\r
3110         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3111     } else if(appData.pieceDirectory[0]) {\r
3112       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3113       oldBrush = SelectObject(hdc, xBrush);\r
3114       if(appData.upsideDown && color==flipView)\r
3115         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3116       else\r
3117         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3118       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3119       if(appData.upsideDown && color==flipView)\r
3120         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3121       else\r
3122         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3123     } else {\r
3124       /* Use square color for details of black pieces */\r
3125       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3126       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3127       if(appData.upsideDown && !flipView)\r
3128         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3129       else\r
3130         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3131     }\r
3132     if(oldBrush) SelectObject(hdc, oldBrush);\r
3133     SelectObject(tmphdc, oldBitmap);\r
3134   }\r
3135 }\r
3136 \r
3137 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3138 int GetBackTextureMode( int algo )\r
3139 {\r
3140     int result = BACK_TEXTURE_MODE_DISABLED;\r
3141 \r
3142     switch( algo ) \r
3143     {\r
3144         case BACK_TEXTURE_MODE_PLAIN:\r
3145             result = 1; /* Always use identity map */\r
3146             break;\r
3147         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3148             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3149             break;\r
3150     }\r
3151 \r
3152     return result;\r
3153 }\r
3154 \r
3155 /* \r
3156     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3157     to handle redraws cleanly (as random numbers would always be different).\r
3158 */\r
3159 VOID RebuildTextureSquareInfo()\r
3160 {\r
3161     BITMAP bi;\r
3162     int lite_w = 0;\r
3163     int lite_h = 0;\r
3164     int dark_w = 0;\r
3165     int dark_h = 0;\r
3166     int row;\r
3167     int col;\r
3168 \r
3169     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3170 \r
3171     if( liteBackTexture != NULL ) {\r
3172         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3173             lite_w = bi.bmWidth;\r
3174             lite_h = bi.bmHeight;\r
3175         }\r
3176     }\r
3177 \r
3178     if( darkBackTexture != NULL ) {\r
3179         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3180             dark_w = bi.bmWidth;\r
3181             dark_h = bi.bmHeight;\r
3182         }\r
3183     }\r
3184 \r
3185     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3186         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3187             if( (col + row) & 1 ) {\r
3188                 /* Lite square */\r
3189                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3190                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3191                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3192                   else\r
3193                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3194                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3195                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3196                   else\r
3197                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3198                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3199                 }\r
3200             }\r
3201             else {\r
3202                 /* Dark square */\r
3203                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3204                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3205                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3206                   else\r
3207                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3208                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3209                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3210                   else\r
3211                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3212                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3213                 }\r
3214             }\r
3215         }\r
3216     }\r
3217 }\r
3218 \r
3219 /* [AS] Arrow highlighting support */\r
3220 \r
3221 static double A_WIDTH = 5; /* Width of arrow body */\r
3222 \r
3223 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3224 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3225 \r
3226 static double Sqr( double x )\r
3227 {\r
3228     return x*x;\r
3229 }\r
3230 \r
3231 static int Round( double x )\r
3232 {\r
3233     return (int) (x + 0.5);\r
3234 }\r
3235 \r
3236 /* Draw an arrow between two points using current settings */\r
3237 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3238 {\r
3239     POINT arrow[7];\r
3240     double dx, dy, j, k, x, y;\r
3241 \r
3242     if( d_x == s_x ) {\r
3243         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3244 \r
3245         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3246         arrow[0].y = s_y;\r
3247 \r
3248         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3249         arrow[1].y = d_y - h;\r
3250 \r
3251         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3252         arrow[2].y = d_y - h;\r
3253 \r
3254         arrow[3].x = d_x;\r
3255         arrow[3].y = d_y;\r
3256 \r
3257         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3258         arrow[5].y = d_y - h;\r
3259 \r
3260         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3261         arrow[4].y = d_y - h;\r
3262 \r
3263         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3264         arrow[6].y = s_y;\r
3265     }\r
3266     else if( d_y == s_y ) {\r
3267         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3268 \r
3269         arrow[0].x = s_x;\r
3270         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3271 \r
3272         arrow[1].x = d_x - w;\r
3273         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3274 \r
3275         arrow[2].x = d_x - w;\r
3276         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3277 \r
3278         arrow[3].x = d_x;\r
3279         arrow[3].y = d_y;\r
3280 \r
3281         arrow[5].x = d_x - w;\r
3282         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3283 \r
3284         arrow[4].x = d_x - w;\r
3285         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3286 \r
3287         arrow[6].x = s_x;\r
3288         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3289     }\r
3290     else {\r
3291         /* [AS] Needed a lot of paper for this! :-) */\r
3292         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3293         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3294   \r
3295         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3296 \r
3297         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3298 \r
3299         x = s_x;\r
3300         y = s_y;\r
3301 \r
3302         arrow[0].x = Round(x - j);\r
3303         arrow[0].y = Round(y + j*dx);\r
3304 \r
3305         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3306         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3307 \r
3308         if( d_x > s_x ) {\r
3309             x = (double) d_x - k;\r
3310             y = (double) d_y - k*dy;\r
3311         }\r
3312         else {\r
3313             x = (double) d_x + k;\r
3314             y = (double) d_y + k*dy;\r
3315         }\r
3316 \r
3317         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3318 \r
3319         arrow[6].x = Round(x - j);\r
3320         arrow[6].y = Round(y + j*dx);\r
3321 \r
3322         arrow[2].x = Round(arrow[6].x + 2*j);\r
3323         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3324 \r
3325         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3326         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3327 \r
3328         arrow[4].x = d_x;\r
3329         arrow[4].y = d_y;\r
3330 \r
3331         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3332         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3333     }\r
3334 \r
3335     Polygon( hdc, arrow, 7 );\r
3336 }\r
3337 \r
3338 /* [AS] Draw an arrow between two squares */\r
3339 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3340 {\r
3341     int s_x, s_y, d_x, d_y;\r
3342     HPEN hpen;\r
3343     HPEN holdpen;\r
3344     HBRUSH hbrush;\r
3345     HBRUSH holdbrush;\r
3346     LOGBRUSH stLB;\r
3347 \r
3348     if( s_col == d_col && s_row == d_row ) {\r
3349         return;\r
3350     }\r
3351 \r
3352     /* Get source and destination points */\r
3353     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3354     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3355 \r
3356     if( d_y > s_y ) {\r
3357         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3358     }\r
3359     else if( d_y < s_y ) {\r
3360         d_y += squareSize / 2 + squareSize / 4;\r
3361     }\r
3362     else {\r
3363         d_y += squareSize / 2;\r
3364     }\r
3365 \r
3366     if( d_x > s_x ) {\r
3367         d_x += squareSize / 2 - squareSize / 4;\r
3368     }\r
3369     else if( d_x < s_x ) {\r
3370         d_x += squareSize / 2 + squareSize / 4;\r
3371     }\r
3372     else {\r
3373         d_x += squareSize / 2;\r
3374     }\r
3375 \r
3376     s_x += squareSize / 2;\r
3377     s_y += squareSize / 2;\r
3378 \r
3379     /* Adjust width */\r
3380     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3381 \r
3382     /* Draw */\r
3383     stLB.lbStyle = BS_SOLID;\r
3384     stLB.lbColor = appData.highlightArrowColor;\r
3385     stLB.lbHatch = 0;\r
3386 \r
3387     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3388     holdpen = SelectObject( hdc, hpen );\r
3389     hbrush = CreateBrushIndirect( &stLB );\r
3390     holdbrush = SelectObject( hdc, hbrush );\r
3391 \r
3392     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3393 \r
3394     SelectObject( hdc, holdpen );\r
3395     SelectObject( hdc, holdbrush );\r
3396     DeleteObject( hpen );\r
3397     DeleteObject( hbrush );\r
3398 }\r
3399 \r
3400 BOOL HasHighlightInfo()\r
3401 {\r
3402     BOOL result = FALSE;\r
3403 \r
3404     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3405         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3406     {\r
3407         result = TRUE;\r
3408     }\r
3409 \r
3410     return result;\r
3411 \r
3412 \r
3413 \r
3414 }\r
3415 \r
3416 BOOL IsDrawArrowEnabled()\r
3417 {\r
3418     BOOL result = FALSE;\r
3419 \r
3420     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3421         result = TRUE;\r
3422     }\r
3423 \r
3424     return result;\r
3425 }\r
3426 \r
3427 VOID DrawArrowHighlight( HDC hdc )\r
3428 {\r
3429     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3430         DrawArrowBetweenSquares( hdc,\r
3431             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3432             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3433     }\r
3434 }\r
3435 \r
3436 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3437 {\r
3438     HRGN result = NULL;\r
3439 \r
3440     if( HasHighlightInfo() ) {\r
3441         int x1, y1, x2, y2;\r
3442         int sx, sy, dx, dy;\r
3443 \r
3444         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3445         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3446 \r
3447         sx = MIN( x1, x2 );\r
3448         sy = MIN( y1, y2 );\r
3449         dx = MAX( x1, x2 ) + squareSize;\r
3450         dy = MAX( y1, y2 ) + squareSize;\r
3451 \r
3452         result = CreateRectRgn( sx, sy, dx, dy );\r
3453     }\r
3454 \r
3455     return result;\r
3456 }\r
3457 \r
3458 /*\r
3459     Warning: this function modifies the behavior of several other functions. \r
3460     \r
3461     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3462     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3463     repaint is scattered all over the place, which is not good for features such as\r
3464     "arrow highlighting" that require a full repaint of the board.\r
3465 \r
3466     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3467     user interaction, when speed is not so important) but especially to avoid errors\r
3468     in the displayed graphics.\r
3469 \r
3470     In such patched places, I always try refer to this function so there is a single\r
3471     place to maintain knowledge.\r
3472     \r
3473     To restore the original behavior, just return FALSE unconditionally.\r
3474 */\r
3475 BOOL IsFullRepaintPreferrable()\r
3476 {\r
3477     BOOL result = FALSE;\r
3478 \r
3479     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3480         /* Arrow may appear on the board */\r
3481         result = TRUE;\r
3482     }\r
3483 \r
3484     return result;\r
3485 }\r
3486 \r
3487 /* \r
3488     This function is called by DrawPosition to know whether a full repaint must\r
3489     be forced or not.\r
3490 \r
3491     Only DrawPosition may directly call this function, which makes use of \r
3492     some state information. Other function should call DrawPosition specifying \r
3493     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3494 */\r
3495 BOOL DrawPositionNeedsFullRepaint()\r
3496 {\r
3497     BOOL result = FALSE;\r
3498 \r
3499     /* \r
3500         Probably a slightly better policy would be to trigger a full repaint\r
3501         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3502         but animation is fast enough that it's difficult to notice.\r
3503     */\r
3504     if( animInfo.piece == EmptySquare ) {\r
3505         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3506             result = TRUE;\r
3507         }\r
3508     }\r
3509 \r
3510     return result;\r
3511 }\r
3512 \r
3513 static HBITMAP borderBitmap;\r
3514 \r
3515 VOID\r
3516 DrawBackgroundOnDC(HDC hdc)\r
3517 {\r
3518   \r
3519   BITMAP bi;\r
3520   HDC tmphdc;\r
3521   HBITMAP hbm;\r
3522   static char oldBorder[MSG_SIZ];\r
3523   int w = 600, h = 600, mode;\r
3524 \r
3525   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3526     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3527     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3528   }\r
3529   if(borderBitmap == NULL) { // loading failed, use white\r
3530     FillRect( hdc, &boardRect, whitePieceBrush );\r
3531     return;\r
3532   }\r
3533   tmphdc = CreateCompatibleDC(hdc);\r
3534   hbm = SelectObject(tmphdc, borderBitmap);\r
3535   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3536             w = bi.bmWidth;\r
3537             h = bi.bmHeight;\r
3538   }\r
3539   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3540   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3541                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3542   SetStretchBltMode(hdc, mode);\r
3543   SelectObject(tmphdc, hbm);\r
3544   DeleteDC(tmphdc);\r
3545 }\r
3546 \r
3547 VOID\r
3548 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3549 {\r
3550   int row, column, x, y, square_color, piece_color;\r
3551   ChessSquare piece;\r
3552   HBRUSH oldBrush;\r
3553   HDC texture_hdc = NULL;\r
3554 \r
3555   /* [AS] Initialize background textures if needed */\r
3556   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3557       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3558       if( backTextureSquareSize != squareSize \r
3559        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3560           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3561           backTextureSquareSize = squareSize;\r
3562           RebuildTextureSquareInfo();\r
3563       }\r
3564 \r
3565       texture_hdc = CreateCompatibleDC( hdc );\r
3566   }\r
3567 \r
3568   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3569     for (column = 0; column < BOARD_WIDTH; column++) {\r
3570   \r
3571       SquareToPos(row, column, &x, &y);\r
3572 \r
3573       piece = board[row][column];\r
3574 \r
3575       square_color = ((column + row) % 2) == 1;\r
3576       if( gameInfo.variant == VariantXiangqi ) {\r
3577           square_color = !InPalace(row, column);\r
3578           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3579           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3580       }\r
3581       piece_color = (int) piece < (int) BlackPawn;\r
3582 \r
3583 \r
3584       /* [HGM] holdings file: light square or black */\r
3585       if(column == BOARD_LEFT-2) {\r
3586             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3587                 square_color = 1;\r
3588             else {\r
3589                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3590                 continue;\r
3591             }\r
3592       } else\r
3593       if(column == BOARD_RGHT + 1 ) {\r
3594             if( row < gameInfo.holdingsSize )\r
3595                 square_color = 1;\r
3596             else {\r
3597                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3598                 continue;\r
3599             }\r
3600       }\r
3601       if(column == BOARD_LEFT-1 ) /* left align */\r
3602             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3603       else if( column == BOARD_RGHT) /* right align */\r
3604             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3605       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3606       else\r
3607       if (appData.monoMode) {\r
3608         if (piece == EmptySquare) {\r
3609           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3610                  square_color ? WHITENESS : BLACKNESS);\r
3611         } else {\r
3612           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3613         }\r
3614       } \r
3615       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3616           /* [AS] Draw the square using a texture bitmap */\r
3617           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3618           int r = row, c = column; // [HGM] do not flip board in flipView\r
3619           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3620 \r
3621           DrawTile( x, y, \r
3622               squareSize, squareSize, \r
3623               hdc, \r
3624               texture_hdc,\r
3625               backTextureSquareInfo[r][c].mode,\r
3626               backTextureSquareInfo[r][c].x,\r
3627               backTextureSquareInfo[r][c].y );\r
3628 \r
3629           SelectObject( texture_hdc, hbm );\r
3630 \r
3631           if (piece != EmptySquare) {\r
3632               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3633           }\r
3634       }\r
3635       else {\r
3636         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3637 \r
3638         oldBrush = SelectObject(hdc, brush );\r
3639         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3640         SelectObject(hdc, oldBrush);\r
3641         if (piece != EmptySquare)\r
3642           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3643       }\r
3644     }\r
3645   }\r
3646 \r
3647   if( texture_hdc != NULL ) {\r
3648     DeleteDC( texture_hdc );\r
3649   }\r
3650 }\r
3651 \r
3652 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3653 void fputDW(FILE *f, int x)\r
3654 {\r
3655         fputc(x     & 255, f);\r
3656         fputc(x>>8  & 255, f);\r
3657         fputc(x>>16 & 255, f);\r
3658         fputc(x>>24 & 255, f);\r
3659 }\r
3660 \r
3661 #define MAX_CLIPS 200   /* more than enough */\r
3662 \r
3663 VOID\r
3664 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3665 {\r
3666 //  HBITMAP bufferBitmap;\r
3667   BITMAP bi;\r
3668 //  RECT Rect;\r
3669   HDC tmphdc;\r
3670   HBITMAP hbm;\r
3671   int w = 100, h = 50;\r
3672 \r
3673   if(logo == NULL) {\r
3674     if(!logoHeight) return;\r
3675     FillRect( hdc, &logoRect, whitePieceBrush );\r
3676   }\r
3677 //  GetClientRect(hwndMain, &Rect);\r
3678 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3679 //                                      Rect.bottom-Rect.top+1);\r
3680   tmphdc = CreateCompatibleDC(hdc);\r
3681   hbm = SelectObject(tmphdc, logo);\r
3682   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3683             w = bi.bmWidth;\r
3684             h = bi.bmHeight;\r
3685   }\r
3686   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3687                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3688   SelectObject(tmphdc, hbm);\r
3689   DeleteDC(tmphdc);\r
3690 }\r
3691 \r
3692 VOID\r
3693 DisplayLogos()\r
3694 {\r
3695   if(logoHeight) {\r
3696         HDC hdc = GetDC(hwndMain);\r
3697         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3698         if(appData.autoLogo) {\r
3699           \r
3700           switch(gameMode) { // pick logos based on game mode\r
3701             case IcsObserving:\r
3702                 whiteLogo = second.programLogo; // ICS logo\r
3703                 blackLogo = second.programLogo;\r
3704             default:\r
3705                 break;\r
3706             case IcsPlayingWhite:\r
3707                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3708                 blackLogo = second.programLogo; // ICS logo\r
3709                 break;\r
3710             case IcsPlayingBlack:\r
3711                 whiteLogo = second.programLogo; // ICS logo\r
3712                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3713                 break;\r
3714             case TwoMachinesPlay:\r
3715                 if(first.twoMachinesColor[0] == 'b') {\r
3716                     whiteLogo = second.programLogo;\r
3717                     blackLogo = first.programLogo;\r
3718                 }\r
3719                 break;\r
3720             case MachinePlaysWhite:\r
3721                 blackLogo = userLogo;\r
3722                 break;\r
3723             case MachinePlaysBlack:\r
3724                 whiteLogo = userLogo;\r
3725                 blackLogo = first.programLogo;\r
3726           }\r
3727         }\r
3728         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3729         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3730         ReleaseDC(hwndMain, hdc);\r
3731   }\r
3732 }\r
3733 \r
3734 void\r
3735 UpdateLogos(int display)\r
3736 { // called after loading new engine(s), in tourney or from menu\r
3737   LoadLogo(&first, 0, FALSE);\r
3738   LoadLogo(&second, 1, appData.icsActive);\r
3739   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3740   if(display) DisplayLogos();\r
3741 }\r
3742 \r
3743 static HDC hdcSeek;\r
3744 \r
3745 // [HGM] seekgraph\r
3746 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3747 {\r
3748     POINT stPt;\r
3749     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3750     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3751     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3752     SelectObject( hdcSeek, hp );\r
3753 }\r
3754 \r
3755 // front-end wrapper for drawing functions to do rectangles\r
3756 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3757 {\r
3758     HPEN hp;\r
3759     RECT rc;\r
3760 \r
3761     if (hdcSeek == NULL) {\r
3762     hdcSeek = GetDC(hwndMain);\r
3763       if (!appData.monoMode) {\r
3764         SelectPalette(hdcSeek, hPal, FALSE);\r
3765         RealizePalette(hdcSeek);\r
3766       }\r
3767     }\r
3768     hp = SelectObject( hdcSeek, gridPen );\r
3769     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3770     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3771     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3772     SelectObject( hdcSeek, hp );\r
3773 }\r
3774 \r
3775 // front-end wrapper for putting text in graph\r
3776 void DrawSeekText(char *buf, int x, int y)\r
3777 {\r
3778         SIZE stSize;\r
3779         SetBkMode( hdcSeek, TRANSPARENT );\r
3780         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3781         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3782 }\r
3783 \r
3784 void DrawSeekDot(int x, int y, int color)\r
3785 {\r
3786         int square = color & 0x80;\r
3787         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3788                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3789         color &= 0x7F;\r
3790         if(square)\r
3791             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3792                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3793         else\r
3794             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3795                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3796             SelectObject(hdcSeek, oldBrush);\r
3797 }\r
3798 \r
3799 void DrawSeekOpen()\r
3800 {\r
3801 }\r
3802 \r
3803 void DrawSeekClose()\r
3804 {\r
3805 }\r
3806 \r
3807 \r
3808 \r
3809 \r
3810 \r
3811 VOID\r
3812 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3813 {\r
3814   static Board lastReq[2], lastDrawn[2];\r
3815   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3816   static int lastDrawnFlipView = 0;\r
3817   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3818   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3819   HDC tmphdc;\r
3820   HDC hdcmem;\r
3821   HBITMAP bufferBitmap;\r
3822   HBITMAP oldBitmap;\r
3823   RECT Rect;\r
3824   HRGN clips[MAX_CLIPS];\r
3825   ChessSquare dragged_piece = EmptySquare;\r
3826   int nr = twoBoards*partnerUp;\r
3827 \r
3828   /* I'm undecided on this - this function figures out whether a full\r
3829    * repaint is necessary on its own, so there's no real reason to have the\r
3830    * caller tell it that.  I think this can safely be set to FALSE - but\r
3831    * if we trust the callers not to request full repaints unnessesarily, then\r
3832    * we could skip some clipping work.  In other words, only request a full\r
3833    * redraw when the majority of pieces have changed positions (ie. flip, \r
3834    * gamestart and similar)  --Hawk\r
3835    */\r
3836   Boolean fullrepaint = repaint;\r
3837 \r
3838   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3839 \r
3840   if( DrawPositionNeedsFullRepaint() ) {\r
3841       fullrepaint = TRUE;\r
3842   }\r
3843 \r
3844   if (board == NULL) {\r
3845     if (!lastReqValid[nr]) {\r
3846       return;\r
3847     }\r
3848     board = lastReq[nr];\r
3849   } else {\r
3850     CopyBoard(lastReq[nr], board);\r
3851     lastReqValid[nr] = 1;\r
3852   }\r
3853 \r
3854   if (doingSizing) {\r
3855     return;\r
3856   }\r
3857 \r
3858   if (IsIconic(hwndMain)) {\r
3859     return;\r
3860   }\r
3861 \r
3862   if (hdc == NULL) {\r
3863     hdc = GetDC(hwndMain);\r
3864     if (!appData.monoMode) {\r
3865       SelectPalette(hdc, hPal, FALSE);\r
3866       RealizePalette(hdc);\r
3867     }\r
3868     releaseDC = TRUE;\r
3869   } else {\r
3870     releaseDC = FALSE;\r
3871   }\r
3872 \r
3873   /* Create some work-DCs */\r
3874   hdcmem = CreateCompatibleDC(hdc);\r
3875   tmphdc = CreateCompatibleDC(hdc);\r
3876 \r
3877   /* If dragging is in progress, we temporarely remove the piece */\r
3878   /* [HGM] or temporarily decrease count if stacked              */\r
3879   /*       !! Moved to before board compare !!                   */\r
3880   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3881     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3882     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3883             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3884         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3885     } else \r
3886     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3887             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3888         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3889     } else \r
3890         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3891   }\r
3892 \r
3893   /* Figure out which squares need updating by comparing the \r
3894    * newest board with the last drawn board and checking if\r
3895    * flipping has changed.\r
3896    */\r
3897   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3898     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3899       for (column = 0; column < BOARD_WIDTH; column++) {\r
3900         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3901           SquareToPos(row, column, &x, &y);\r
3902           clips[num_clips++] =\r
3903             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3904         }\r
3905       }\r
3906     }\r
3907    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3908     for (i=0; i<2; i++) {\r
3909       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3910           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3911         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3912             lastDrawnHighlight.sq[i].y >= 0) {\r
3913           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3914                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3915           clips[num_clips++] =\r
3916             CreateRectRgn(x - lineGap, y - lineGap, \r
3917                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3918         }\r
3919         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3920           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3921           clips[num_clips++] =\r
3922             CreateRectRgn(x - lineGap, y - lineGap, \r
3923                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3924         }\r
3925       }\r
3926     }\r
3927     for (i=0; i<2; i++) {\r
3928       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3929           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3930         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3931             lastDrawnPremove.sq[i].y >= 0) {\r
3932           SquareToPos(lastDrawnPremove.sq[i].y,\r
3933                       lastDrawnPremove.sq[i].x, &x, &y);\r
3934           clips[num_clips++] =\r
3935             CreateRectRgn(x - lineGap, y - lineGap, \r
3936                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3937         }\r
3938         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3939             premoveHighlightInfo.sq[i].y >= 0) {\r
3940           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3941                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3942           clips[num_clips++] =\r
3943             CreateRectRgn(x - lineGap, y - lineGap, \r
3944                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3945         }\r
3946       }\r
3947     }\r
3948    } else { // nr == 1\r
3949         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3950         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3951         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3952         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3953       for (i=0; i<2; i++) {\r
3954         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3955             partnerHighlightInfo.sq[i].y >= 0) {\r
3956           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3957                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3958           clips[num_clips++] =\r
3959             CreateRectRgn(x - lineGap, y - lineGap, \r
3960                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3961         }\r
3962         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3963             oldPartnerHighlight.sq[i].y >= 0) {\r
3964           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3965                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3966           clips[num_clips++] =\r
3967             CreateRectRgn(x - lineGap, y - lineGap, \r
3968                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3969         }\r
3970       }\r
3971    }\r
3972   } else {\r
3973     fullrepaint = TRUE;\r
3974   }\r
3975 \r
3976   /* Create a buffer bitmap - this is the actual bitmap\r
3977    * being written to.  When all the work is done, we can\r
3978    * copy it to the real DC (the screen).  This avoids\r
3979    * the problems with flickering.\r
3980    */\r
3981   GetClientRect(hwndMain, &Rect);\r
3982   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3983                                         Rect.bottom-Rect.top+1);\r
3984   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3985   if (!appData.monoMode) {\r
3986     SelectPalette(hdcmem, hPal, FALSE);\r
3987   }\r
3988 \r
3989   /* Create clips for dragging */\r
3990   if (!fullrepaint) {\r
3991     if (dragInfo.from.x >= 0) {\r
3992       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3993       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3994     }\r
3995     if (dragInfo.start.x >= 0) {\r
3996       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3997       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3998     }\r
3999     if (dragInfo.pos.x >= 0) {\r
4000       x = dragInfo.pos.x - squareSize / 2;\r
4001       y = dragInfo.pos.y - squareSize / 2;\r
4002       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4003     }\r
4004     if (dragInfo.lastpos.x >= 0) {\r
4005       x = dragInfo.lastpos.x - squareSize / 2;\r
4006       y = dragInfo.lastpos.y - squareSize / 2;\r
4007       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4008     }\r
4009   }\r
4010 \r
4011   /* Are we animating a move?  \r
4012    * If so, \r
4013    *   - remove the piece from the board (temporarely)\r
4014    *   - calculate the clipping region\r
4015    */\r
4016   if (!fullrepaint) {\r
4017     if (animInfo.piece != EmptySquare) {\r
4018       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4019       x = boardRect.left + animInfo.lastpos.x;\r
4020       y = boardRect.top + animInfo.lastpos.y;\r
4021       x2 = boardRect.left + animInfo.pos.x;\r
4022       y2 = boardRect.top + animInfo.pos.y;\r
4023       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4024       /* Slight kludge.  The real problem is that after AnimateMove is\r
4025          done, the position on the screen does not match lastDrawn.\r
4026          This currently causes trouble only on e.p. captures in\r
4027          atomic, where the piece moves to an empty square and then\r
4028          explodes.  The old and new positions both had an empty square\r
4029          at the destination, but animation has drawn a piece there and\r
4030          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4031 \r
4032       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4033     }\r
4034   }\r
4035 \r
4036   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4037   if (num_clips == 0)\r
4038     fullrepaint = TRUE;\r
4039 \r
4040   /* Set clipping on the memory DC */\r
4041   if (!fullrepaint) {\r
4042     SelectClipRgn(hdcmem, clips[0]);\r
4043     for (x = 1; x < num_clips; x++) {\r
4044       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4045         abort();  // this should never ever happen!\r
4046     }\r
4047   }\r
4048 \r
4049   /* Do all the drawing to the memory DC */\r
4050   if(explodeInfo.radius) { // [HGM] atomic\r
4051         HBRUSH oldBrush;\r
4052         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4053         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
4054         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4055         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4056         x += squareSize/2;\r
4057         y += squareSize/2;\r
4058         if(!fullrepaint) {\r
4059           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4060           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4061         }\r
4062         DrawGridOnDC(hdcmem);\r
4063         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4064         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4065         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4066         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
4067         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4068         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4069         SelectObject(hdcmem, oldBrush);\r
4070   } else {\r
4071     if(border) DrawBackgroundOnDC(hdcmem);\r
4072     DrawGridOnDC(hdcmem);\r
4073     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
4074         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4075         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4076     } else {\r
4077         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
4078         oldPartnerHighlight = partnerHighlightInfo;\r
4079     }\r
4080     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4081   }\r
4082   if(nr == 0) // [HGM] dual: markers only on left board\r
4083   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4084     for (column = 0; column < BOARD_WIDTH; column++) {\r
4085         if (marker[row][column]) { // marker changes only occur with full repaint!\r
4086             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
4087             SquareToPos(row, column, &x, &y);\r
4088             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
4089                           x + 3*squareSize/4, y + 3*squareSize/4);\r
4090             SelectObject(hdcmem, oldBrush);\r
4091         }\r
4092     }\r
4093   }\r
4094 \r
4095   if( appData.highlightMoveWithArrow ) {\r
4096 \r
4097     DrawArrowHighlight(hdcmem);\r
4098   }\r
4099 \r
4100   DrawCoordsOnDC(hdcmem);\r
4101 \r
4102   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4103                  /* to make sure lastDrawn contains what is actually drawn */\r
4104 \r
4105   /* Put the dragged piece back into place and draw it (out of place!) */\r
4106     if (dragged_piece != EmptySquare) {\r
4107     /* [HGM] or restack */\r
4108     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4109                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4110     else\r
4111     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4112                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4113 \r
4114     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4115     x = dragInfo.pos.x - squareSize / 2;\r
4116     y = dragInfo.pos.y - squareSize / 2;\r
4117     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4118                   ((int) dragInfo.piece < (int) BlackPawn), \r
4119                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4120   }   \r
4121   \r
4122   /* Put the animated piece back into place and draw it */\r
4123   if (animInfo.piece != EmptySquare) {\r
4124     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4125     x = boardRect.left + animInfo.pos.x;\r
4126     y = boardRect.top + animInfo.pos.y;\r
4127     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4128                   ((int) animInfo.piece < (int) BlackPawn),\r
4129                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4130   }\r
4131 \r
4132   /* Release the bufferBitmap by selecting in the old bitmap \r
4133    * and delete the memory DC\r
4134    */\r
4135   SelectObject(hdcmem, oldBitmap);\r
4136   DeleteDC(hdcmem);\r
4137 \r
4138   /* Set clipping on the target DC */\r
4139   if (!fullrepaint) {\r
4140     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4141         RECT rect;\r
4142         GetRgnBox(clips[x], &rect);\r
4143         DeleteObject(clips[x]);\r
4144         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4145                           rect.right + wpMain.width/2, rect.bottom);\r
4146     }\r
4147     SelectClipRgn(hdc, clips[0]);\r
4148     for (x = 1; x < num_clips; x++) {\r
4149       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4150         abort();   // this should never ever happen!\r
4151     } \r
4152   }\r
4153 \r
4154   /* Copy the new bitmap onto the screen in one go.\r
4155    * This way we avoid any flickering\r
4156    */\r
4157   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4158   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4159          boardRect.right - boardRect.left,\r
4160          boardRect.bottom - boardRect.top,\r
4161          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4162   if(saveDiagFlag) { \r
4163     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4164     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4165     HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);\r
4166 \r
4167     bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);\r
4168     obmp = SelectObject(tmp, bufferBitmap);\r
4169     BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,\r
4170            tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);\r
4171     GetObject(bufferBitmap, sizeof(b), &b);\r
4172     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4173         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4174         bih.biWidth = b.bmWidth;\r
4175         bih.biHeight = b.bmHeight;\r
4176         bih.biPlanes = 1;\r
4177         bih.biBitCount = b.bmBitsPixel;\r
4178         bih.biCompression = 0;\r
4179         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4180         bih.biXPelsPerMeter = 0;\r
4181         bih.biYPelsPerMeter = 0;\r
4182         bih.biClrUsed = 0;\r
4183         bih.biClrImportant = 0;\r
4184 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4185 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4186         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4187 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4188 \r
4189         wb = b.bmWidthBytes;\r
4190         // count colors\r
4191         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4192                 int k = ((int*) pData)[i];\r
4193                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4194                 if(j >= 16) break;\r
4195                 color[j] = k;\r
4196                 if(j >= nrColors) nrColors = j+1;\r
4197         }\r
4198         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4199                 INT p = 0;\r
4200                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4201                     for(w=0; w<(wb>>2); w+=2) {\r
4202                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4203                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4204                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4205                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4206                         pData[p++] = m | j<<4;\r
4207                     }\r
4208                     while(p&3) pData[p++] = 0;\r
4209                 }\r
4210                 fac = 3;\r
4211                 wb = ((wb+31)>>5)<<2;\r
4212         }\r
4213         // write BITMAPFILEHEADER\r
4214         fprintf(diagFile, "BM");\r
4215         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4216         fputDW(diagFile, 0);\r
4217         fputDW(diagFile, 0x36 + (fac?64:0));\r
4218         // write BITMAPINFOHEADER\r
4219         fputDW(diagFile, 40);\r
4220         fputDW(diagFile, b.bmWidth);\r
4221         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4222         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4223         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4224         fputDW(diagFile, 0);\r
4225         fputDW(diagFile, 0);\r
4226         fputDW(diagFile, 0);\r
4227         fputDW(diagFile, 0);\r
4228         fputDW(diagFile, 0);\r
4229         fputDW(diagFile, 0);\r
4230         // write color table\r
4231         if(fac)\r
4232         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4233         // write bitmap data\r
4234 \r
4235         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4236                 fputc(pData[i], diagFile);\r
4237         free(pData);\r
4238      }\r
4239      DeleteObject(bufferBitmap); bufferBitmap = src;\r
4240      SelectObject(tmp, obmp);\r
4241      DeleteDC(tmp);\r
4242   }\r
4243 \r
4244   SelectObject(tmphdc, oldBitmap);\r
4245 \r
4246   /* Massive cleanup */\r
4247   for (x = 0; x < num_clips; x++)\r
4248     DeleteObject(clips[x]);\r
4249 \r
4250   DeleteDC(tmphdc);\r
4251   DeleteObject(bufferBitmap);\r
4252 \r
4253   if (releaseDC) \r
4254     ReleaseDC(hwndMain, hdc);\r
4255   \r
4256   if (lastDrawnFlipView != flipView && nr == 0) {\r
4257     if (flipView)\r
4258       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4259     else\r
4260       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4261   }\r
4262 \r
4263 /*  CopyBoard(lastDrawn, board);*/\r
4264   lastDrawnHighlight = highlightInfo;\r
4265   lastDrawnPremove   = premoveHighlightInfo;\r
4266   lastDrawnFlipView = flipView;\r
4267   lastDrawnValid[nr] = 1;\r
4268 }\r
4269 \r
4270 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4271 int\r
4272 SaveDiagram(f)\r
4273      FILE *f;\r
4274 {\r
4275     saveDiagFlag = 1; diagFile = f;\r
4276     HDCDrawPosition(NULL, TRUE, NULL);\r
4277     saveDiagFlag = 0;\r
4278 \r
4279     fclose(f);\r
4280     return TRUE;\r
4281 }\r
4282 \r
4283 \r
4284 /*---------------------------------------------------------------------------*\\r
4285 | CLIENT PAINT PROCEDURE\r
4286 |   This is the main event-handler for the WM_PAINT message.\r
4287 |\r
4288 \*---------------------------------------------------------------------------*/\r
4289 VOID\r
4290 PaintProc(HWND hwnd)\r
4291 {\r
4292   HDC         hdc;\r
4293   PAINTSTRUCT ps;\r
4294   HFONT       oldFont;\r
4295 \r
4296   if((hdc = BeginPaint(hwnd, &ps))) {\r
4297     if (IsIconic(hwnd)) {\r
4298       DrawIcon(hdc, 2, 2, iconCurrent);\r
4299     } else {\r
4300       if (!appData.monoMode) {\r
4301         SelectPalette(hdc, hPal, FALSE);\r
4302         RealizePalette(hdc);\r
4303       }\r
4304       HDCDrawPosition(hdc, 1, NULL);\r
4305       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4306         flipView = !flipView; partnerUp = !partnerUp;\r
4307         HDCDrawPosition(hdc, 1, NULL);\r
4308         flipView = !flipView; partnerUp = !partnerUp;\r
4309       }\r
4310       oldFont =\r
4311         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4312       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4313                  ETO_CLIPPED|ETO_OPAQUE,\r
4314                  &messageRect, messageText, strlen(messageText), NULL);\r
4315       SelectObject(hdc, oldFont);\r
4316       DisplayBothClocks();\r
4317       DisplayLogos();\r
4318     }\r
4319     EndPaint(hwnd,&ps);\r
4320   }\r
4321 \r
4322   return;\r
4323 }\r
4324 \r
4325 \r
4326 /*\r
4327  * If the user selects on a border boundary, return -1; if off the board,\r
4328  *   return -2.  Otherwise map the event coordinate to the square.\r
4329  * The offset boardRect.left or boardRect.top must already have been\r
4330  *   subtracted from x.\r
4331  */\r
4332 int EventToSquare(x, limit)\r
4333      int x, limit;\r
4334 {\r
4335   if (x <= border)\r
4336     return -2;\r
4337   if (x < lineGap + border)\r
4338     return -1;\r
4339   x -= lineGap + border;\r
4340   if ((x % (squareSize + lineGap)) >= squareSize)\r
4341     return -1;\r
4342   x /= (squareSize + lineGap);\r
4343     if (x >= limit)\r
4344     return -2;\r
4345   return x;\r
4346 }\r
4347 \r
4348 typedef struct {\r
4349   char piece;\r
4350   int command;\r
4351   char* name;\r
4352 } DropEnable;\r
4353 \r
4354 DropEnable dropEnables[] = {\r
4355   { 'P', DP_Pawn, N_("Pawn") },\r
4356   { 'N', DP_Knight, N_("Knight") },\r
4357   { 'B', DP_Bishop, N_("Bishop") },\r
4358   { 'R', DP_Rook, N_("Rook") },\r
4359   { 'Q', DP_Queen, N_("Queen") },\r
4360 };\r
4361 \r
4362 VOID\r
4363 SetupDropMenu(HMENU hmenu)\r
4364 {\r
4365   int i, count, enable;\r
4366   char *p;\r
4367   extern char white_holding[], black_holding[];\r
4368   char item[MSG_SIZ];\r
4369 \r
4370   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4371     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4372                dropEnables[i].piece);\r
4373     count = 0;\r
4374     while (p && *p++ == dropEnables[i].piece) count++;\r
4375       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4376     enable = count > 0 || !appData.testLegality\r
4377       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4378                       && !appData.icsActive);\r
4379     ModifyMenu(hmenu, dropEnables[i].command,\r
4380                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4381                dropEnables[i].command, item);\r
4382   }\r
4383 }\r
4384 \r
4385 void DragPieceBegin(int x, int y, Boolean instantly)\r
4386 {\r
4387       dragInfo.lastpos.x = boardRect.left + x;\r
4388       dragInfo.lastpos.y = boardRect.top + y;\r
4389       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4390       dragInfo.from.x = fromX;\r
4391       dragInfo.from.y = fromY;\r
4392       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4393       dragInfo.start = dragInfo.from;\r
4394       SetCapture(hwndMain);\r
4395 }\r
4396 \r
4397 void DragPieceEnd(int x, int y)\r
4398 {\r
4399     ReleaseCapture();\r
4400     dragInfo.start.x = dragInfo.start.y = -1;\r
4401     dragInfo.from = dragInfo.start;\r
4402     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4403 }\r
4404 \r
4405 void ChangeDragPiece(ChessSquare piece)\r
4406 {\r
4407     dragInfo.piece = piece;\r
4408 }\r
4409 \r
4410 /* Event handler for mouse messages */\r
4411 VOID\r
4412 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4413 {\r
4414   int x, y, menuNr;\r
4415   POINT pt;\r
4416   static int recursive = 0;\r
4417   HMENU hmenu;\r
4418   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4419 \r
4420   if (recursive) {\r
4421     if (message == WM_MBUTTONUP) {\r
4422       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4423          to the middle button: we simulate pressing the left button too!\r
4424          */\r
4425       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4426       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4427     }\r
4428     return;\r
4429   }\r
4430   recursive++;\r
4431   \r
4432   pt.x = LOWORD(lParam);\r
4433   pt.y = HIWORD(lParam);\r
4434   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4435   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4436   if (!flipView && y >= 0) {\r
4437     y = BOARD_HEIGHT - 1 - y;\r
4438   }\r
4439   if (flipView && x >= 0) {\r
4440     x = BOARD_WIDTH - 1 - x;\r
4441   }\r
4442 \r
4443   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4444   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4445 \r
4446   switch (message) {\r
4447   case WM_LBUTTONDOWN:\r
4448       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4449         ClockClick(flipClock); break;\r
4450       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4451         ClockClick(!flipClock); break;\r
4452       }\r
4453     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4454       dragInfo.start.x = dragInfo.start.y = -1;\r
4455       dragInfo.from = dragInfo.start;\r
4456     }\r
4457     if(fromX == -1 && frozen) { // not sure where this is for\r
4458                 fromX = fromY = -1; \r
4459       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4460       break;\r
4461     }\r
4462       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4463       DrawPosition(TRUE, NULL);\r
4464     break;\r
4465 \r
4466   case WM_LBUTTONUP:\r
4467       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4468       DrawPosition(TRUE, NULL);\r
4469     break;\r
4470 \r
4471   case WM_MOUSEMOVE:\r
4472     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4473     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4474     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4475     if ((appData.animateDragging || appData.highlightDragging)\r
4476         && (wParam & MK_LBUTTON || dragging == 2)\r
4477         && dragInfo.from.x >= 0) \r
4478     {\r
4479       BOOL full_repaint = FALSE;\r
4480 \r
4481       if (appData.animateDragging) {\r
4482         dragInfo.pos = pt;\r
4483       }\r
4484       if (appData.highlightDragging) {\r
4485         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4486         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4487             full_repaint = TRUE;\r
4488         }\r
4489       }\r
4490       \r
4491       DrawPosition( full_repaint, NULL);\r
4492       \r
4493       dragInfo.lastpos = dragInfo.pos;\r
4494     }\r
4495     break;\r
4496 \r
4497   case WM_MOUSEWHEEL: // [DM]\r
4498     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4499        /* Mouse Wheel is being rolled forward\r
4500         * Play moves forward\r
4501         */\r
4502        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4503                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4504        /* Mouse Wheel is being rolled backward\r
4505         * Play moves backward\r
4506         */\r
4507        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4508                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4509     }\r
4510     break;\r
4511 \r
4512   case WM_MBUTTONUP:\r
4513   case WM_RBUTTONUP:\r
4514     ReleaseCapture();\r
4515     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4516     break;\r
4517  \r
4518   case WM_MBUTTONDOWN:\r
4519   case WM_RBUTTONDOWN:\r
4520     ErrorPopDown();\r
4521     ReleaseCapture();\r
4522     fromX = fromY = -1;\r
4523     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4524     dragInfo.start.x = dragInfo.start.y = -1;\r
4525     dragInfo.from = dragInfo.start;\r
4526     dragInfo.lastpos = dragInfo.pos;\r
4527     if (appData.highlightDragging) {\r
4528       ClearHighlights();\r
4529     }\r
4530     if(y == -2) {\r
4531       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4532       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4533           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4534       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4535           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4536       }\r
4537       break;\r
4538     }\r
4539     DrawPosition(TRUE, NULL);\r
4540 \r
4541     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4542     switch (menuNr) {\r
4543     case 0:\r
4544       if (message == WM_MBUTTONDOWN) {\r
4545         buttonCount = 3;  /* even if system didn't think so */\r
4546         if (wParam & MK_SHIFT) \r
4547           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4548         else\r
4549           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4550       } else { /* message == WM_RBUTTONDOWN */\r
4551         /* Just have one menu, on the right button.  Windows users don't\r
4552            think to try the middle one, and sometimes other software steals\r
4553            it, or it doesn't really exist. */\r
4554         if(gameInfo.variant != VariantShogi)\r
4555             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4556         else\r
4557             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4558       }\r
4559       break;\r
4560     case 2:\r
4561       SetCapture(hwndMain);\r
4562       break;\r
4563     case 1:\r
4564       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4565       SetupDropMenu(hmenu);\r
4566       MenuPopup(hwnd, pt, hmenu, -1);\r
4567     default:\r
4568       break;\r
4569     }\r
4570     break;\r
4571   }\r
4572 \r
4573   recursive--;\r
4574 }\r
4575 \r
4576 /* Preprocess messages for buttons in main window */\r
4577 LRESULT CALLBACK\r
4578 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4579 {\r
4580   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4581   int i, dir;\r
4582 \r
4583   for (i=0; i<N_BUTTONS; i++) {\r
4584     if (buttonDesc[i].id == id) break;\r
4585   }\r
4586   if (i == N_BUTTONS) return 0;\r
4587   switch (message) {\r
4588   case WM_KEYDOWN:\r
4589     switch (wParam) {\r
4590     case VK_LEFT:\r
4591     case VK_RIGHT:\r
4592       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4593       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4594       return TRUE;\r
4595     }\r
4596     break;\r
4597   case WM_CHAR:\r
4598     switch (wParam) {\r
4599     case '\r':\r
4600       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4601       return TRUE;\r
4602     default:\r
4603       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4604         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4605         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4606         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4607         SetFocus(h);\r
4608         SendMessage(h, WM_CHAR, wParam, lParam);\r
4609         return TRUE;\r
4610       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4611         TypeInEvent((char)wParam);\r
4612       }\r
4613       break;\r
4614     }\r
4615     break;\r
4616   }\r
4617   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4618 }\r
4619 \r
4620 static int promoStyle;\r
4621 \r
4622 /* Process messages for Promotion dialog box */\r
4623 LRESULT CALLBACK\r
4624 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4625 {\r
4626 \r
4627   char promoChar;\r
4628 \r
4629   switch (message) {\r
4630 \r
4631   case WM_INITDIALOG: /* message: initialize dialog box */\r
4632     /* Center the dialog over the application window */\r
4633     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4634     Translate(hDlg, DLG_PromotionKing);\r
4635     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4636       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4637        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4638        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4639                SW_SHOW : SW_HIDE);\r
4640     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4641     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4642        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4643          PieceToChar(WhiteAngel) != '~') ||\r
4644         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4645          PieceToChar(BlackAngel) != '~')   ) ?\r
4646                SW_SHOW : SW_HIDE);\r
4647     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4648        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4649          PieceToChar(WhiteMarshall) != '~') ||\r
4650         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4651          PieceToChar(BlackMarshall) != '~')   ) ?\r
4652                SW_SHOW : SW_HIDE);\r
4653     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4654     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4655     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4656     if(promoStyle) {\r
4657         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4658         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4659         SetWindowText(hDlg, "Promote?");\r
4660     }\r
4661     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4662        gameInfo.variant == VariantSuper ?\r
4663                SW_SHOW : SW_HIDE);\r
4664     return TRUE;\r
4665 \r
4666   case WM_COMMAND: /* message: received a command */\r
4667     switch (LOWORD(wParam)) {\r
4668     case IDCANCEL:\r
4669       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4670       ClearHighlights();\r
4671       DrawPosition(FALSE, NULL);\r
4672       return TRUE;\r
4673     case PB_King:\r
4674       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4675       break;\r
4676     case PB_Queen:\r
4677       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4678       break;\r
4679     case PB_Rook:\r
4680       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4681       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4682       break;\r
4683     case PB_Bishop:\r
4684       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4685       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4686       break;\r
4687     case PB_Chancellor:\r
4688       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4689       break;\r
4690     case PB_Archbishop:\r
4691       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4692       break;\r
4693     case PB_Knight:\r
4694       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4695                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4696       break;\r
4697     default:\r
4698       return FALSE;\r
4699     }\r
4700     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4701     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4702     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4703     fromX = fromY = -1;\r
4704     if (!appData.highlightLastMove) {\r
4705       ClearHighlights();\r
4706       DrawPosition(FALSE, NULL);\r
4707     }\r
4708     return TRUE;\r
4709   }\r
4710   return FALSE;\r
4711 }\r
4712 \r
4713 /* Pop up promotion dialog */\r
4714 VOID\r
4715 PromotionPopup(HWND hwnd)\r
4716 {\r
4717   FARPROC lpProc;\r
4718 \r
4719   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4720   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4721     hwnd, (DLGPROC)lpProc);\r
4722   FreeProcInstance(lpProc);\r
4723 }\r
4724 \r
4725 void\r
4726 PromotionPopUp(char choice)\r
4727 {\r
4728   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4729   DrawPosition(TRUE, NULL);\r
4730   PromotionPopup(hwndMain);\r
4731 }\r
4732 \r
4733 VOID\r
4734 LoadGameDialog(HWND hwnd, char* title)\r
4735 {\r
4736   UINT number = 0;\r
4737   FILE *f;\r
4738   char fileTitle[MSG_SIZ];\r
4739   f = OpenFileDialog(hwnd, "rb", "",\r
4740                      appData.oldSaveStyle ? "gam" : "pgn",\r
4741                      GAME_FILT,\r
4742                      title, &number, fileTitle, NULL);\r
4743   if (f != NULL) {\r
4744     cmailMsgLoaded = FALSE;\r
4745     if (number == 0) {\r
4746       int error = GameListBuild(f);\r
4747       if (error) {\r
4748         DisplayError(_("Cannot build game list"), error);\r
4749       } else if (!ListEmpty(&gameList) &&\r
4750                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4751         GameListPopUp(f, fileTitle);\r
4752         return;\r
4753       }\r
4754       GameListDestroy();\r
4755       number = 1;\r
4756     }\r
4757     LoadGame(f, number, fileTitle, FALSE);\r
4758   }\r
4759 }\r
4760 \r
4761 int get_term_width()\r
4762 {\r
4763     HDC hdc;\r
4764     TEXTMETRIC tm;\r
4765     RECT rc;\r
4766     HFONT hfont, hold_font;\r
4767     LOGFONT lf;\r
4768     HWND hText;\r
4769 \r
4770     if (hwndConsole)\r
4771         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4772     else\r
4773         return 79;\r
4774 \r
4775     // get the text metrics\r
4776     hdc = GetDC(hText);\r
4777     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4778     if (consoleCF.dwEffects & CFE_BOLD)\r
4779         lf.lfWeight = FW_BOLD;\r
4780     if (consoleCF.dwEffects & CFE_ITALIC)\r
4781         lf.lfItalic = TRUE;\r
4782     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4783         lf.lfStrikeOut = TRUE;\r
4784     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4785         lf.lfUnderline = TRUE;\r
4786     hfont = CreateFontIndirect(&lf);\r
4787     hold_font = SelectObject(hdc, hfont);\r
4788     GetTextMetrics(hdc, &tm);\r
4789     SelectObject(hdc, hold_font);\r
4790     DeleteObject(hfont);\r
4791     ReleaseDC(hText, hdc);\r
4792 \r
4793     // get the rectangle\r
4794     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4795 \r
4796     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4797 }\r
4798 \r
4799 void UpdateICSWidth(HWND hText)\r
4800 {\r
4801     LONG old_width, new_width;\r
4802 \r
4803     new_width = get_term_width(hText, FALSE);\r
4804     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4805     if (new_width != old_width)\r
4806     {\r
4807         ics_update_width(new_width);\r
4808         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4809     }\r
4810 }\r
4811 \r
4812 VOID\r
4813 ChangedConsoleFont()\r
4814 {\r
4815   CHARFORMAT cfmt;\r
4816   CHARRANGE tmpsel, sel;\r
4817   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4818   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4819   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4820   PARAFORMAT paraf;\r
4821 \r
4822   cfmt.cbSize = sizeof(CHARFORMAT);\r
4823   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4824     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4825                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4826   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4827    * size.  This was undocumented in the version of MSVC++ that I had\r
4828    * when I wrote the code, but is apparently documented now.\r
4829    */\r
4830   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4831   cfmt.bCharSet = f->lf.lfCharSet;\r
4832   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4833   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4834   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4835   /* Why are the following seemingly needed too? */\r
4836   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4837   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4838   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4839   tmpsel.cpMin = 0;\r
4840   tmpsel.cpMax = -1; /*999999?*/\r
4841   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4842   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4843   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4844    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4845    */\r
4846   paraf.cbSize = sizeof(paraf);\r
4847   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4848   paraf.dxStartIndent = 0;\r
4849   paraf.dxOffset = WRAP_INDENT;\r
4850   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4851   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4852   UpdateICSWidth(hText);\r
4853 }\r
4854 \r
4855 /*---------------------------------------------------------------------------*\\r
4856  *\r
4857  * Window Proc for main window\r
4858  *\r
4859 \*---------------------------------------------------------------------------*/\r
4860 \r
4861 /* Process messages for main window, etc. */\r
4862 LRESULT CALLBACK\r
4863 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4864 {\r
4865   FARPROC lpProc;\r
4866   int wmId;\r
4867   char *defName;\r
4868   FILE *f;\r
4869   UINT number;\r
4870   char fileTitle[MSG_SIZ];\r
4871   static SnapData sd;\r
4872   static int peek=0;\r
4873 \r
4874   switch (message) {\r
4875 \r
4876   case WM_PAINT: /* message: repaint portion of window */\r
4877     PaintProc(hwnd);\r
4878     break;\r
4879 \r
4880   case WM_ERASEBKGND:\r
4881     if (IsIconic(hwnd)) {\r
4882       /* Cheat; change the message */\r
4883       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4884     } else {\r
4885       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4886     }\r
4887     break;\r
4888 \r
4889   case WM_LBUTTONDOWN:\r
4890   case WM_MBUTTONDOWN:\r
4891   case WM_RBUTTONDOWN:\r
4892   case WM_LBUTTONUP:\r
4893   case WM_MBUTTONUP:\r
4894   case WM_RBUTTONUP:\r
4895   case WM_MOUSEMOVE:\r
4896   case WM_MOUSEWHEEL:\r
4897     MouseEvent(hwnd, message, wParam, lParam);\r
4898     break;\r
4899 \r
4900   case WM_KEYUP:\r
4901     if((char)wParam == '\b') {\r
4902       ForwardEvent(); peek = 0;\r
4903     }\r
4904 \r
4905     JAWS_KBUP_NAVIGATION\r
4906 \r
4907     break;\r
4908 \r
4909   case WM_KEYDOWN:\r
4910     if((char)wParam == '\b') {\r
4911       if(!peek) BackwardEvent(), peek = 1;\r
4912     }\r
4913 \r
4914     JAWS_KBDOWN_NAVIGATION\r
4915 \r
4916     break;\r
4917 \r
4918   case WM_CHAR:\r
4919     \r
4920     JAWS_ALT_INTERCEPT\r
4921 \r
4922     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4923         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4924         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4925         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4926         SetFocus(h);\r
4927         SendMessage(h, message, wParam, lParam);\r
4928     } else if(lParam != KF_REPEAT) {\r
4929         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4930                 TypeInEvent((char)wParam);\r
4931         } else if((char)wParam == 003) CopyGameToClipboard();\r
4932          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4933     }\r
4934 \r
4935     break;\r
4936 \r
4937   case WM_PALETTECHANGED:\r
4938     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4939       int nnew;\r
4940       HDC hdc = GetDC(hwndMain);\r
4941       SelectPalette(hdc, hPal, TRUE);\r
4942       nnew = RealizePalette(hdc);\r
4943       if (nnew > 0) {\r
4944         paletteChanged = TRUE;\r
4945 \r
4946         InvalidateRect(hwnd, &boardRect, FALSE);\r
4947       }\r
4948       ReleaseDC(hwnd, hdc);\r
4949     }\r
4950     break;\r
4951 \r
4952   case WM_QUERYNEWPALETTE:\r
4953     if (!appData.monoMode /*&& paletteChanged*/) {\r
4954       int nnew;\r
4955       HDC hdc = GetDC(hwndMain);\r
4956       paletteChanged = FALSE;\r
4957       SelectPalette(hdc, hPal, FALSE);\r
4958       nnew = RealizePalette(hdc);\r
4959       if (nnew > 0) {\r
4960         InvalidateRect(hwnd, &boardRect, FALSE);\r
4961       }\r
4962       ReleaseDC(hwnd, hdc);\r
4963       return TRUE;\r
4964     }\r
4965     return FALSE;\r
4966 \r
4967   case WM_COMMAND: /* message: command from application menu */\r
4968     wmId    = LOWORD(wParam);\r
4969 \r
4970     switch (wmId) {\r
4971     case IDM_NewGame:\r
4972       ResetGameEvent();\r
4973       SAY("new game enter a move to play against the computer with white");\r
4974       break;\r
4975 \r
4976     case IDM_NewGameFRC:\r
4977       if( NewGameFRC() == 0 ) {\r
4978         ResetGameEvent();\r
4979       }\r
4980       break;\r
4981 \r
4982     case IDM_NewVariant:\r
4983       NewVariantPopup(hwnd);\r
4984       break;\r
4985 \r
4986     case IDM_LoadGame:\r
4987       LoadGameDialog(hwnd, _("Load Game from File"));\r
4988       break;\r
4989 \r
4990     case IDM_LoadNextGame:\r
4991       ReloadGame(1);\r
4992       break;\r
4993 \r
4994     case IDM_LoadPrevGame:\r
4995       ReloadGame(-1);\r
4996       break;\r
4997 \r
4998     case IDM_ReloadGame:\r
4999       ReloadGame(0);\r
5000       break;\r
5001 \r
5002     case IDM_LoadPosition:\r
5003       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5004         Reset(FALSE, TRUE);\r
5005       }\r
5006       number = 1;\r
5007       f = OpenFileDialog(hwnd, "rb", "",\r
5008                          appData.oldSaveStyle ? "pos" : "fen",\r
5009                          POSITION_FILT,\r
5010                          _("Load Position from File"), &number, fileTitle, NULL);\r
5011       if (f != NULL) {\r
5012         LoadPosition(f, number, fileTitle);\r
5013       }\r
5014       break;\r
5015 \r
5016     case IDM_LoadNextPosition:\r
5017       ReloadPosition(1);\r
5018       break;\r
5019 \r
5020     case IDM_LoadPrevPosition:\r
5021       ReloadPosition(-1);\r
5022       break;\r
5023 \r
5024     case IDM_ReloadPosition:\r
5025       ReloadPosition(0);\r
5026       break;\r
5027 \r
5028     case IDM_SaveGame:\r
5029       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5030       f = OpenFileDialog(hwnd, "a", defName,\r
5031                          appData.oldSaveStyle ? "gam" : "pgn",\r
5032                          GAME_FILT,\r
5033                          _("Save Game to File"), NULL, fileTitle, NULL);\r
5034       if (f != NULL) {\r
5035         SaveGame(f, 0, "");\r
5036       }\r
5037       break;\r
5038 \r
5039     case IDM_SavePosition:\r
5040       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5041       f = OpenFileDialog(hwnd, "a", defName,\r
5042                          appData.oldSaveStyle ? "pos" : "fen",\r
5043                          POSITION_FILT,\r
5044                          _("Save Position to File"), NULL, fileTitle, NULL);\r
5045       if (f != NULL) {\r
5046         SavePosition(f, 0, "");\r
5047       }\r
5048       break;\r
5049 \r
5050     case IDM_SaveDiagram:\r
5051       defName = "diagram";\r
5052       f = OpenFileDialog(hwnd, "wb", defName,\r
5053                          "bmp",\r
5054                          DIAGRAM_FILT,\r
5055                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
5056       if (f != NULL) {\r
5057         SaveDiagram(f);\r
5058       }\r
5059       break;\r
5060 \r
5061     case IDM_SaveSelected:\r
5062       f = OpenFileDialog(hwnd, "a", "",\r
5063                          "pgn",\r
5064                          GAME_FILT,\r
5065                          _("Save Game to File"), NULL, fileTitle, NULL);\r
5066       if (f != NULL) {\r
5067         SaveSelected(f, 0, "");\r
5068       }\r
5069       break;\r
5070 \r
5071     case IDM_CreateBook:\r
5072       CreateBookEvent();\r
5073       break;\r
5074 \r
5075     case IDM_CopyGame:\r
5076       CopyGameToClipboard();\r
5077       break;\r
5078 \r
5079     case IDM_PasteGame:\r
5080       PasteGameFromClipboard();\r
5081       break;\r
5082 \r
5083     case IDM_CopyGameListToClipboard:\r
5084       CopyGameListToClipboard();\r
5085       break;\r
5086 \r
5087     /* [AS] Autodetect FEN or PGN data */\r
5088     case IDM_PasteAny:\r
5089       PasteGameOrFENFromClipboard();\r
5090       break;\r
5091 \r
5092     /* [AS] Move history */\r
5093     case IDM_ShowMoveHistory:\r
5094         if( MoveHistoryIsUp() ) {\r
5095             MoveHistoryPopDown();\r
5096         }\r
5097         else {\r
5098             MoveHistoryPopUp();\r
5099         }\r
5100         break;\r
5101 \r
5102     /* [AS] Eval graph */\r
5103     case IDM_ShowEvalGraph:\r
5104         if( EvalGraphIsUp() ) {\r
5105             EvalGraphPopDown();\r
5106         }\r
5107         else {\r
5108             EvalGraphPopUp();\r
5109             SetFocus(hwndMain);\r
5110         }\r
5111         break;\r
5112 \r
5113     /* [AS] Engine output */\r
5114     case IDM_ShowEngineOutput:\r
5115         if( EngineOutputIsUp() ) {\r
5116             EngineOutputPopDown();\r
5117         }\r
5118         else {\r
5119             EngineOutputPopUp();\r
5120         }\r
5121         break;\r
5122 \r
5123     /* [AS] User adjudication */\r
5124     case IDM_UserAdjudication_White:\r
5125         UserAdjudicationEvent( +1 );\r
5126         break;\r
5127 \r
5128     case IDM_UserAdjudication_Black:\r
5129         UserAdjudicationEvent( -1 );\r
5130         break;\r
5131 \r
5132     case IDM_UserAdjudication_Draw:\r
5133         UserAdjudicationEvent( 0 );\r
5134         break;\r
5135 \r
5136     /* [AS] Game list options dialog */\r
5137     case IDM_GameListOptions:\r
5138       GameListOptions();\r
5139       break;\r
5140 \r
5141     case IDM_NewChat:\r
5142       ChatPopUp(NULL);\r
5143       break;\r
5144 \r
5145     case IDM_CopyPosition:\r
5146       CopyFENToClipboard();\r
5147       break;\r
5148 \r
5149     case IDM_PastePosition:\r
5150       PasteFENFromClipboard();\r
5151       break;\r
5152 \r
5153     case IDM_MailMove:\r
5154       MailMoveEvent();\r
5155       break;\r
5156 \r
5157     case IDM_ReloadCMailMsg:\r
5158       Reset(TRUE, TRUE);\r
5159       ReloadCmailMsgEvent(FALSE);\r
5160       break;\r
5161 \r
5162     case IDM_Minimize:\r
5163       ShowWindow(hwnd, SW_MINIMIZE);\r
5164       break;\r
5165 \r
5166     case IDM_Exit:\r
5167       ExitEvent(0);\r
5168       break;\r
5169 \r
5170     case IDM_MachineWhite:\r
5171       MachineWhiteEvent();\r
5172       /*\r
5173        * refresh the tags dialog only if it's visible\r
5174        */\r
5175       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5176           char *tags;\r
5177           tags = PGNTags(&gameInfo);\r
5178           TagsPopUp(tags, CmailMsg());\r
5179           free(tags);\r
5180       }\r
5181       SAY("computer starts playing white");\r
5182       break;\r
5183 \r
5184     case IDM_MachineBlack:\r
5185       MachineBlackEvent();\r
5186       /*\r
5187        * refresh the tags dialog only if it's visible\r
5188        */\r
5189       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5190           char *tags;\r
5191           tags = PGNTags(&gameInfo);\r
5192           TagsPopUp(tags, CmailMsg());\r
5193           free(tags);\r
5194       }\r
5195       SAY("computer starts playing black");\r
5196       break;\r
5197 \r
5198     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5199       if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
5200       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5201       break;\r
5202 \r
5203     case IDM_TwoMachines:\r
5204       TwoMachinesEvent();\r
5205       /*\r
5206 \r
5207        * refresh the tags dialog only if it's visible\r
5208        */\r
5209       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5210           char *tags;\r
5211           tags = PGNTags(&gameInfo);\r
5212           TagsPopUp(tags, CmailMsg());\r
5213           free(tags);\r
5214       }\r
5215       SAY("computer starts playing both sides");\r
5216       break;\r
5217 \r
5218     case IDM_AnalysisMode:\r
5219       if(AnalyzeModeEvent()) {\r
5220         SAY("analyzing current position");\r
5221       }\r
5222       break;\r
5223 \r
5224     case IDM_AnalyzeFile:\r
5225       AnalyzeFileEvent();\r
5226       break;\r
5227 \r
5228     case IDM_IcsClient:\r
5229       IcsClientEvent();\r
5230       break;\r
5231 \r
5232     case IDM_EditGame:\r
5233     case IDM_EditGame2:\r
5234       EditGameEvent();\r
5235       SAY("edit game");\r
5236       break;\r
5237 \r
5238     case IDM_EditPosition:\r
5239     case IDM_EditPosition2:\r
5240       EditPositionEvent();\r
5241       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5242       break;\r
5243 \r
5244     case IDM_Training:\r
5245       TrainingEvent();\r
5246       break;\r
5247 \r
5248     case IDM_ShowGameList:\r
5249       ShowGameListProc();\r
5250       break;\r
5251 \r
5252     case IDM_EditProgs1:\r
5253       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5254       break;\r
5255 \r
5256     case IDM_LoadProg1:\r
5257      LoadEnginePopUp(hwndMain, 0);\r
5258       break;\r
5259 \r
5260     case IDM_LoadProg2:\r
5261      LoadEnginePopUp(hwndMain, 1);\r
5262       break;\r
5263 \r
5264     case IDM_EditServers:\r
5265       EditTagsPopUp(icsNames, &icsNames);\r
5266       break;\r
5267 \r
5268     case IDM_EditTags:\r
5269     case IDM_Tags:\r
5270       EditTagsProc();\r
5271       break;\r
5272 \r
5273     case IDM_EditBook:\r
5274       EditBookEvent();\r
5275       break;\r
5276 \r
5277     case IDM_EditComment:\r
5278     case IDM_Comment:\r
5279       if (commentUp && editComment) {\r
5280         CommentPopDown();\r
5281       } else {\r
5282         EditCommentEvent();\r
5283       }\r
5284       break;\r
5285 \r
5286     case IDM_Pause:\r
5287       PauseEvent();\r
5288       break;\r
5289 \r
5290     case IDM_Accept:\r
5291       AcceptEvent();\r
5292       break;\r
5293 \r
5294     case IDM_Decline:\r
5295       DeclineEvent();\r
5296       break;\r
5297 \r
5298     case IDM_Rematch:\r
5299 \r
5300       RematchEvent();\r
5301       break;\r
5302 \r
5303     case IDM_CallFlag:\r
5304       CallFlagEvent();\r
5305       break;\r
5306 \r
5307     case IDM_Draw:\r
5308       DrawEvent();\r
5309       break;\r
5310 \r
5311     case IDM_Adjourn:\r
5312       AdjournEvent();\r
5313       break;\r
5314 \r
5315     case IDM_Abort:\r
5316       AbortEvent();\r
5317       break;\r
5318 \r
5319     case IDM_Resign:\r
5320       ResignEvent();\r
5321       break;\r
5322 \r
5323     case IDM_StopObserving:\r
5324       StopObservingEvent();\r
5325       break;\r
5326 \r
5327     case IDM_StopExamining:\r
5328       StopExaminingEvent();\r
5329       break;\r
5330 \r
5331     case IDM_Upload:\r
5332       UploadGameEvent();\r
5333       break;\r
5334 \r
5335     case IDM_TypeInMove:\r
5336       TypeInEvent('\000');\r
5337       break;\r
5338 \r
5339     case IDM_TypeInName:\r
5340       PopUpNameDialog('\000');\r
5341       break;\r
5342 \r
5343     case IDM_Backward:\r
5344       BackwardEvent();\r
5345       SetFocus(hwndMain);\r
5346       break;\r
5347 \r
5348     JAWS_MENU_ITEMS\r
5349 \r
5350     case IDM_Forward:\r
5351       ForwardEvent();\r
5352       SetFocus(hwndMain);\r
5353       break;\r
5354 \r
5355     case IDM_ToStart:\r
5356       ToStartEvent();\r
5357       SetFocus(hwndMain);\r
5358       break;\r
5359 \r
5360     case IDM_ToEnd:\r
5361       ToEndEvent();\r
5362       SetFocus(hwndMain);\r
5363       break;\r
5364 \r
5365     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5366     case OPT_GameListPrev:\r
5367       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5368       break;\r
5369 \r
5370     case IDM_Revert:\r
5371       RevertEvent(FALSE);\r
5372       break;\r
5373 \r
5374     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5375       RevertEvent(TRUE);\r
5376       break;\r
5377 \r
5378     case IDM_TruncateGame:\r
5379       TruncateGameEvent();\r
5380       break;\r
5381 \r
5382     case IDM_MoveNow:\r
5383       MoveNowEvent();\r
5384       break;\r
5385 \r
5386     case IDM_RetractMove:\r
5387       RetractMoveEvent();\r
5388       break;\r
5389 \r
5390     case IDM_FlipView:\r
5391       flipView = !flipView;\r
5392       DrawPosition(FALSE, NULL);\r
5393       break;\r
5394 \r
5395     case IDM_FlipClock:\r
5396       flipClock = !flipClock;\r
5397       DisplayBothClocks();\r
5398       DisplayLogos();\r
5399       break;\r
5400 \r
5401     case IDM_MuteSounds:\r
5402       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5403       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5404                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5405       break;\r
5406 \r
5407     case IDM_GeneralOptions:\r
5408       GeneralOptionsPopup(hwnd);\r
5409       DrawPosition(TRUE, NULL);\r
5410       break;\r
5411 \r
5412     case IDM_BoardOptions:\r
5413       BoardOptionsPopup(hwnd);\r
5414       break;\r
5415 \r
5416     case IDM_ThemeOptions:\r
5417       ThemeOptionsPopup(hwnd);\r
5418       break;\r
5419 \r
5420     case IDM_EnginePlayOptions:\r
5421       EnginePlayOptionsPopup(hwnd);\r
5422       break;\r
5423 \r
5424     case IDM_Engine1Options:\r
5425       EngineOptionsPopup(hwnd, &first);\r
5426       break;\r
5427 \r
5428     case IDM_Engine2Options:\r
5429       savedHwnd = hwnd;\r
5430       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5431       EngineOptionsPopup(hwnd, &second);\r
5432       break;\r
5433 \r
5434     case IDM_OptionsUCI:\r
5435       UciOptionsPopup(hwnd);\r
5436       break;\r
5437 \r
5438     case IDM_Tourney:\r
5439       TourneyPopup(hwnd);\r
5440       break;\r
5441 \r
5442     case IDM_IcsOptions:\r
5443       IcsOptionsPopup(hwnd);\r
5444       break;\r
5445 \r
5446     case IDM_Fonts:\r
5447       FontsOptionsPopup(hwnd);\r
5448       break;\r
5449 \r
5450     case IDM_Sounds:\r
5451       SoundOptionsPopup(hwnd);\r
5452       break;\r
5453 \r
5454     case IDM_CommPort:\r
5455       CommPortOptionsPopup(hwnd);\r
5456       break;\r
5457 \r
5458     case IDM_LoadOptions:\r
5459       LoadOptionsPopup(hwnd);\r
5460       break;\r
5461 \r
5462     case IDM_SaveOptions:\r
5463       SaveOptionsPopup(hwnd);\r
5464       break;\r
5465 \r
5466     case IDM_TimeControl:\r
5467       TimeControlOptionsPopup(hwnd);\r
5468       break;\r
5469 \r
5470     case IDM_SaveSettings:\r
5471       SaveSettings(settingsFileName);\r
5472       break;\r
5473 \r
5474     case IDM_SaveSettingsOnExit:\r
5475       saveSettingsOnExit = !saveSettingsOnExit;\r
5476       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5477                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5478                                          MF_CHECKED : MF_UNCHECKED));\r
5479       break;\r
5480 \r
5481     case IDM_Hint:\r
5482       HintEvent();\r
5483       break;\r
5484 \r
5485     case IDM_Book:\r
5486       BookEvent();\r
5487       break;\r
5488 \r
5489     case IDM_AboutGame:\r
5490       AboutGameEvent();\r
5491       break;\r
5492 \r
5493     case IDM_Debug:\r
5494       appData.debugMode = !appData.debugMode;\r
5495       if (appData.debugMode) {\r
5496         char dir[MSG_SIZ];\r
5497         GetCurrentDirectory(MSG_SIZ, dir);\r
5498         SetCurrentDirectory(installDir);\r
5499         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5500         SetCurrentDirectory(dir);\r
5501         setbuf(debugFP, NULL);\r
5502       } else {\r
5503         fclose(debugFP);\r
5504         debugFP = NULL;\r
5505       }\r
5506       break;\r
5507 \r
5508     case IDM_HELPCONTENTS:\r
5509       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5510           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5511           MessageBox (GetFocus(),\r
5512                     _("Unable to activate help"),\r
5513                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5514       }\r
5515       break;\r
5516 \r
5517     case IDM_HELPSEARCH:\r
5518         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5519             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5520         MessageBox (GetFocus(),\r
5521                     _("Unable to activate help"),\r
5522                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5523       }\r
5524       break;\r
5525 \r
5526     case IDM_HELPHELP:\r
5527       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5528         MessageBox (GetFocus(),\r
5529                     _("Unable to activate help"),\r
5530                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5531       }\r
5532       break;\r
5533 \r
5534     case IDM_ABOUT:\r
5535       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5536       DialogBox(hInst, \r
5537         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5538         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5539       FreeProcInstance(lpProc);\r
5540       break;\r
5541 \r
5542     case IDM_DirectCommand1:\r
5543       AskQuestionEvent(_("Direct Command"),\r
5544                        _("Send to chess program:"), "", "1");\r
5545       break;\r
5546     case IDM_DirectCommand2:\r
5547       AskQuestionEvent(_("Direct Command"),\r
5548                        _("Send to second chess program:"), "", "2");\r
5549       break;\r
5550 \r
5551     case EP_WhitePawn:\r
5552       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5553       fromX = fromY = -1;\r
5554       break;\r
5555 \r
5556     case EP_WhiteKnight:\r
5557       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5558       fromX = fromY = -1;\r
5559       break;\r
5560 \r
5561     case EP_WhiteBishop:\r
5562       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5563       fromX = fromY = -1;\r
5564       break;\r
5565 \r
5566     case EP_WhiteRook:\r
5567       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5568       fromX = fromY = -1;\r
5569       break;\r
5570 \r
5571     case EP_WhiteQueen:\r
5572       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5573       fromX = fromY = -1;\r
5574       break;\r
5575 \r
5576     case EP_WhiteFerz:\r
5577       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5578       fromX = fromY = -1;\r
5579       break;\r
5580 \r
5581     case EP_WhiteWazir:\r
5582       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5583       fromX = fromY = -1;\r
5584       break;\r
5585 \r
5586     case EP_WhiteAlfil:\r
5587       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5588       fromX = fromY = -1;\r
5589       break;\r
5590 \r
5591     case EP_WhiteCannon:\r
5592       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5593       fromX = fromY = -1;\r
5594       break;\r
5595 \r
5596     case EP_WhiteCardinal:\r
5597       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5598       fromX = fromY = -1;\r
5599       break;\r
5600 \r
5601     case EP_WhiteMarshall:\r
5602       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5603       fromX = fromY = -1;\r
5604       break;\r
5605 \r
5606     case EP_WhiteKing:\r
5607       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5608       fromX = fromY = -1;\r
5609       break;\r
5610 \r
5611     case EP_BlackPawn:\r
5612       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5613       fromX = fromY = -1;\r
5614       break;\r
5615 \r
5616     case EP_BlackKnight:\r
5617       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5618       fromX = fromY = -1;\r
5619       break;\r
5620 \r
5621     case EP_BlackBishop:\r
5622       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5623       fromX = fromY = -1;\r
5624       break;\r
5625 \r
5626     case EP_BlackRook:\r
5627       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5628       fromX = fromY = -1;\r
5629       break;\r
5630 \r
5631     case EP_BlackQueen:\r
5632       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5633       fromX = fromY = -1;\r
5634       break;\r
5635 \r
5636     case EP_BlackFerz:\r
5637       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5638       fromX = fromY = -1;\r
5639       break;\r
5640 \r
5641     case EP_BlackWazir:\r
5642       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5643       fromX = fromY = -1;\r
5644       break;\r
5645 \r
5646     case EP_BlackAlfil:\r
5647       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5648       fromX = fromY = -1;\r
5649       break;\r
5650 \r
5651     case EP_BlackCannon:\r
5652       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5653       fromX = fromY = -1;\r
5654       break;\r
5655 \r
5656     case EP_BlackCardinal:\r
5657       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5658       fromX = fromY = -1;\r
5659       break;\r
5660 \r
5661     case EP_BlackMarshall:\r
5662       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5663       fromX = fromY = -1;\r
5664       break;\r
5665 \r
5666     case EP_BlackKing:\r
5667       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5668       fromX = fromY = -1;\r
5669       break;\r
5670 \r
5671     case EP_EmptySquare:\r
5672       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5673       fromX = fromY = -1;\r
5674       break;\r
5675 \r
5676     case EP_ClearBoard:\r
5677       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5678       fromX = fromY = -1;\r
5679       break;\r
5680 \r
5681     case EP_White:\r
5682       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5683       fromX = fromY = -1;\r
5684       break;\r
5685 \r
5686     case EP_Black:\r
5687       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5688       fromX = fromY = -1;\r
5689       break;\r
5690 \r
5691     case EP_Promote:\r
5692       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5693       fromX = fromY = -1;\r
5694       break;\r
5695 \r
5696     case EP_Demote:\r
5697       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5698       fromX = fromY = -1;\r
5699       break;\r
5700 \r
5701     case DP_Pawn:\r
5702       DropMenuEvent(WhitePawn, fromX, fromY);\r
5703       fromX = fromY = -1;\r
5704       break;\r
5705 \r
5706     case DP_Knight:\r
5707       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5708       fromX = fromY = -1;\r
5709       break;\r
5710 \r
5711     case DP_Bishop:\r
5712       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5713       fromX = fromY = -1;\r
5714       break;\r
5715 \r
5716     case DP_Rook:\r
5717       DropMenuEvent(WhiteRook, fromX, fromY);\r
5718       fromX = fromY = -1;\r
5719       break;\r
5720 \r
5721     case DP_Queen:\r
5722       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5723       fromX = fromY = -1;\r
5724       break;\r
5725 \r
5726     case IDM_English:\r
5727       barbaric = 0; appData.language = "";\r
5728       TranslateMenus(0);\r
5729       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5730       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5731       lastChecked = wmId;\r
5732       break;\r
5733 \r
5734     default:\r
5735       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5736           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5737       else\r
5738       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5739           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5740           TranslateMenus(0);\r
5741           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5742           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5743           lastChecked = wmId;\r
5744           break;\r
5745       }\r
5746       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5747     }\r
5748     break;\r
5749 \r
5750   case WM_TIMER:\r
5751     switch (wParam) {\r
5752     case CLOCK_TIMER_ID:\r
5753       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5754       clockTimerEvent = 0;\r
5755       DecrementClocks(); /* call into back end */\r
5756       break;\r
5757     case LOAD_GAME_TIMER_ID:\r
5758       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5759       loadGameTimerEvent = 0;\r
5760       AutoPlayGameLoop(); /* call into back end */\r
5761       break;\r
5762     case ANALYSIS_TIMER_ID:\r
5763       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5764                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5765         AnalysisPeriodicEvent(0);\r
5766       } else {\r
5767         KillTimer(hwnd, analysisTimerEvent);\r
5768         analysisTimerEvent = 0;\r
5769       }\r
5770       break;\r
5771     case DELAYED_TIMER_ID:\r
5772       KillTimer(hwnd, delayedTimerEvent);\r
5773       delayedTimerEvent = 0;\r
5774       delayedTimerCallback();\r
5775       break;\r
5776     }\r
5777     break;\r
5778 \r
5779   case WM_USER_Input:\r
5780     InputEvent(hwnd, message, wParam, lParam);\r
5781     break;\r
5782 \r
5783   /* [AS] Also move "attached" child windows */\r
5784   case WM_WINDOWPOSCHANGING:\r
5785 \r
5786     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5787         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5788 \r
5789         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5790             /* Window is moving */\r
5791             RECT rcMain;\r
5792 \r
5793 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5794             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5795             rcMain.right  = wpMain.x + wpMain.width;\r
5796             rcMain.top    = wpMain.y;\r
5797             rcMain.bottom = wpMain.y + wpMain.height;\r
5798             \r
5799             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5800             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5801             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5802             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5803             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5804             wpMain.x = lpwp->x;\r
5805             wpMain.y = lpwp->y;\r
5806 \r
5807         }\r
5808     }\r
5809     break;\r
5810 \r
5811   /* [AS] Snapping */\r
5812   case WM_ENTERSIZEMOVE:\r
5813     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5814     if (hwnd == hwndMain) {\r
5815       doingSizing = TRUE;\r
5816       lastSizing = 0;\r
5817     }\r
5818     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5819     break;\r
5820 \r
5821   case WM_SIZING:\r
5822     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5823     if (hwnd == hwndMain) {\r
5824       lastSizing = wParam;\r
5825     }\r
5826     break;\r
5827 \r
5828   case WM_MOVING:\r
5829     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5830       return OnMoving( &sd, hwnd, wParam, lParam );\r
5831 \r
5832   case WM_EXITSIZEMOVE:\r
5833     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5834     if (hwnd == hwndMain) {\r
5835       RECT client;\r
5836       doingSizing = FALSE;\r
5837       InvalidateRect(hwnd, &boardRect, FALSE);\r
5838       GetClientRect(hwnd, &client);\r
5839       ResizeBoard(client.right, client.bottom, lastSizing);\r
5840       lastSizing = 0;\r
5841       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5842     }\r
5843     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5844     break;\r
5845 \r
5846   case WM_DESTROY: /* message: window being destroyed */\r
5847     PostQuitMessage(0);\r
5848     break;\r
5849 \r
5850   case WM_CLOSE:\r
5851     if (hwnd == hwndMain) {\r
5852       ExitEvent(0);\r
5853     }\r
5854     break;\r
5855 \r
5856   default:      /* Passes it on if unprocessed */\r
5857     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5858   }\r
5859 \r
5860 \r
5861   return 0;\r
5862 }\r
5863 \r
5864 /*---------------------------------------------------------------------------*\\r
5865  *\r
5866  * Misc utility routines\r
5867  *\r
5868 \*---------------------------------------------------------------------------*/\r
5869 \r
5870 /*\r
5871  * Decent random number generator, at least not as bad as Windows\r
5872  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5873  */\r
5874 unsigned int randstate;\r
5875 \r
5876 int\r
5877 myrandom(void)\r
5878 {\r
5879   randstate = randstate * 1664525 + 1013904223;\r
5880   return (int) randstate & 0x7fffffff;\r
5881 }\r
5882 \r
5883 void\r
5884 mysrandom(unsigned int seed)\r
5885 {\r
5886   randstate = seed;\r
5887 }\r
5888 \r
5889 \r
5890 /* \r
5891  * returns TRUE if user selects a different color, FALSE otherwise \r
5892  */\r
5893 \r
5894 BOOL\r
5895 ChangeColor(HWND hwnd, COLORREF *which)\r
5896 {\r
5897   static BOOL firstTime = TRUE;\r
5898   static DWORD customColors[16];\r
5899   CHOOSECOLOR cc;\r
5900   COLORREF newcolor;\r
5901   int i;\r
5902   ColorClass ccl;\r
5903 \r
5904   if (firstTime) {\r
5905     /* Make initial colors in use available as custom colors */\r
5906     /* Should we put the compiled-in defaults here instead? */\r
5907     i = 0;\r
5908     customColors[i++] = lightSquareColor & 0xffffff;\r
5909     customColors[i++] = darkSquareColor & 0xffffff;\r
5910     customColors[i++] = whitePieceColor & 0xffffff;\r
5911     customColors[i++] = blackPieceColor & 0xffffff;\r
5912     customColors[i++] = highlightSquareColor & 0xffffff;\r
5913     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5914 \r
5915     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5916       customColors[i++] = textAttribs[ccl].color;\r
5917     }\r
5918     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5919     firstTime = FALSE;\r
5920   }\r
5921 \r
5922   cc.lStructSize = sizeof(cc);\r
5923   cc.hwndOwner = hwnd;\r
5924   cc.hInstance = NULL;\r
5925   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5926   cc.lpCustColors = (LPDWORD) customColors;\r
5927   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5928 \r
5929   if (!ChooseColor(&cc)) return FALSE;\r
5930 \r
5931   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5932   if (newcolor == *which) return FALSE;\r
5933   *which = newcolor;\r
5934   return TRUE;\r
5935 \r
5936   /*\r
5937   InitDrawingColors();\r
5938   InvalidateRect(hwnd, &boardRect, FALSE);\r
5939   */\r
5940 }\r
5941 \r
5942 BOOLEAN\r
5943 MyLoadSound(MySound *ms)\r
5944 {\r
5945   BOOL ok = FALSE;\r
5946   struct stat st;\r
5947   FILE *f;\r
5948 \r
5949   if (ms->data && ms->flag) free(ms->data);\r
5950   ms->data = NULL;\r
5951 \r
5952   switch (ms->name[0]) {\r
5953   case NULLCHAR:\r
5954     /* Silence */\r
5955     ok = TRUE;\r
5956     break;\r
5957   case '$':\r
5958     /* System sound from Control Panel.  Don't preload here. */\r
5959     ok = TRUE;\r
5960     break;\r
5961   case '!':\r
5962     if (ms->name[1] == NULLCHAR) {\r
5963       /* "!" alone = silence */\r
5964       ok = TRUE;\r
5965     } else {\r
5966       /* Builtin wave resource.  Error if not found. */\r
5967       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5968       if (h == NULL) break;\r
5969       ms->data = (void *)LoadResource(hInst, h);\r
5970       ms->flag = 0; // not maloced, so cannot be freed!\r
5971       if (h == NULL) break;\r
5972       ok = TRUE;\r
5973     }\r
5974     break;\r
5975   default:\r
5976     /* .wav file.  Error if not found. */\r
5977     f = fopen(ms->name, "rb");\r
5978     if (f == NULL) break;\r
5979     if (fstat(fileno(f), &st) < 0) break;\r
5980     ms->data = malloc(st.st_size);\r
5981     ms->flag = 1;\r
5982     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5983     fclose(f);\r
5984     ok = TRUE;\r
5985     break;\r
5986   }\r
5987   if (!ok) {\r
5988     char buf[MSG_SIZ];\r
5989       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5990     DisplayError(buf, GetLastError());\r
5991   }\r
5992   return ok;\r
5993 }\r
5994 \r
5995 BOOLEAN\r
5996 MyPlaySound(MySound *ms)\r
5997 {\r
5998   BOOLEAN ok = FALSE;\r
5999 \r
6000   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6001   switch (ms->name[0]) {\r
6002   case NULLCHAR:\r
6003         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6004     /* Silence */\r
6005     ok = TRUE;\r
6006     break;\r
6007   case '$':\r
6008     /* System sound from Control Panel (deprecated feature).\r
6009        "$" alone or an unset sound name gets default beep (still in use). */\r
6010     if (ms->name[1]) {\r
6011       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6012     }\r
6013     if (!ok) ok = MessageBeep(MB_OK);\r
6014     break; \r
6015   case '!':\r
6016     /* Builtin wave resource, or "!" alone for silence */\r
6017     if (ms->name[1]) {\r
6018       if (ms->data == NULL) return FALSE;\r
6019       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6020     } else {\r
6021       ok = TRUE;\r
6022     }\r
6023     break;\r
6024   default:\r
6025     /* .wav file.  Error if not found. */\r
6026     if (ms->data == NULL) return FALSE;\r
6027     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6028     break;\r
6029   }\r
6030   /* Don't print an error: this can happen innocently if the sound driver\r
6031      is busy; for instance, if another instance of WinBoard is playing\r
6032      a sound at about the same time. */\r
6033   return ok;\r
6034 }\r
6035 \r
6036 \r
6037 LRESULT CALLBACK\r
6038 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6039 {\r
6040   BOOL ok;\r
6041   OPENFILENAME *ofn;\r
6042   static UINT *number; /* gross that this is static */\r
6043 \r
6044   switch (message) {\r
6045   case WM_INITDIALOG: /* message: initialize dialog box */\r
6046     /* Center the dialog over the application window */\r
6047     ofn = (OPENFILENAME *) lParam;\r
6048     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6049       number = (UINT *) ofn->lCustData;\r
6050       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6051     } else {\r
6052       number = NULL;\r
6053     }\r
6054     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6055     Translate(hDlg, 1536);\r
6056     return FALSE;  /* Allow for further processing */\r
6057 \r
6058   case WM_COMMAND:\r
6059     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6060       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6061     }\r
6062     return FALSE;  /* Allow for further processing */\r
6063   }\r
6064   return FALSE;\r
6065 }\r
6066 \r
6067 UINT APIENTRY\r
6068 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6069 {\r
6070   static UINT *number;\r
6071   OPENFILENAME *ofname;\r
6072   OFNOTIFY *ofnot;\r
6073   switch (uiMsg) {\r
6074   case WM_INITDIALOG:\r
6075     Translate(hdlg, DLG_IndexNumber);\r
6076     ofname = (OPENFILENAME *)lParam;\r
6077     number = (UINT *)(ofname->lCustData);\r
6078     break;\r
6079   case WM_NOTIFY:\r
6080     ofnot = (OFNOTIFY *)lParam;\r
6081     if (ofnot->hdr.code == CDN_FILEOK) {\r
6082       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6083     }\r
6084     break;\r
6085   }\r
6086   return 0;\r
6087 }\r
6088 \r
6089 \r
6090 FILE *\r
6091 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6092                char *nameFilt, char *dlgTitle, UINT *number,\r
6093                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6094 {\r
6095   OPENFILENAME openFileName;\r
6096   char buf1[MSG_SIZ];\r
6097   FILE *f;\r
6098 \r
6099   if (fileName == NULL) fileName = buf1;\r
6100   if (defName == NULL) {\r
6101     safeStrCpy(fileName, "*.", 3 );\r
6102     strcat(fileName, defExt);\r
6103   } else {\r
6104     safeStrCpy(fileName, defName, MSG_SIZ );\r
6105   }\r
6106     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
6107   if (number) *number = 0;\r
6108 \r
6109   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6110   openFileName.hwndOwner         = hwnd;\r
6111   openFileName.hInstance         = (HANDLE) hInst;\r
6112   openFileName.lpstrFilter       = nameFilt;\r
6113   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6114   openFileName.nMaxCustFilter    = 0L;\r
6115   openFileName.nFilterIndex      = 1L;\r
6116   openFileName.lpstrFile         = fileName;\r
6117   openFileName.nMaxFile          = MSG_SIZ;\r
6118   openFileName.lpstrFileTitle    = fileTitle;\r
6119   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6120   openFileName.lpstrInitialDir   = NULL;\r
6121   openFileName.lpstrTitle        = dlgTitle;\r
6122   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6123     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6124     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6125     | (oldDialog ? 0 : OFN_EXPLORER);\r
6126   openFileName.nFileOffset       = 0;\r
6127   openFileName.nFileExtension    = 0;\r
6128   openFileName.lpstrDefExt       = defExt;\r
6129   openFileName.lCustData         = (LONG) number;\r
6130   openFileName.lpfnHook          = oldDialog ?\r
6131     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6132   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6133 \r
6134   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6135                         GetOpenFileName(&openFileName)) {\r
6136     /* open the file */\r
6137     f = fopen(openFileName.lpstrFile, write);\r
6138     if (f == NULL) {\r
6139       MessageBox(hwnd, _("File open failed"), NULL,\r
6140                  MB_OK|MB_ICONEXCLAMATION);\r
6141       return NULL;\r
6142     }\r
6143   } else {\r
6144     int err = CommDlgExtendedError();\r
6145     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6146     return FALSE;\r
6147   }\r
6148   return f;\r
6149 }\r
6150 \r
6151 \r
6152 \r
6153 VOID APIENTRY\r
6154 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6155 {\r
6156   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6157 \r
6158   /*\r
6159    * Get the first pop-up menu in the menu template. This is the\r
6160    * menu that TrackPopupMenu displays.\r
6161    */\r
6162   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6163   TranslateOneMenu(10, hmenuTrackPopup);\r
6164 \r
6165   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6166 \r
6167   /*\r
6168    * TrackPopup uses screen coordinates, so convert the\r
6169    * coordinates of the mouse click to screen coordinates.\r
6170    */\r
6171   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6172 \r
6173   /* Draw and track the floating pop-up menu. */\r
6174   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6175                  pt.x, pt.y, 0, hwnd, NULL);\r
6176 \r
6177   /* Destroy the menu.*/\r
6178   DestroyMenu(hmenu);\r
6179 }\r
6180    \r
6181 typedef struct {\r
6182   HWND hDlg, hText;\r
6183   int sizeX, sizeY, newSizeX, newSizeY;\r
6184   HDWP hdwp;\r
6185 } ResizeEditPlusButtonsClosure;\r
6186 \r
6187 BOOL CALLBACK\r
6188 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6189 {\r
6190   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6191   RECT rect;\r
6192   POINT pt;\r
6193 \r
6194   if (hChild == cl->hText) return TRUE;\r
6195   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6196   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6197   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6198   ScreenToClient(cl->hDlg, &pt);\r
6199   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6200     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6201   return TRUE;\r
6202 }\r
6203 \r
6204 /* Resize a dialog that has a (rich) edit field filling most of\r
6205    the top, with a row of buttons below */\r
6206 VOID\r
6207 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6208 {\r
6209   RECT rectText;\r
6210   int newTextHeight, newTextWidth;\r
6211   ResizeEditPlusButtonsClosure cl;\r
6212   \r
6213   /*if (IsIconic(hDlg)) return;*/\r
6214   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6215   \r
6216   cl.hdwp = BeginDeferWindowPos(8);\r
6217 \r
6218   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6219   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6220   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6221   if (newTextHeight < 0) {\r
6222     newSizeY += -newTextHeight;\r
6223     newTextHeight = 0;\r
6224   }\r
6225   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6226     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6227 \r
6228   cl.hDlg = hDlg;\r
6229   cl.hText = hText;\r
6230   cl.sizeX = sizeX;\r
6231   cl.sizeY = sizeY;\r
6232   cl.newSizeX = newSizeX;\r
6233   cl.newSizeY = newSizeY;\r
6234   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6235 \r
6236   EndDeferWindowPos(cl.hdwp);\r
6237 }\r
6238 \r
6239 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6240 {\r
6241     RECT    rChild, rParent;\r
6242     int     wChild, hChild, wParent, hParent;\r
6243     int     wScreen, hScreen, xNew, yNew;\r
6244     HDC     hdc;\r
6245 \r
6246     /* Get the Height and Width of the child window */\r
6247     GetWindowRect (hwndChild, &rChild);\r
6248     wChild = rChild.right - rChild.left;\r
6249     hChild = rChild.bottom - rChild.top;\r
6250 \r
6251     /* Get the Height and Width of the parent window */\r
6252     GetWindowRect (hwndParent, &rParent);\r
6253     wParent = rParent.right - rParent.left;\r
6254     hParent = rParent.bottom - rParent.top;\r
6255 \r
6256     /* Get the display limits */\r
6257     hdc = GetDC (hwndChild);\r
6258     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6259     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6260     ReleaseDC(hwndChild, hdc);\r
6261 \r
6262     /* Calculate new X position, then adjust for screen */\r
6263     xNew = rParent.left + ((wParent - wChild) /2);\r
6264     if (xNew < 0) {\r
6265         xNew = 0;\r
6266     } else if ((xNew+wChild) > wScreen) {\r
6267         xNew = wScreen - wChild;\r
6268     }\r
6269 \r
6270     /* Calculate new Y position, then adjust for screen */\r
6271     if( mode == 0 ) {\r
6272         yNew = rParent.top  + ((hParent - hChild) /2);\r
6273     }\r
6274     else {\r
6275         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6276     }\r
6277 \r
6278     if (yNew < 0) {\r
6279         yNew = 0;\r
6280     } else if ((yNew+hChild) > hScreen) {\r
6281         yNew = hScreen - hChild;\r
6282     }\r
6283 \r
6284     /* Set it, and return */\r
6285     return SetWindowPos (hwndChild, NULL,\r
6286                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6287 }\r
6288 \r
6289 /* Center one window over another */\r
6290 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6291 {\r
6292     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6293 }\r
6294 \r
6295 /*---------------------------------------------------------------------------*\\r
6296  *\r
6297  * Startup Dialog functions\r
6298  *\r
6299 \*---------------------------------------------------------------------------*/\r
6300 void\r
6301 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6302 {\r
6303   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6304 \r
6305   while (*cd != NULL) {\r
6306     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6307     cd++;\r
6308   }\r
6309 }\r
6310 \r
6311 void\r
6312 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6313 {\r
6314   char buf1[MAX_ARG_LEN];\r
6315   int len;\r
6316 \r
6317   if (str[0] == '@') {\r
6318     FILE* f = fopen(str + 1, "r");\r
6319     if (f == NULL) {\r
6320       DisplayFatalError(str + 1, errno, 2);\r
6321       return;\r
6322     }\r
6323     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6324     fclose(f);\r
6325     buf1[len] = NULLCHAR;\r
6326     str = buf1;\r
6327   }\r
6328 \r
6329 \r
6330 \r
6331   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6332 \r
6333   for (;;) {\r
6334     char buf[MSG_SIZ];\r
6335     char *end = strchr(str, '\n');\r
6336     if (end == NULL) return;\r
6337     memcpy(buf, str, end - str);\r
6338     buf[end - str] = NULLCHAR;\r
6339     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6340     str = end + 1;\r
6341   }\r
6342 }\r
6343 \r
6344 void\r
6345 SetStartupDialogEnables(HWND hDlg)\r
6346 {\r
6347   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6348     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6349     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6350   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6351     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6352   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6353     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6354   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6355     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6356   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6357     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6358     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6359     IsDlgButtonChecked(hDlg, OPT_View));\r
6360 }\r
6361 \r
6362 char *\r
6363 QuoteForFilename(char *filename)\r
6364 {\r
6365   int dquote, space;\r
6366   dquote = strchr(filename, '"') != NULL;\r
6367   space = strchr(filename, ' ') != NULL;\r
6368   if (dquote || space) {\r
6369     if (dquote) {\r
6370       return "'";\r
6371     } else {\r
6372       return "\"";\r
6373     }\r
6374   } else {\r
6375     return "";\r
6376   }\r
6377 }\r
6378 \r
6379 VOID\r
6380 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6381 {\r
6382   char buf[MSG_SIZ];\r
6383   char *q;\r
6384 \r
6385   InitComboStringsFromOption(hwndCombo, nthnames);\r
6386   q = QuoteForFilename(nthcp);\r
6387     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6388   if (*nthdir != NULLCHAR) {\r
6389     q = QuoteForFilename(nthdir);\r
6390       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6391   }\r
6392   if (*nthcp == NULLCHAR) {\r
6393     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6394   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6395     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6396     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6397   }\r
6398 }\r
6399 \r
6400 LRESULT CALLBACK\r
6401 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6402 {\r
6403   char buf[MSG_SIZ];\r
6404   HANDLE hwndCombo;\r
6405   char *p;\r
6406 \r
6407   switch (message) {\r
6408   case WM_INITDIALOG:\r
6409     /* Center the dialog */\r
6410     CenterWindow (hDlg, GetDesktopWindow());\r
6411     Translate(hDlg, DLG_Startup);\r
6412     /* Initialize the dialog items */\r
6413     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6414                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6415                   firstChessProgramNames);\r
6416     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6417                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6418                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6419     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6420     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6421       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6422     if (*appData.icsHelper != NULLCHAR) {\r
6423       char *q = QuoteForFilename(appData.icsHelper);\r
6424       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6425     }\r
6426     if (*appData.icsHost == NULLCHAR) {\r
6427       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6428       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6429     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6430       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6431       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6432     }\r
6433 \r
6434     if (appData.icsActive) {\r
6435       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6436     }\r
6437     else if (appData.noChessProgram) {\r
6438       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6439     }\r
6440     else {\r
6441       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6442     }\r
6443 \r
6444     SetStartupDialogEnables(hDlg);\r
6445     return TRUE;\r
6446 \r
6447   case WM_COMMAND:\r
6448     switch (LOWORD(wParam)) {\r
6449     case IDOK:\r
6450       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6451         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6452         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6453         p = buf;\r
6454         currentEngine[0] = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6455         ParseArgs(StringGet, &p);\r
6456         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6457         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6458         p = buf;\r
6459         currentEngine[1] = strdup(p+5); // [HGM] also remember engine line of 2nd for saving its settings\r
6460         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6461         ParseArgs(StringGet, &p);\r
6462         SwapEngines(singleList); // ... and then make it 'second'\r
6463 \r
6464         appData.noChessProgram = FALSE;\r
6465         appData.icsActive = FALSE;\r
6466       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6467         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6468         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6469         p = buf;\r
6470         ParseArgs(StringGet, &p);\r
6471         if (appData.zippyPlay) {\r
6472           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6473           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6474           p = buf;\r
6475           ParseArgs(StringGet, &p);\r
6476         }\r
6477       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6478         appData.noChessProgram = TRUE;\r
6479         appData.icsActive = FALSE;\r
6480       } else {\r
6481         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6482                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6483         return TRUE;\r
6484       }\r
6485       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6486         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6487         p = buf;\r
6488         ParseArgs(StringGet, &p);\r
6489       }\r
6490       EndDialog(hDlg, TRUE);\r
6491       return TRUE;\r
6492 \r
6493     case IDCANCEL:\r
6494       ExitEvent(0);\r
6495       return TRUE;\r
6496 \r
6497     case IDM_HELPCONTENTS:\r
6498       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6499         MessageBox (GetFocus(),\r
6500                     _("Unable to activate help"),\r
6501                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6502       }\r
6503       break;\r
6504 \r
6505     default:\r
6506       SetStartupDialogEnables(hDlg);\r
6507       break;\r
6508     }\r
6509     break;\r
6510   }\r
6511   return FALSE;\r
6512 }\r
6513 \r
6514 /*---------------------------------------------------------------------------*\\r
6515  *\r
6516  * About box dialog functions\r
6517  *\r
6518 \*---------------------------------------------------------------------------*/\r
6519 \r
6520 /* Process messages for "About" dialog box */\r
6521 LRESULT CALLBACK\r
6522 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6523 {\r
6524   switch (message) {\r
6525   case WM_INITDIALOG: /* message: initialize dialog box */\r
6526     /* Center the dialog over the application window */\r
6527     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6528     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6529     Translate(hDlg, ABOUTBOX);\r
6530     JAWS_COPYRIGHT\r
6531     return (TRUE);\r
6532 \r
6533   case WM_COMMAND: /* message: received a command */\r
6534     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6535         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6536       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6537       return (TRUE);\r
6538     }\r
6539     break;\r
6540   }\r
6541   return (FALSE);\r
6542 }\r
6543 \r
6544 /*---------------------------------------------------------------------------*\\r
6545  *\r
6546  * Comment Dialog functions\r
6547  *\r
6548 \*---------------------------------------------------------------------------*/\r
6549 \r
6550 LRESULT CALLBACK\r
6551 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6552 {\r
6553   static HANDLE hwndText = NULL;\r
6554   int len, newSizeX, newSizeY;\r
6555   static int sizeX, sizeY;\r
6556   char *str;\r
6557   RECT rect;\r
6558   MINMAXINFO *mmi;\r
6559 \r
6560   switch (message) {\r
6561   case WM_INITDIALOG: /* message: initialize dialog box */\r
6562     /* Initialize the dialog items */\r
6563     Translate(hDlg, DLG_EditComment);\r
6564     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6565     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6566     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6567     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6568     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6569     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6570     SetWindowText(hDlg, commentTitle);\r
6571     if (editComment) {\r
6572       SetFocus(hwndText);\r
6573     } else {\r
6574       SetFocus(GetDlgItem(hDlg, IDOK));\r
6575     }\r
6576     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6577                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6578                 MAKELPARAM(FALSE, 0));\r
6579     /* Size and position the dialog */\r
6580     if (!commentDialog) {\r
6581       commentDialog = hDlg;\r
6582       GetClientRect(hDlg, &rect);\r
6583       sizeX = rect.right;\r
6584       sizeY = rect.bottom;\r
6585       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6586           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6587         WINDOWPLACEMENT wp;\r
6588         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6589         wp.length = sizeof(WINDOWPLACEMENT);\r
6590         wp.flags = 0;\r
6591         wp.showCmd = SW_SHOW;\r
6592         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6593         wp.rcNormalPosition.left = wpComment.x;\r
6594         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6595         wp.rcNormalPosition.top = wpComment.y;\r
6596         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6597         SetWindowPlacement(hDlg, &wp);\r
6598 \r
6599         GetClientRect(hDlg, &rect);\r
6600         newSizeX = rect.right;\r
6601         newSizeY = rect.bottom;\r
6602         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6603                               newSizeX, newSizeY);\r
6604         sizeX = newSizeX;\r
6605         sizeY = newSizeY;\r
6606       }\r
6607     }\r
6608     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6609     return FALSE;\r
6610 \r
6611   case WM_COMMAND: /* message: received a command */\r
6612     switch (LOWORD(wParam)) {\r
6613     case IDOK:\r
6614       if (editComment) {\r
6615         char *p, *q;\r
6616         /* Read changed options from the dialog box */\r
6617         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6618         len = GetWindowTextLength(hwndText);\r
6619         str = (char *) malloc(len + 1);\r
6620         GetWindowText(hwndText, str, len + 1);\r
6621         p = q = str;\r
6622         while (*q) {\r
6623           if (*q == '\r')\r
6624             q++;\r
6625           else\r
6626             *p++ = *q++;\r
6627         }\r
6628         *p = NULLCHAR;\r
6629         ReplaceComment(commentIndex, str);\r
6630         free(str);\r
6631       }\r
6632       CommentPopDown();\r
6633       return TRUE;\r
6634 \r
6635     case IDCANCEL:\r
6636     case OPT_CancelComment:\r
6637       CommentPopDown();\r
6638       return TRUE;\r
6639 \r
6640     case OPT_ClearComment:\r
6641       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6642       break;\r
6643 \r
6644     case OPT_EditComment:\r
6645       EditCommentEvent();\r
6646       return TRUE;\r
6647 \r
6648     default:\r
6649       break;\r
6650     }\r
6651     break;\r
6652 \r
6653   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6654         if( wParam == OPT_CommentText ) {\r
6655             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6656 \r
6657             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6658                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6659                 POINTL pt;\r
6660                 LRESULT index;\r
6661 \r
6662                 pt.x = LOWORD( lpMF->lParam );\r
6663                 pt.y = HIWORD( lpMF->lParam );\r
6664 \r
6665                 if(lpMF->msg == WM_CHAR) {\r
6666                         CHARRANGE sel;\r
6667                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6668                         index = sel.cpMin;\r
6669                 } else\r
6670                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6671 \r
6672                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6673                 len = GetWindowTextLength(hwndText);\r
6674                 str = (char *) malloc(len + 1);\r
6675                 GetWindowText(hwndText, str, len + 1);\r
6676                 ReplaceComment(commentIndex, str);\r
6677                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6678                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6679                 free(str);\r
6680 \r
6681                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6682                 lpMF->msg = WM_USER;\r
6683 \r
6684                 return TRUE;\r
6685             }\r
6686         }\r
6687         break;\r
6688 \r
6689   case WM_SIZE:\r
6690     newSizeX = LOWORD(lParam);\r
6691     newSizeY = HIWORD(lParam);\r
6692     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6693     sizeX = newSizeX;\r
6694     sizeY = newSizeY;\r
6695     break;\r
6696 \r
6697   case WM_GETMINMAXINFO:\r
6698     /* Prevent resizing window too small */\r
6699     mmi = (MINMAXINFO *) lParam;\r
6700     mmi->ptMinTrackSize.x = 100;\r
6701     mmi->ptMinTrackSize.y = 100;\r
6702     break;\r
6703   }\r
6704   return FALSE;\r
6705 }\r
6706 \r
6707 VOID\r
6708 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6709 {\r
6710   FARPROC lpProc;\r
6711   char *p, *q;\r
6712 \r
6713   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6714 \r
6715   if (str == NULL) str = "";\r
6716   p = (char *) malloc(2 * strlen(str) + 2);\r
6717   q = p;\r
6718   while (*str) {\r
6719     if (*str == '\n') *q++ = '\r';\r
6720     *q++ = *str++;\r
6721   }\r
6722   *q = NULLCHAR;\r
6723   if (commentText != NULL) free(commentText);\r
6724 \r
6725   commentIndex = index;\r
6726   commentTitle = title;\r
6727   commentText = p;\r
6728   editComment = edit;\r
6729 \r
6730   if (commentDialog) {\r
6731     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6732     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6733   } else {\r
6734     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6735     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6736                  hwndMain, (DLGPROC)lpProc);\r
6737     FreeProcInstance(lpProc);\r
6738   }\r
6739   commentUp = TRUE;\r
6740 }\r
6741 \r
6742 \r
6743 /*---------------------------------------------------------------------------*\\r
6744  *\r
6745  * Type-in move dialog functions\r
6746  * \r
6747 \*---------------------------------------------------------------------------*/\r
6748 \r
6749 LRESULT CALLBACK\r
6750 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6751 {\r
6752   char move[MSG_SIZ];\r
6753   HWND hInput;\r
6754 \r
6755   switch (message) {\r
6756   case WM_INITDIALOG:\r
6757     move[0] = (char) lParam;\r
6758     move[1] = NULLCHAR;\r
6759     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6760     Translate(hDlg, DLG_TypeInMove);\r
6761     hInput = GetDlgItem(hDlg, OPT_Move);\r
6762     SetWindowText(hInput, move);\r
6763     SetFocus(hInput);\r
6764     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6765     return FALSE;\r
6766 \r
6767   case WM_COMMAND:\r
6768     switch (LOWORD(wParam)) {\r
6769     case IDOK:\r
6770 \r
6771       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6772       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6773       TypeInDoneEvent(move);\r
6774       EndDialog(hDlg, TRUE);\r
6775       return TRUE;\r
6776     case IDCANCEL:\r
6777       EndDialog(hDlg, FALSE);\r
6778       return TRUE;\r
6779     default:\r
6780       break;\r
6781     }\r
6782     break;\r
6783   }\r
6784   return FALSE;\r
6785 }\r
6786 \r
6787 VOID\r
6788 PopUpMoveDialog(char firstchar)\r
6789 {\r
6790     FARPROC lpProc;\r
6791 \r
6792       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6793       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6794         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6795       FreeProcInstance(lpProc);\r
6796 }\r
6797 \r
6798 /*---------------------------------------------------------------------------*\\r
6799  *\r
6800  * Type-in name dialog functions\r
6801  * \r
6802 \*---------------------------------------------------------------------------*/\r
6803 \r
6804 LRESULT CALLBACK\r
6805 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6806 {\r
6807   char move[MSG_SIZ];\r
6808   HWND hInput;\r
6809 \r
6810   switch (message) {\r
6811   case WM_INITDIALOG:\r
6812     move[0] = (char) lParam;\r
6813     move[1] = NULLCHAR;\r
6814     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6815     Translate(hDlg, DLG_TypeInName);\r
6816     hInput = GetDlgItem(hDlg, OPT_Name);\r
6817     SetWindowText(hInput, move);\r
6818     SetFocus(hInput);\r
6819     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6820     return FALSE;\r
6821 \r
6822   case WM_COMMAND:\r
6823     switch (LOWORD(wParam)) {\r
6824     case IDOK:\r
6825       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6826       appData.userName = strdup(move);\r
6827       SetUserLogo(); DisplayLogos();\r
6828       SetGameInfo();\r
6829       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6830         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6831         DisplayTitle(move);\r
6832       }\r
6833 \r
6834 \r
6835       EndDialog(hDlg, TRUE);\r
6836       return TRUE;\r
6837     case IDCANCEL:\r
6838       EndDialog(hDlg, FALSE);\r
6839       return TRUE;\r
6840     default:\r
6841       break;\r
6842     }\r
6843     break;\r
6844   }\r
6845   return FALSE;\r
6846 }\r
6847 \r
6848 VOID\r
6849 PopUpNameDialog(char firstchar)\r
6850 {\r
6851     FARPROC lpProc;\r
6852     \r
6853       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6854       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6855         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6856       FreeProcInstance(lpProc);\r
6857 }\r
6858 \r
6859 /*---------------------------------------------------------------------------*\\r
6860  *\r
6861  *  Error dialogs\r
6862  * \r
6863 \*---------------------------------------------------------------------------*/\r
6864 \r
6865 /* Nonmodal error box */\r
6866 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6867                              WPARAM wParam, LPARAM lParam);\r
6868 \r
6869 VOID\r
6870 ErrorPopUp(char *title, char *content)\r
6871 {\r
6872   FARPROC lpProc;\r
6873   char *p, *q;\r
6874   BOOLEAN modal = hwndMain == NULL;\r
6875 \r
6876   p = content;\r
6877   q = errorMessage;\r
6878   while (*p) {\r
6879     if (*p == '\n') {\r
6880       if (modal) {\r
6881         *q++ = ' ';\r
6882         p++;\r
6883       } else {\r
6884         *q++ = '\r';\r
6885         *q++ = *p++;\r
6886       }\r
6887     } else {\r
6888       *q++ = *p++;\r
6889     }\r
6890   }\r
6891   *q = NULLCHAR;\r
6892   strncpy(errorTitle, title, sizeof(errorTitle));\r
6893   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6894   \r
6895   if (modal) {\r
6896     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6897   } else {\r
6898     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6899     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6900                  hwndMain, (DLGPROC)lpProc);\r
6901     FreeProcInstance(lpProc);\r
6902   }\r
6903 }\r
6904 \r
6905 VOID\r
6906 ErrorPopDown()\r
6907 {\r
6908   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6909   if (errorDialog == NULL) return;\r
6910   DestroyWindow(errorDialog);\r
6911   errorDialog = NULL;\r
6912   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6913 }\r
6914 \r
6915 LRESULT CALLBACK\r
6916 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6917 {\r
6918   RECT rChild;\r
6919 \r
6920   switch (message) {\r
6921   case WM_INITDIALOG:\r
6922     GetWindowRect(hDlg, &rChild);\r
6923 \r
6924     /*\r
6925     SetWindowPos(hDlg, NULL, rChild.left,\r
6926       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6927       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6928     */\r
6929 \r
6930     /* \r
6931         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6932         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6933         and it doesn't work when you resize the dialog.\r
6934         For now, just give it a default position.\r
6935     */\r
6936     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6937     Translate(hDlg, DLG_Error);\r
6938 \r
6939     errorDialog = hDlg;\r
6940     SetWindowText(hDlg, errorTitle);\r
6941     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6942     return FALSE;\r
6943 \r
6944   case WM_COMMAND:\r
6945     switch (LOWORD(wParam)) {\r
6946     case IDOK:\r
6947     case IDCANCEL:\r
6948       if (errorDialog == hDlg) errorDialog = NULL;\r
6949       DestroyWindow(hDlg);\r
6950       return TRUE;\r
6951 \r
6952     default:\r
6953       break;\r
6954     }\r
6955     break;\r
6956   }\r
6957   return FALSE;\r
6958 }\r
6959 \r
6960 #ifdef GOTHIC\r
6961 HWND gothicDialog = NULL;\r
6962 \r
6963 LRESULT CALLBACK\r
6964 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6965 {\r
6966   RECT rChild;\r
6967   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6968 \r
6969   switch (message) {\r
6970   case WM_INITDIALOG:\r
6971     GetWindowRect(hDlg, &rChild);\r
6972 \r
6973     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6974                                                              SWP_NOZORDER);\r
6975 \r
6976     /* \r
6977         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6978         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6979         and it doesn't work when you resize the dialog.\r
6980         For now, just give it a default position.\r
6981     */\r
6982     gothicDialog = hDlg;\r
6983     SetWindowText(hDlg, errorTitle);\r
6984     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6985     return FALSE;\r
6986 \r
6987   case WM_COMMAND:\r
6988     switch (LOWORD(wParam)) {\r
6989     case IDOK:\r
6990     case IDCANCEL:\r
6991       if (errorDialog == hDlg) errorDialog = NULL;\r
6992       DestroyWindow(hDlg);\r
6993       return TRUE;\r
6994 \r
6995     default:\r
6996       break;\r
6997     }\r
6998     break;\r
6999   }\r
7000   return FALSE;\r
7001 }\r
7002 \r
7003 VOID\r
7004 GothicPopUp(char *title, VariantClass variant)\r
7005 {\r
7006   FARPROC lpProc;\r
7007   static char *lastTitle;\r
7008 \r
7009   strncpy(errorTitle, title, sizeof(errorTitle));\r
7010   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7011 \r
7012   if(lastTitle != title && gothicDialog != NULL) {\r
7013     DestroyWindow(gothicDialog);\r
7014     gothicDialog = NULL;\r
7015   }\r
7016   if(variant != VariantNormal && gothicDialog == NULL) {\r
7017     title = lastTitle;\r
7018     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7019     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7020                  hwndMain, (DLGPROC)lpProc);\r
7021     FreeProcInstance(lpProc);\r
7022   }\r
7023 }\r
7024 #endif\r
7025 \r
7026 /*---------------------------------------------------------------------------*\\r
7027  *\r
7028  *  Ics Interaction console functions\r
7029  *\r
7030 \*---------------------------------------------------------------------------*/\r
7031 \r
7032 #define HISTORY_SIZE 64\r
7033 static char *history[HISTORY_SIZE];\r
7034 int histIn = 0, histP = 0;\r
7035 \r
7036 \r
7037 VOID\r
7038 SaveInHistory(char *cmd)\r
7039 {\r
7040   if (history[histIn] != NULL) {\r
7041     free(history[histIn]);\r
7042     history[histIn] = NULL;\r
7043   }\r
7044   if (*cmd == NULLCHAR) return;\r
7045   history[histIn] = StrSave(cmd);\r
7046   histIn = (histIn + 1) % HISTORY_SIZE;\r
7047   if (history[histIn] != NULL) {\r
7048     free(history[histIn]);\r
7049 \r
7050     history[histIn] = NULL;\r
7051   }\r
7052   histP = histIn;\r
7053 }\r
7054 \r
7055 char *\r
7056 PrevInHistory(char *cmd)\r
7057 {\r
7058   int newhp;\r
7059   if (histP == histIn) {\r
7060     if (history[histIn] != NULL) free(history[histIn]);\r
7061     history[histIn] = StrSave(cmd);\r
7062   }\r
7063   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7064   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7065   histP = newhp;\r
7066   return history[histP];\r
7067 }\r
7068 \r
7069 char *\r
7070 NextInHistory()\r
7071 {\r
7072   if (histP == histIn) return NULL;\r
7073   histP = (histP + 1) % HISTORY_SIZE;\r
7074   return history[histP];   \r
7075 }\r
7076 \r
7077 HMENU\r
7078 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7079 {\r
7080   HMENU hmenu, h;\r
7081   int i = 0;\r
7082   hmenu = LoadMenu(hInst, "TextMenu");\r
7083   h = GetSubMenu(hmenu, 0);\r
7084   while (e->item) {\r
7085     if (strcmp(e->item, "-") == 0) {\r
7086       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7087     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
7088       int flags = MF_STRING, j = 0;\r
7089       if (e->item[0] == '|') {\r
7090         flags |= MF_MENUBARBREAK;\r
7091         j++;\r
7092       }\r
7093       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
7094       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
7095     }\r
7096     e++;\r
7097     i++;\r
7098   } \r
7099   return hmenu;\r
7100 }\r
7101 \r
7102 WNDPROC consoleTextWindowProc;\r
7103 \r
7104 void\r
7105 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7106 {\r
7107   char buf[MSG_SIZ], name[MSG_SIZ];\r
7108   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7109   CHARRANGE sel;\r
7110 \r
7111   if (!getname) {\r
7112     SetWindowText(hInput, command);\r
7113     if (immediate) {\r
7114       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7115     } else {\r
7116       sel.cpMin = 999999;\r
7117       sel.cpMax = 999999;\r
7118       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7119       SetFocus(hInput);\r
7120     }\r
7121     return;\r
7122   }    \r
7123   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7124   if (sel.cpMin == sel.cpMax) {\r
7125     /* Expand to surrounding word */\r
7126     TEXTRANGE tr;\r
7127     do {\r
7128       tr.chrg.cpMax = sel.cpMin;\r
7129       tr.chrg.cpMin = --sel.cpMin;\r
7130       if (sel.cpMin < 0) break;\r
7131       tr.lpstrText = name;\r
7132       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7133     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7134     sel.cpMin++;\r
7135 \r
7136     do {\r
7137       tr.chrg.cpMin = sel.cpMax;\r
7138       tr.chrg.cpMax = ++sel.cpMax;\r
7139       tr.lpstrText = name;\r
7140       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7141     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7142     sel.cpMax--;\r
7143 \r
7144     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7145       MessageBeep(MB_ICONEXCLAMATION);\r
7146       return;\r
7147     }\r
7148     tr.chrg = sel;\r
7149     tr.lpstrText = name;\r
7150     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7151   } else {\r
7152     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7153       MessageBeep(MB_ICONEXCLAMATION);\r
7154       return;\r
7155     }\r
7156     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7157   }\r
7158   if (immediate) {\r
7159     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7160     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7161     SetWindowText(hInput, buf);\r
7162     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7163   } else {\r
7164     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7165       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7166     SetWindowText(hInput, buf);\r
7167     sel.cpMin = 999999;\r
7168     sel.cpMax = 999999;\r
7169     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7170     SetFocus(hInput);\r
7171   }\r
7172 }\r
7173 \r
7174 LRESULT CALLBACK \r
7175 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7176 {\r
7177   HWND hInput;\r
7178   CHARRANGE sel;\r
7179 \r
7180   switch (message) {\r
7181   case WM_KEYDOWN:\r
7182     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7183     if(wParam=='R') return 0;\r
7184     switch (wParam) {\r
7185     case VK_PRIOR:\r
7186       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7187       return 0;\r
7188     case VK_NEXT:\r
7189       sel.cpMin = 999999;\r
7190       sel.cpMax = 999999;\r
7191       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7192       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7193       return 0;\r
7194     }\r
7195     break;\r
7196   case WM_CHAR:\r
7197    if(wParam != '\022') {\r
7198     if (wParam == '\t') {\r
7199       if (GetKeyState(VK_SHIFT) < 0) {\r
7200         /* shifted */\r
7201         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7202         if (buttonDesc[0].hwnd) {\r
7203           SetFocus(buttonDesc[0].hwnd);\r
7204         } else {\r
7205           SetFocus(hwndMain);\r
7206         }\r
7207       } else {\r
7208         /* unshifted */\r
7209         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7210       }\r
7211     } else {\r
7212       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7213       JAWS_DELETE( SetFocus(hInput); )\r
7214       SendMessage(hInput, message, wParam, lParam);\r
7215     }\r
7216     return 0;\r
7217    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7218    lParam = -1;\r
7219   case WM_RBUTTONDOWN:\r
7220     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7221       /* Move selection here if it was empty */\r
7222       POINT pt;\r
7223       pt.x = LOWORD(lParam);\r
7224       pt.y = HIWORD(lParam);\r
7225       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7226       if (sel.cpMin == sel.cpMax) {\r
7227         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7228         sel.cpMax = sel.cpMin;\r
7229         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7230       }\r
7231       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7232 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7233       POINT pt;\r
7234       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7235       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7236       if (sel.cpMin == sel.cpMax) {\r
7237         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7238         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7239       }\r
7240       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7241         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7242       }\r
7243       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7244       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7245       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7246       MenuPopup(hwnd, pt, hmenu, -1);\r
7247 }\r
7248     }\r
7249     return 0;\r
7250   case WM_RBUTTONUP:\r
7251     if (GetKeyState(VK_SHIFT) & ~1) {\r
7252       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7253         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7254     }\r
7255     return 0;\r
7256   case WM_PASTE:\r
7257     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7258     SetFocus(hInput);\r
7259     return SendMessage(hInput, message, wParam, lParam);\r
7260   case WM_MBUTTONDOWN:\r
7261     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7262   case WM_COMMAND:\r
7263     switch (LOWORD(wParam)) {\r
7264     case IDM_QuickPaste:\r
7265       {\r
7266         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7267         if (sel.cpMin == sel.cpMax) {\r
7268           MessageBeep(MB_ICONEXCLAMATION);\r
7269           return 0;\r
7270         }\r
7271         SendMessage(hwnd, WM_COPY, 0, 0);\r
7272         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7273         SendMessage(hInput, WM_PASTE, 0, 0);\r
7274         SetFocus(hInput);\r
7275         return 0;\r
7276       }\r
7277     case IDM_Cut:\r
7278       SendMessage(hwnd, WM_CUT, 0, 0);\r
7279       return 0;\r
7280     case IDM_Paste:\r
7281       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7282       return 0;\r
7283     case IDM_Copy:\r
7284       SendMessage(hwnd, WM_COPY, 0, 0);\r
7285       return 0;\r
7286     default:\r
7287       {\r
7288         int i = LOWORD(wParam) - IDM_CommandX;\r
7289         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7290             icsTextMenuEntry[i].command != NULL) {\r
7291           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7292                    icsTextMenuEntry[i].getname,\r
7293                    icsTextMenuEntry[i].immediate);\r
7294           return 0;\r
7295         }\r
7296       }\r
7297       break;\r
7298     }\r
7299     break;\r
7300   }\r
7301   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7302 }\r
7303 \r
7304 WNDPROC consoleInputWindowProc;\r
7305 \r
7306 LRESULT CALLBACK\r
7307 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7308 {\r
7309   char buf[MSG_SIZ];\r
7310   char *p;\r
7311   static BOOL sendNextChar = FALSE;\r
7312   static BOOL quoteNextChar = FALSE;\r
7313   InputSource *is = consoleInputSource;\r
7314   CHARFORMAT cf;\r
7315   CHARRANGE sel;\r
7316 \r
7317   switch (message) {\r
7318   case WM_CHAR:\r
7319     if (!appData.localLineEditing || sendNextChar) {\r
7320       is->buf[0] = (CHAR) wParam;\r
7321       is->count = 1;\r
7322       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7323       sendNextChar = FALSE;\r
7324       return 0;\r
7325     }\r
7326     if (quoteNextChar) {\r
7327       buf[0] = (char) wParam;\r
7328       buf[1] = NULLCHAR;\r
7329       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7330       quoteNextChar = FALSE;\r
7331       return 0;\r
7332     }\r
7333     switch (wParam) {\r
7334     case '\r':   /* Enter key */\r
7335       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7336       if (consoleEcho) SaveInHistory(is->buf);\r
7337       is->buf[is->count++] = '\n';\r
7338       is->buf[is->count] = NULLCHAR;\r
7339       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7340       if (consoleEcho) {\r
7341         ConsoleOutput(is->buf, is->count, TRUE);\r
7342       } else if (appData.localLineEditing) {\r
7343         ConsoleOutput("\n", 1, TRUE);\r
7344       }\r
7345       /* fall thru */\r
7346     case '\033': /* Escape key */\r
7347       SetWindowText(hwnd, "");\r
7348       cf.cbSize = sizeof(CHARFORMAT);\r
7349       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7350       if (consoleEcho) {\r
7351         cf.crTextColor = textAttribs[ColorNormal].color;\r
7352       } else {\r
7353         cf.crTextColor = COLOR_ECHOOFF;\r
7354       }\r
7355       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7356       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7357       return 0;\r
7358     case '\t':   /* Tab key */\r
7359       if (GetKeyState(VK_SHIFT) < 0) {\r
7360         /* shifted */\r
7361         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7362       } else {\r
7363         /* unshifted */\r
7364         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7365         if (buttonDesc[0].hwnd) {\r
7366           SetFocus(buttonDesc[0].hwnd);\r
7367         } else {\r
7368           SetFocus(hwndMain);\r
7369         }\r
7370       }\r
7371       return 0;\r
7372     case '\023': /* Ctrl+S */\r
7373       sendNextChar = TRUE;\r
7374       return 0;\r
7375     case '\021': /* Ctrl+Q */\r
7376       quoteNextChar = TRUE;\r
7377       return 0;\r
7378     JAWS_REPLAY\r
7379     default:\r
7380       break;\r
7381     }\r
7382     break;\r
7383   case WM_KEYDOWN:\r
7384     switch (wParam) {\r
7385     case VK_UP:\r
7386       GetWindowText(hwnd, buf, MSG_SIZ);\r
7387       p = PrevInHistory(buf);\r
7388       if (p != NULL) {\r
7389         SetWindowText(hwnd, p);\r
7390         sel.cpMin = 999999;\r
7391         sel.cpMax = 999999;\r
7392         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7393         return 0;\r
7394       }\r
7395       break;\r
7396     case VK_DOWN:\r
7397       p = NextInHistory();\r
7398       if (p != NULL) {\r
7399         SetWindowText(hwnd, p);\r
7400         sel.cpMin = 999999;\r
7401         sel.cpMax = 999999;\r
7402         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7403         return 0;\r
7404       }\r
7405       break;\r
7406     case VK_HOME:\r
7407     case VK_END:\r
7408       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7409       /* fall thru */\r
7410     case VK_PRIOR:\r
7411     case VK_NEXT:\r
7412       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7413       return 0;\r
7414     }\r
7415     break;\r
7416   case WM_MBUTTONDOWN:\r
7417     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7418       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7419     break;\r
7420   case WM_RBUTTONUP:\r
7421     if (GetKeyState(VK_SHIFT) & ~1) {\r
7422       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7423         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7424     } else {\r
7425       POINT pt;\r
7426       HMENU hmenu;\r
7427       hmenu = LoadMenu(hInst, "InputMenu");\r
7428       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7429       if (sel.cpMin == sel.cpMax) {\r
7430         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7431         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7432       }\r
7433       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7434         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7435       }\r
7436       pt.x = LOWORD(lParam);\r
7437       pt.y = HIWORD(lParam);\r
7438       MenuPopup(hwnd, pt, hmenu, -1);\r
7439     }\r
7440     return 0;\r
7441   case WM_COMMAND:\r
7442     switch (LOWORD(wParam)) { \r
7443     case IDM_Undo:\r
7444       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7445       return 0;\r
7446     case IDM_SelectAll:\r
7447       sel.cpMin = 0;\r
7448       sel.cpMax = -1; /*999999?*/\r
7449       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7450       return 0;\r
7451     case IDM_Cut:\r
7452       SendMessage(hwnd, WM_CUT, 0, 0);\r
7453       return 0;\r
7454     case IDM_Paste:\r
7455       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7456       return 0;\r
7457     case IDM_Copy:\r
7458       SendMessage(hwnd, WM_COPY, 0, 0);\r
7459       return 0;\r
7460     }\r
7461     break;\r
7462   }\r
7463   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7464 }\r
7465 \r
7466 #define CO_MAX  100000\r
7467 #define CO_TRIM   1000\r
7468 \r
7469 LRESULT CALLBACK\r
7470 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7471 {\r
7472   static SnapData sd;\r
7473   HWND hText, hInput;\r
7474   RECT rect;\r
7475   static int sizeX, sizeY;\r
7476   int newSizeX, newSizeY;\r
7477   MINMAXINFO *mmi;\r
7478   WORD wMask;\r
7479 \r
7480   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7481   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7482 \r
7483   switch (message) {\r
7484   case WM_NOTIFY:\r
7485     if (((NMHDR*)lParam)->code == EN_LINK)\r
7486     {\r
7487       ENLINK *pLink = (ENLINK*)lParam;\r
7488       if (pLink->msg == WM_LBUTTONUP)\r
7489       {\r
7490         TEXTRANGE tr;\r
7491 \r
7492         tr.chrg = pLink->chrg;\r
7493         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7494         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7495         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7496         free(tr.lpstrText);\r
7497       }\r
7498     }\r
7499     break;\r
7500   case WM_INITDIALOG: /* message: initialize dialog box */\r
7501     hwndConsole = hDlg;\r
7502     SetFocus(hInput);\r
7503     consoleTextWindowProc = (WNDPROC)\r
7504       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7505     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7506     consoleInputWindowProc = (WNDPROC)\r
7507       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7508     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7509     Colorize(ColorNormal, TRUE);\r
7510     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7511     ChangedConsoleFont();\r
7512     GetClientRect(hDlg, &rect);\r
7513     sizeX = rect.right;\r
7514     sizeY = rect.bottom;\r
7515     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7516         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7517       WINDOWPLACEMENT wp;\r
7518       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7519       wp.length = sizeof(WINDOWPLACEMENT);\r
7520       wp.flags = 0;\r
7521       wp.showCmd = SW_SHOW;\r
7522       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7523       wp.rcNormalPosition.left = wpConsole.x;\r
7524       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7525       wp.rcNormalPosition.top = wpConsole.y;\r
7526       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7527       SetWindowPlacement(hDlg, &wp);\r
7528     }\r
7529 \r
7530    // [HGM] Chessknight's change 2004-07-13\r
7531    else { /* Determine Defaults */\r
7532        WINDOWPLACEMENT wp;\r
7533        wpConsole.x = wpMain.width + 1;\r
7534        wpConsole.y = wpMain.y;\r
7535        wpConsole.width = screenWidth -  wpMain.width;\r
7536        wpConsole.height = wpMain.height;\r
7537        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7538        wp.length = sizeof(WINDOWPLACEMENT);\r
7539        wp.flags = 0;\r
7540        wp.showCmd = SW_SHOW;\r
7541        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7542        wp.rcNormalPosition.left = wpConsole.x;\r
7543        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7544        wp.rcNormalPosition.top = wpConsole.y;\r
7545        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7546        SetWindowPlacement(hDlg, &wp);\r
7547     }\r
7548 \r
7549    // Allow hText to highlight URLs and send notifications on them\r
7550    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7551    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7552    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7553    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7554 \r
7555     return FALSE;\r
7556 \r
7557   case WM_SETFOCUS:\r
7558     SetFocus(hInput);\r
7559     return 0;\r
7560 \r
7561   case WM_CLOSE:\r
7562     ExitEvent(0);\r
7563     /* not reached */\r
7564     break;\r
7565 \r
7566   case WM_SIZE:\r
7567     if (IsIconic(hDlg)) break;\r
7568     newSizeX = LOWORD(lParam);\r
7569     newSizeY = HIWORD(lParam);\r
7570     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7571       RECT rectText, rectInput;\r
7572       POINT pt;\r
7573       int newTextHeight, newTextWidth;\r
7574       GetWindowRect(hText, &rectText);\r
7575       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7576       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7577       if (newTextHeight < 0) {\r
7578         newSizeY += -newTextHeight;\r
7579         newTextHeight = 0;\r
7580       }\r
7581       SetWindowPos(hText, NULL, 0, 0,\r
7582         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7583       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7584       pt.x = rectInput.left;\r
7585       pt.y = rectInput.top + newSizeY - sizeY;\r
7586       ScreenToClient(hDlg, &pt);\r
7587       SetWindowPos(hInput, NULL, \r
7588         pt.x, pt.y, /* needs client coords */   \r
7589         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7590         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7591     }\r
7592     sizeX = newSizeX;\r
7593     sizeY = newSizeY;\r
7594     break;\r
7595 \r
7596   case WM_GETMINMAXINFO:\r
7597     /* Prevent resizing window too small */\r
7598     mmi = (MINMAXINFO *) lParam;\r
7599     mmi->ptMinTrackSize.x = 100;\r
7600     mmi->ptMinTrackSize.y = 100;\r
7601     break;\r
7602 \r
7603   /* [AS] Snapping */\r
7604   case WM_ENTERSIZEMOVE:\r
7605     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7606 \r
7607   case WM_SIZING:\r
7608     return OnSizing( &sd, hDlg, wParam, lParam );\r
7609 \r
7610   case WM_MOVING:\r
7611     return OnMoving( &sd, hDlg, wParam, lParam );\r
7612 \r
7613   case WM_EXITSIZEMOVE:\r
7614         UpdateICSWidth(hText);\r
7615     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7616   }\r
7617 \r
7618   return DefWindowProc(hDlg, message, wParam, lParam);\r
7619 }\r
7620 \r
7621 \r
7622 VOID\r
7623 ConsoleCreate()\r
7624 {\r
7625   HWND hCons;\r
7626   if (hwndConsole) return;\r
7627   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7628   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7629 }\r
7630 \r
7631 \r
7632 VOID\r
7633 ConsoleOutput(char* data, int length, int forceVisible)\r
7634 {\r
7635   HWND hText;\r
7636   int trim, exlen;\r
7637   char *p, *q;\r
7638   char buf[CO_MAX+1];\r
7639   POINT pEnd;\r
7640   RECT rect;\r
7641   static int delayLF = 0;\r
7642   CHARRANGE savesel, sel;\r
7643 \r
7644   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7645   p = data;\r
7646   q = buf;\r
7647   if (delayLF) {\r
7648     *q++ = '\r';\r
7649     *q++ = '\n';\r
7650     delayLF = 0;\r
7651   }\r
7652   while (length--) {\r
7653     if (*p == '\n') {\r
7654       if (*++p) {\r
7655         *q++ = '\r';\r
7656         *q++ = '\n';\r
7657       } else {\r
7658         delayLF = 1;\r
7659       }\r
7660     } else if (*p == '\007') {\r
7661        MyPlaySound(&sounds[(int)SoundBell]);\r
7662        p++;\r
7663     } else {\r
7664       *q++ = *p++;\r
7665     }\r
7666   }\r
7667   *q = NULLCHAR;\r
7668   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7669   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7670   /* Save current selection */\r
7671   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7672   exlen = GetWindowTextLength(hText);\r
7673   /* Find out whether current end of text is visible */\r
7674   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7675   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7676   /* Trim existing text if it's too long */\r
7677   if (exlen + (q - buf) > CO_MAX) {\r
7678     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7679     sel.cpMin = 0;\r
7680     sel.cpMax = trim;\r
7681     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7682     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7683     exlen -= trim;\r
7684     savesel.cpMin -= trim;\r
7685     savesel.cpMax -= trim;\r
7686     if (exlen < 0) exlen = 0;\r
7687     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7688     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7689   }\r
7690   /* Append the new text */\r
7691   sel.cpMin = exlen;\r
7692   sel.cpMax = exlen;\r
7693   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7694   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7695   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7696   if (forceVisible || exlen == 0 ||\r
7697       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7698        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7699     /* Scroll to make new end of text visible if old end of text\r
7700        was visible or new text is an echo of user typein */\r
7701     sel.cpMin = 9999999;\r
7702     sel.cpMax = 9999999;\r
7703     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7704     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7705     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7706     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7707   }\r
7708   if (savesel.cpMax == exlen || forceVisible) {\r
7709     /* Move insert point to new end of text if it was at the old\r
7710        end of text or if the new text is an echo of user typein */\r
7711     sel.cpMin = 9999999;\r
7712     sel.cpMax = 9999999;\r
7713     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7714   } else {\r
7715     /* Restore previous selection */\r
7716     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7717   }\r
7718   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7719 }\r
7720 \r
7721 /*---------*/\r
7722 \r
7723 \r
7724 void\r
7725 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7726 {\r
7727   char buf[100];\r
7728   char *str;\r
7729   COLORREF oldFg, oldBg;\r
7730   HFONT oldFont;\r
7731   RECT rect;\r
7732 \r
7733   if(copyNumber > 1)\r
7734     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7735 \r
7736   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7737   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7738   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7739 \r
7740   rect.left = x;\r
7741   rect.right = x + squareSize;\r
7742   rect.top  = y;\r
7743   rect.bottom = y + squareSize;\r
7744   str = buf;\r
7745 \r
7746   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7747                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7748              y, ETO_CLIPPED|ETO_OPAQUE,\r
7749              &rect, str, strlen(str), NULL);\r
7750 \r
7751   (void) SetTextColor(hdc, oldFg);\r
7752   (void) SetBkColor(hdc, oldBg);\r
7753   (void) SelectObject(hdc, oldFont);\r
7754 }\r
7755 \r
7756 void\r
7757 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7758               RECT *rect, char *color, char *flagFell)\r
7759 {\r
7760   char buf[100];\r
7761   char *str;\r
7762   COLORREF oldFg, oldBg;\r
7763   HFONT oldFont;\r
7764 \r
7765   if (twoBoards && partnerUp) return;\r
7766   if (appData.clockMode) {\r
7767     if (tinyLayout == 2)\r
7768       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7769     else\r
7770       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7771     str = buf;\r
7772   } else {\r
7773     str = color;\r
7774   }\r
7775 \r
7776   if (highlight) {\r
7777     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7778     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7779   } else {\r
7780     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7781     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7782   }\r
7783 \r
7784   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7785 \r
7786   JAWS_SILENCE\r
7787 \r
7788   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7789              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7790              rect, str, strlen(str), NULL);\r
7791   if(logoHeight > 0 && appData.clockMode) {\r
7792       RECT r;\r
7793       str += strlen(color)+2;\r
7794       r.top = rect->top + logoHeight/2;\r
7795       r.left = rect->left;\r
7796       r.right = rect->right;\r
7797       r.bottom = rect->bottom;\r
7798       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7799                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7800                  &r, str, strlen(str), NULL);\r
7801   }\r
7802   (void) SetTextColor(hdc, oldFg);\r
7803   (void) SetBkColor(hdc, oldBg);\r
7804   (void) SelectObject(hdc, oldFont);\r
7805 }\r
7806 \r
7807 \r
7808 int\r
7809 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7810            OVERLAPPED *ovl)\r
7811 {\r
7812   int ok, err;\r
7813 \r
7814   /* [AS]  */\r
7815   if( count <= 0 ) {\r
7816     if (appData.debugMode) {\r
7817       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7818     }\r
7819 \r
7820     return ERROR_INVALID_USER_BUFFER;\r
7821   }\r
7822 \r
7823   ResetEvent(ovl->hEvent);\r
7824   ovl->Offset = ovl->OffsetHigh = 0;\r
7825   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7826   if (ok) {\r
7827     err = NO_ERROR;\r
7828   } else {\r
7829     err = GetLastError();\r
7830     if (err == ERROR_IO_PENDING) {\r
7831       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7832       if (ok)\r
7833         err = NO_ERROR;\r
7834       else\r
7835         err = GetLastError();\r
7836     }\r
7837   }\r
7838   return err;\r
7839 }\r
7840 \r
7841 int\r
7842 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7843             OVERLAPPED *ovl)\r
7844 {\r
7845   int ok, err;\r
7846 \r
7847   ResetEvent(ovl->hEvent);\r
7848   ovl->Offset = ovl->OffsetHigh = 0;\r
7849   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7850   if (ok) {\r
7851     err = NO_ERROR;\r
7852   } else {\r
7853     err = GetLastError();\r
7854     if (err == ERROR_IO_PENDING) {\r
7855       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7856       if (ok)\r
7857         err = NO_ERROR;\r
7858       else\r
7859         err = GetLastError();\r
7860     }\r
7861 \r
7862   }\r
7863   return err;\r
7864 }\r
7865 \r
7866 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7867 void CheckForInputBufferFull( InputSource * is )\r
7868 {\r
7869     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7870         /* Look for end of line */\r
7871         char * p = is->buf;\r
7872         \r
7873         while( p < is->next && *p != '\n' ) {\r
7874             p++;\r
7875         }\r
7876 \r
7877         if( p >= is->next ) {\r
7878             if (appData.debugMode) {\r
7879                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7880             }\r
7881 \r
7882             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7883             is->count = (DWORD) -1;\r
7884             is->next = is->buf;\r
7885         }\r
7886     }\r
7887 }\r
7888 \r
7889 DWORD\r
7890 InputThread(LPVOID arg)\r
7891 {\r
7892   InputSource *is;\r
7893   OVERLAPPED ovl;\r
7894 \r
7895   is = (InputSource *) arg;\r
7896   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7897   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7898   while (is->hThread != NULL) {\r
7899     is->error = DoReadFile(is->hFile, is->next,\r
7900                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7901                            &is->count, &ovl);\r
7902     if (is->error == NO_ERROR) {\r
7903       is->next += is->count;\r
7904     } else {\r
7905       if (is->error == ERROR_BROKEN_PIPE) {\r
7906         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7907         is->count = 0;\r
7908       } else {\r
7909         is->count = (DWORD) -1;\r
7910         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7911         break; \r
7912       }\r
7913     }\r
7914 \r
7915     CheckForInputBufferFull( is );\r
7916 \r
7917     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7918 \r
7919     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7920 \r
7921     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7922   }\r
7923 \r
7924   CloseHandle(ovl.hEvent);\r
7925   CloseHandle(is->hFile);\r
7926 \r
7927   if (appData.debugMode) {\r
7928     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7929   }\r
7930 \r
7931   return 0;\r
7932 }\r
7933 \r
7934 \r
7935 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7936 DWORD\r
7937 NonOvlInputThread(LPVOID arg)\r
7938 {\r
7939   InputSource *is;\r
7940   char *p, *q;\r
7941   int i;\r
7942   char prev;\r
7943 \r
7944   is = (InputSource *) arg;\r
7945   while (is->hThread != NULL) {\r
7946     is->error = ReadFile(is->hFile, is->next,\r
7947                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7948                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7949     if (is->error == NO_ERROR) {\r
7950       /* Change CRLF to LF */\r
7951       if (is->next > is->buf) {\r
7952         p = is->next - 1;\r
7953         i = is->count + 1;\r
7954       } else {\r
7955         p = is->next;\r
7956         i = is->count;\r
7957       }\r
7958       q = p;\r
7959       prev = NULLCHAR;\r
7960       while (i > 0) {\r
7961         if (prev == '\r' && *p == '\n') {\r
7962           *(q-1) = '\n';\r
7963           is->count--;\r
7964         } else { \r
7965           *q++ = *p;\r
7966         }\r
7967         prev = *p++;\r
7968         i--;\r
7969       }\r
7970       *q = NULLCHAR;\r
7971       is->next = q;\r
7972     } else {\r
7973       if (is->error == ERROR_BROKEN_PIPE) {\r
7974         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7975         is->count = 0; \r
7976       } else {\r
7977         is->count = (DWORD) -1;\r
7978       }\r
7979     }\r
7980 \r
7981     CheckForInputBufferFull( is );\r
7982 \r
7983     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7984 \r
7985     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7986 \r
7987     if (is->count < 0) break;  /* Quit on error */\r
7988   }\r
7989   CloseHandle(is->hFile);\r
7990   return 0;\r
7991 }\r
7992 \r
7993 DWORD\r
7994 SocketInputThread(LPVOID arg)\r
7995 {\r
7996   InputSource *is;\r
7997 \r
7998   is = (InputSource *) arg;\r
7999   while (is->hThread != NULL) {\r
8000     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8001     if ((int)is->count == SOCKET_ERROR) {\r
8002       is->count = (DWORD) -1;\r
8003       is->error = WSAGetLastError();\r
8004     } else {\r
8005       is->error = NO_ERROR;\r
8006       is->next += is->count;\r
8007       if (is->count == 0 && is->second == is) {\r
8008         /* End of file on stderr; quit with no message */\r
8009         break;\r
8010       }\r
8011     }\r
8012     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8013 \r
8014     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8015 \r
8016     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8017   }\r
8018   return 0;\r
8019 }\r
8020 \r
8021 VOID\r
8022 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8023 {\r
8024   InputSource *is;\r
8025 \r
8026   is = (InputSource *) lParam;\r
8027   if (is->lineByLine) {\r
8028     /* Feed in lines one by one */\r
8029     char *p = is->buf;\r
8030     char *q = p;\r
8031     while (q < is->next) {\r
8032       if (*q++ == '\n') {\r
8033         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8034         p = q;\r
8035       }\r
8036     }\r
8037     \r
8038     /* Move any partial line to the start of the buffer */\r
8039     q = is->buf;\r
8040     while (p < is->next) {\r
8041       *q++ = *p++;\r
8042     }\r
8043     is->next = q;\r
8044 \r
8045     if (is->error != NO_ERROR || is->count == 0) {\r
8046       /* Notify backend of the error.  Note: If there was a partial\r
8047          line at the end, it is not flushed through. */\r
8048       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8049     }\r
8050   } else {\r
8051     /* Feed in the whole chunk of input at once */\r
8052     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8053     is->next = is->buf;\r
8054   }\r
8055 }\r
8056 \r
8057 /*---------------------------------------------------------------------------*\\r
8058  *\r
8059  *  Menu enables. Used when setting various modes.\r
8060  *\r
8061 \*---------------------------------------------------------------------------*/\r
8062 \r
8063 typedef struct {\r
8064   int item;\r
8065   int flags;\r
8066 } Enables;\r
8067 \r
8068 VOID\r
8069 GreyRevert(Boolean grey)\r
8070 { // [HGM] vari: for retracting variations in local mode\r
8071   HMENU hmenu = GetMenu(hwndMain);\r
8072   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8073   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8074 }\r
8075 \r
8076 VOID\r
8077 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8078 {\r
8079   while (enab->item > 0) {\r
8080     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8081     enab++;\r
8082   }\r
8083 }\r
8084 \r
8085 Enables gnuEnables[] = {\r
8086   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8087   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8088   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8089   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8090   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8091   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8092   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8093   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8094   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8095   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
8096   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8099 \r
8100 \r
8101   // Needed to switch from ncp to GNU mode on Engine Load\r
8102   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8103   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8104   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8105   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8106   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8107   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8108   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8109   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8110   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8111   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8112   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8113   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8114   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8115   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8116   { -1, -1 }\r
8117 };\r
8118 \r
8119 Enables icsEnables[] = {\r
8120   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8121   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8122   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8123   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8124   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8125   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8126   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8127   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8128   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8129   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8130   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8131   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8132   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8133   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8134   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8135   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8136   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8137   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8138   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8139   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8140   { -1, -1 }\r
8141 };\r
8142 \r
8143 #if ZIPPY\r
8144 Enables zippyEnables[] = {\r
8145   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8146   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8147   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8148   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8149   { -1, -1 }\r
8150 };\r
8151 #endif\r
8152 \r
8153 Enables ncpEnables[] = {\r
8154   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8155   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8156   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8157   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8158   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8159   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8160   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8161   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8162   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8163   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8164   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8165   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8166   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8167   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8168   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8169   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8170   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8171   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8172   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8173   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8174   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8175   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8176   { -1, -1 }\r
8177 };\r
8178 \r
8179 Enables trainingOnEnables[] = {\r
8180   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8181   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8182   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8183   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8184   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8185   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8186   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8187   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8188   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8189   { -1, -1 }\r
8190 };\r
8191 \r
8192 Enables trainingOffEnables[] = {\r
8193   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8194   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8195   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8196   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8197   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8198   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8199   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8200   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8201   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8202   { -1, -1 }\r
8203 };\r
8204 \r
8205 /* These modify either ncpEnables or gnuEnables */\r
8206 Enables cmailEnables[] = {\r
8207   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8208   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8209   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8210   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8211   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8212   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8213   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8214   { -1, -1 }\r
8215 };\r
8216 \r
8217 Enables machineThinkingEnables[] = {\r
8218   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8219   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8220   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8221   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8222   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8223   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8224   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8225   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8226   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8227   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8228   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8229   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8230   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8231 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8232   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8233   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8234   { -1, -1 }\r
8235 };\r
8236 \r
8237 Enables userThinkingEnables[] = {\r
8238   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8239   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8240   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8241   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8242   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8243   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8244   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8245   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8246   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8247   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8248   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8249   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8250   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8251 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8252   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8253   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8254   { -1, -1 }\r
8255 };\r
8256 \r
8257 /*---------------------------------------------------------------------------*\\r
8258  *\r
8259  *  Front-end interface functions exported by XBoard.\r
8260  *  Functions appear in same order as prototypes in frontend.h.\r
8261  * \r
8262 \*---------------------------------------------------------------------------*/\r
8263 VOID\r
8264 CheckMark(UINT item, int state)\r
8265 {\r
8266     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8267 }\r
8268 \r
8269 VOID\r
8270 ModeHighlight()\r
8271 {\r
8272   static UINT prevChecked = 0;\r
8273   static int prevPausing = 0;\r
8274   UINT nowChecked;\r
8275 \r
8276   if (pausing != prevPausing) {\r
8277     prevPausing = pausing;\r
8278     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8279                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8280     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8281   }\r
8282 \r
8283   switch (gameMode) {\r
8284   case BeginningOfGame:\r
8285     if (appData.icsActive)\r
8286       nowChecked = IDM_IcsClient;\r
8287     else if (appData.noChessProgram)\r
8288       nowChecked = IDM_EditGame;\r
8289     else\r
8290       nowChecked = IDM_MachineBlack;\r
8291     break;\r
8292   case MachinePlaysBlack:\r
8293     nowChecked = IDM_MachineBlack;\r
8294     break;\r
8295   case MachinePlaysWhite:\r
8296     nowChecked = IDM_MachineWhite;\r
8297     break;\r
8298   case TwoMachinesPlay:\r
8299     nowChecked = IDM_TwoMachines;\r
8300     break;\r
8301   case AnalyzeMode:\r
8302     nowChecked = IDM_AnalysisMode;\r
8303     break;\r
8304   case AnalyzeFile:\r
8305     nowChecked = IDM_AnalyzeFile;\r
8306     break;\r
8307   case EditGame:\r
8308     nowChecked = IDM_EditGame;\r
8309     break;\r
8310   case PlayFromGameFile:\r
8311     nowChecked = IDM_LoadGame;\r
8312     break;\r
8313   case EditPosition:\r
8314     nowChecked = IDM_EditPosition;\r
8315     break;\r
8316   case Training:\r
8317     nowChecked = IDM_Training;\r
8318     break;\r
8319   case IcsPlayingWhite:\r
8320   case IcsPlayingBlack:\r
8321   case IcsObserving:\r
8322   case IcsIdle:\r
8323     nowChecked = IDM_IcsClient;\r
8324     break;\r
8325   default:\r
8326   case EndOfGame:\r
8327     nowChecked = 0;\r
8328     break;\r
8329   }\r
8330   if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8331     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
8332   CheckMark(prevChecked, MF_UNCHECKED);\r
8333   CheckMark(nowChecked, MF_CHECKED);\r
8334   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8335 \r
8336   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8337     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8338                           MF_BYCOMMAND|MF_ENABLED);\r
8339   } else {\r
8340     (void) EnableMenuItem(GetMenu(hwndMain), \r
8341                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8342   }\r
8343 \r
8344   prevChecked = nowChecked;\r
8345 \r
8346   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8347   if (appData.icsActive) {\r
8348        if (appData.icsEngineAnalyze) {\r
8349                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8350        } else {\r
8351                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8352        }\r
8353   }\r
8354   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8355 }\r
8356 \r
8357 VOID\r
8358 SetICSMode()\r
8359 {\r
8360   HMENU hmenu = GetMenu(hwndMain);\r
8361   SetMenuEnables(hmenu, icsEnables);\r
8362   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8363     MF_BYCOMMAND|MF_ENABLED);\r
8364 #if ZIPPY\r
8365   if (appData.zippyPlay) {\r
8366     SetMenuEnables(hmenu, zippyEnables);\r
8367     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8368          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8369           MF_BYCOMMAND|MF_ENABLED);\r
8370   }\r
8371 #endif\r
8372 }\r
8373 \r
8374 VOID\r
8375 SetGNUMode()\r
8376 {\r
8377   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8378 }\r
8379 \r
8380 VOID\r
8381 SetNCPMode()\r
8382 {\r
8383   HMENU hmenu = GetMenu(hwndMain);\r
8384   SetMenuEnables(hmenu, ncpEnables);\r
8385     DrawMenuBar(hwndMain);\r
8386 }\r
8387 \r
8388 VOID\r
8389 SetCmailMode()\r
8390 {\r
8391   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8392 }\r
8393 \r
8394 VOID \r
8395 SetTrainingModeOn()\r
8396 {\r
8397   int i;\r
8398   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8399   for (i = 0; i < N_BUTTONS; i++) {\r
8400     if (buttonDesc[i].hwnd != NULL)\r
8401       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8402   }\r
8403   CommentPopDown();\r
8404 }\r
8405 \r
8406 VOID SetTrainingModeOff()\r
8407 {\r
8408   int i;\r
8409   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8410   for (i = 0; i < N_BUTTONS; i++) {\r
8411     if (buttonDesc[i].hwnd != NULL)\r
8412       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8413   }\r
8414 }\r
8415 \r
8416 \r
8417 VOID\r
8418 SetUserThinkingEnables()\r
8419 {\r
8420   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8421 }\r
8422 \r
8423 VOID\r
8424 SetMachineThinkingEnables()\r
8425 {\r
8426   HMENU hMenu = GetMenu(hwndMain);\r
8427   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8428 \r
8429   SetMenuEnables(hMenu, machineThinkingEnables);\r
8430 \r
8431   if (gameMode == MachinePlaysBlack) {\r
8432     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8433   } else if (gameMode == MachinePlaysWhite) {\r
8434     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8435   } else if (gameMode == TwoMachinesPlay) {\r
8436     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8437   }\r
8438 }\r
8439 \r
8440 \r
8441 VOID\r
8442 DisplayTitle(char *str)\r
8443 {\r
8444   char title[MSG_SIZ], *host;\r
8445   if (str[0] != NULLCHAR) {\r
8446     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8447   } else if (appData.icsActive) {\r
8448     if (appData.icsCommPort[0] != NULLCHAR)\r
8449       host = "ICS";\r
8450     else \r
8451       host = appData.icsHost;\r
8452       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8453   } else if (appData.noChessProgram) {\r
8454     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8455   } else {\r
8456     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8457     strcat(title, ": ");\r
8458     strcat(title, first.tidy);\r
8459   }\r
8460   SetWindowText(hwndMain, title);\r
8461 }\r
8462 \r
8463 \r
8464 VOID\r
8465 DisplayMessage(char *str1, char *str2)\r
8466 {\r
8467   HDC hdc;\r
8468   HFONT oldFont;\r
8469   int remain = MESSAGE_TEXT_MAX - 1;\r
8470   int len;\r
8471 \r
8472   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8473   messageText[0] = NULLCHAR;\r
8474   if (*str1) {\r
8475     len = strlen(str1);\r
8476     if (len > remain) len = remain;\r
8477     strncpy(messageText, str1, len);\r
8478     messageText[len] = NULLCHAR;\r
8479     remain -= len;\r
8480   }\r
8481   if (*str2 && remain >= 2) {\r
8482     if (*str1) {\r
8483       strcat(messageText, "  ");\r
8484       remain -= 2;\r
8485     }\r
8486     len = strlen(str2);\r
8487     if (len > remain) len = remain;\r
8488     strncat(messageText, str2, len);\r
8489   }\r
8490   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8491   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8492 \r
8493   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8494 \r
8495   SAYMACHINEMOVE();\r
8496 \r
8497   hdc = GetDC(hwndMain);\r
8498   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8499   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8500              &messageRect, messageText, strlen(messageText), NULL);\r
8501   (void) SelectObject(hdc, oldFont);\r
8502   (void) ReleaseDC(hwndMain, hdc);\r
8503 }\r
8504 \r
8505 VOID\r
8506 DisplayError(char *str, int error)\r
8507 {\r
8508   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8509   int len;\r
8510 \r
8511   if (error == 0) {\r
8512     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8513   } else {\r
8514     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8515                         NULL, error, LANG_NEUTRAL,\r
8516                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8517     if (len > 0) {\r
8518       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8519     } else {\r
8520       ErrorMap *em = errmap;\r
8521       while (em->err != 0 && em->err != error) em++;\r
8522       if (em->err != 0) {\r
8523         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8524       } else {\r
8525         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8526       }\r
8527     }\r
8528   }\r
8529   \r
8530   ErrorPopUp(_("Error"), buf);\r
8531 }\r
8532 \r
8533 \r
8534 VOID\r
8535 DisplayMoveError(char *str)\r
8536 {\r
8537   fromX = fromY = -1;\r
8538   ClearHighlights();\r
8539   DrawPosition(FALSE, NULL);\r
8540   if (appData.popupMoveErrors) {\r
8541     ErrorPopUp(_("Error"), str);\r
8542   } else {\r
8543     DisplayMessage(str, "");\r
8544     moveErrorMessageUp = TRUE;\r
8545   }\r
8546 }\r
8547 \r
8548 VOID\r
8549 DisplayFatalError(char *str, int error, int exitStatus)\r
8550 {\r
8551   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8552   int len;\r
8553   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8554 \r
8555   if (error != 0) {\r
8556     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8557                         NULL, error, LANG_NEUTRAL,\r
8558                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8559     if (len > 0) {\r
8560       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8561     } else {\r
8562       ErrorMap *em = errmap;\r
8563       while (em->err != 0 && em->err != error) em++;\r
8564       if (em->err != 0) {\r
8565         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8566       } else {\r
8567         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8568       }\r
8569     }\r
8570     str = buf;\r
8571   }\r
8572   if (appData.debugMode) {\r
8573     fprintf(debugFP, "%s: %s\n", label, str);\r
8574   }\r
8575   if (appData.popupExitMessage) {\r
8576     if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!\r
8577     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8578                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8579   }\r
8580   ExitEvent(exitStatus);\r
8581 }\r
8582 \r
8583 \r
8584 VOID\r
8585 DisplayInformation(char *str)\r
8586 {\r
8587   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8588 }\r
8589 \r
8590 char *\r
8591 Shorten (char *s)\r
8592 {\r
8593   return s;\r
8594 }\r
8595 \r
8596 VOID\r
8597 DisplayNote(char *str)\r
8598 {\r
8599   ErrorPopUp(_("Note"), str);\r
8600 }\r
8601 \r
8602 \r
8603 typedef struct {\r
8604   char *title, *question, *replyPrefix;\r
8605   ProcRef pr;\r
8606 } QuestionParams;\r
8607 \r
8608 LRESULT CALLBACK\r
8609 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8610 {\r
8611   static QuestionParams *qp;\r
8612   char reply[MSG_SIZ];\r
8613   int len, err;\r
8614 \r
8615   switch (message) {\r
8616   case WM_INITDIALOG:\r
8617     qp = (QuestionParams *) lParam;\r
8618     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8619     Translate(hDlg, DLG_Question);\r
8620     SetWindowText(hDlg, qp->title);\r
8621     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8622     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8623     return FALSE;\r
8624 \r
8625   case WM_COMMAND:\r
8626     switch (LOWORD(wParam)) {\r
8627     case IDOK:\r
8628       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8629       if (*reply) strcat(reply, " ");\r
8630       len = strlen(reply);\r
8631       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8632       strcat(reply, "\n");\r
8633       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8634       EndDialog(hDlg, TRUE);\r
8635       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8636       return TRUE;\r
8637     case IDCANCEL:\r
8638       EndDialog(hDlg, FALSE);\r
8639       return TRUE;\r
8640     default:\r
8641       break;\r
8642     }\r
8643     break;\r
8644   }\r
8645   return FALSE;\r
8646 }\r
8647 \r
8648 VOID\r
8649 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8650 {\r
8651     QuestionParams qp;\r
8652     FARPROC lpProc;\r
8653     \r
8654     qp.title = title;\r
8655     qp.question = question;\r
8656     qp.replyPrefix = replyPrefix;\r
8657     qp.pr = pr;\r
8658     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8659     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8660       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8661     FreeProcInstance(lpProc);\r
8662 }\r
8663 \r
8664 /* [AS] Pick FRC position */\r
8665 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8666 {\r
8667     static int * lpIndexFRC;\r
8668     BOOL index_is_ok;\r
8669     char buf[16];\r
8670 \r
8671     switch( message )\r
8672     {\r
8673     case WM_INITDIALOG:\r
8674         lpIndexFRC = (int *) lParam;\r
8675 \r
8676         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8677         Translate(hDlg, DLG_NewGameFRC);\r
8678 \r
8679         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8680         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8681         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8682         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8683 \r
8684         break;\r
8685 \r
8686     case WM_COMMAND:\r
8687         switch( LOWORD(wParam) ) {\r
8688         case IDOK:\r
8689             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8690             EndDialog( hDlg, 0 );\r
8691             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8692             return TRUE;\r
8693         case IDCANCEL:\r
8694             EndDialog( hDlg, 1 );   \r
8695             return TRUE;\r
8696         case IDC_NFG_Edit:\r
8697             if( HIWORD(wParam) == EN_CHANGE ) {\r
8698                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8699 \r
8700                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8701             }\r
8702             return TRUE;\r
8703         case IDC_NFG_Random:\r
8704           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8705             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8706             return TRUE;\r
8707         }\r
8708 \r
8709         break;\r
8710     }\r
8711 \r
8712     return FALSE;\r
8713 }\r
8714 \r
8715 int NewGameFRC()\r
8716 {\r
8717     int result;\r
8718     int index = appData.defaultFrcPosition;\r
8719     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8720 \r
8721     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8722 \r
8723     if( result == 0 ) {\r
8724         appData.defaultFrcPosition = index;\r
8725     }\r
8726 \r
8727     return result;\r
8728 }\r
8729 \r
8730 /* [AS] Game list options. Refactored by HGM */\r
8731 \r
8732 HWND gameListOptionsDialog;\r
8733 \r
8734 // low-level front-end: clear text edit / list widget\r
8735 void\r
8736 \r
8737 GLT_ClearList()\r
8738 {\r
8739     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8740 }\r
8741 \r
8742 // low-level front-end: clear text edit / list widget\r
8743 void\r
8744 GLT_DeSelectList()\r
8745 {\r
8746     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8747 }\r
8748 \r
8749 // low-level front-end: append line to text edit / list widget\r
8750 void\r
8751 GLT_AddToList( char *name )\r
8752 {\r
8753     if( name != 0 ) {\r
8754             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8755     }\r
8756 }\r
8757 \r
8758 // low-level front-end: get line from text edit / list widget\r
8759 Boolean\r
8760 GLT_GetFromList( int index, char *name )\r
8761 {\r
8762     if( name != 0 ) {\r
8763             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8764                 return TRUE;\r
8765     }\r
8766     return FALSE;\r
8767 }\r
8768 \r
8769 void GLT_MoveSelection( HWND hDlg, int delta )\r
8770 {\r
8771     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8772     int idx2 = idx1 + delta;\r
8773     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8774 \r
8775     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8776         char buf[128];\r
8777 \r
8778         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8779         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8780         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8781         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8782     }\r
8783 }\r
8784 \r
8785 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8786 {\r
8787     switch( message )\r
8788     {\r
8789     case WM_INITDIALOG:\r
8790         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8791         \r
8792         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8793         Translate(hDlg, DLG_GameListOptions);\r
8794 \r
8795         /* Initialize list */\r
8796         GLT_TagsToList( lpUserGLT );\r
8797 \r
8798         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8799 \r
8800         break;\r
8801 \r
8802     case WM_COMMAND:\r
8803         switch( LOWORD(wParam) ) {\r
8804         case IDOK:\r
8805             GLT_ParseList();\r
8806             EndDialog( hDlg, 0 );\r
8807             return TRUE;\r
8808         case IDCANCEL:\r
8809             EndDialog( hDlg, 1 );\r
8810             return TRUE;\r
8811 \r
8812         case IDC_GLT_Default:\r
8813             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8814             return TRUE;\r
8815 \r
8816         case IDC_GLT_Restore:\r
8817             GLT_TagsToList( appData.gameListTags );\r
8818             return TRUE;\r
8819 \r
8820         case IDC_GLT_Up:\r
8821             GLT_MoveSelection( hDlg, -1 );\r
8822             return TRUE;\r
8823 \r
8824         case IDC_GLT_Down:\r
8825             GLT_MoveSelection( hDlg, +1 );\r
8826             return TRUE;\r
8827         }\r
8828 \r
8829         break;\r
8830     }\r
8831 \r
8832     return FALSE;\r
8833 }\r
8834 \r
8835 int GameListOptions()\r
8836 {\r
8837     int result;\r
8838     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8839 \r
8840       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8841 \r
8842     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8843 \r
8844     if( result == 0 ) {\r
8845         char *oldTags = appData.gameListTags;\r
8846         /* [AS] Memory leak here! */\r
8847         appData.gameListTags = strdup( lpUserGLT ); \r
8848         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8849             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8850     }\r
8851 \r
8852     return result;\r
8853 }\r
8854 \r
8855 VOID\r
8856 DisplayIcsInteractionTitle(char *str)\r
8857 {\r
8858   char consoleTitle[MSG_SIZ];\r
8859 \r
8860     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8861     SetWindowText(hwndConsole, consoleTitle);\r
8862 \r
8863     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8864       char buf[MSG_SIZ], *p = buf, *q;\r
8865         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8866       do {\r
8867         q = strchr(p, ';');\r
8868         if(q) *q++ = 0;\r
8869         if(*p) ChatPopUp(p);\r
8870       } while(p=q);\r
8871     }\r
8872 \r
8873     SetActiveWindow(hwndMain);\r
8874 }\r
8875 \r
8876 void\r
8877 DrawPosition(int fullRedraw, Board board)\r
8878 {\r
8879   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8880 }\r
8881 \r
8882 void NotifyFrontendLogin()\r
8883 {\r
8884         if (hwndConsole)\r
8885                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8886 }\r
8887 \r
8888 VOID\r
8889 ResetFrontEnd()\r
8890 {\r
8891   fromX = fromY = -1;\r
8892   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8893     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8894     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8895     dragInfo.lastpos = dragInfo.pos;\r
8896     dragInfo.start.x = dragInfo.start.y = -1;\r
8897     dragInfo.from = dragInfo.start;\r
8898     ReleaseCapture();\r
8899     DrawPosition(TRUE, NULL);\r
8900   }\r
8901   TagsPopDown();\r
8902 }\r
8903 \r
8904 \r
8905 VOID\r
8906 CommentPopUp(char *title, char *str)\r
8907 {\r
8908   HWND hwnd = GetActiveWindow();\r
8909   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8910   SAY(str);\r
8911   SetActiveWindow(hwnd);\r
8912 }\r
8913 \r
8914 VOID\r
8915 CommentPopDown(void)\r
8916 {\r
8917   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8918   if (commentDialog) {\r
8919     ShowWindow(commentDialog, SW_HIDE);\r
8920   }\r
8921   commentUp = FALSE;\r
8922 }\r
8923 \r
8924 VOID\r
8925 EditCommentPopUp(int index, char *title, char *str)\r
8926 {\r
8927   EitherCommentPopUp(index, title, str, TRUE);\r
8928 }\r
8929 \r
8930 \r
8931 int\r
8932 Roar()\r
8933 {\r
8934   MyPlaySound(&sounds[(int)SoundRoar]);\r
8935   return 1;\r
8936 }\r
8937 \r
8938 VOID\r
8939 RingBell()\r
8940 {\r
8941   MyPlaySound(&sounds[(int)SoundMove]);\r
8942 }\r
8943 \r
8944 VOID PlayIcsWinSound()\r
8945 {\r
8946   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8947 }\r
8948 \r
8949 VOID PlayIcsLossSound()\r
8950 {\r
8951   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8952 }\r
8953 \r
8954 VOID PlayIcsDrawSound()\r
8955 {\r
8956   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8957 }\r
8958 \r
8959 VOID PlayIcsUnfinishedSound()\r
8960 {\r
8961   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8962 }\r
8963 \r
8964 VOID\r
8965 PlayAlarmSound()\r
8966 {\r
8967   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8968 }\r
8969 \r
8970 VOID\r
8971 PlayTellSound()\r
8972 {\r
8973   MyPlaySound(&textAttribs[ColorTell].sound);\r
8974 }\r
8975 \r
8976 \r
8977 VOID\r
8978 EchoOn()\r
8979 {\r
8980   HWND hInput;\r
8981   consoleEcho = TRUE;\r
8982   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8983   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8984   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8985 }\r
8986 \r
8987 \r
8988 VOID\r
8989 EchoOff()\r
8990 {\r
8991   CHARFORMAT cf;\r
8992   HWND hInput;\r
8993   consoleEcho = FALSE;\r
8994   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8995   /* This works OK: set text and background both to the same color */\r
8996   cf = consoleCF;\r
8997   cf.crTextColor = COLOR_ECHOOFF;\r
8998   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8999   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9000 }\r
9001 \r
9002 /* No Raw()...? */\r
9003 \r
9004 void Colorize(ColorClass cc, int continuation)\r
9005 {\r
9006   currentColorClass = cc;\r
9007   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9008   consoleCF.crTextColor = textAttribs[cc].color;\r
9009   consoleCF.dwEffects = textAttribs[cc].effects;\r
9010   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9011 }\r
9012 \r
9013 char *\r
9014 UserName()\r
9015 {\r
9016   static char buf[MSG_SIZ];\r
9017   DWORD bufsiz = MSG_SIZ;\r
9018 \r
9019   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9020         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9021   }\r
9022   if (!GetUserName(buf, &bufsiz)) {\r
9023     /*DisplayError("Error getting user name", GetLastError());*/\r
9024     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
9025   }\r
9026   return buf;\r
9027 }\r
9028 \r
9029 char *\r
9030 HostName()\r
9031 {\r
9032   static char buf[MSG_SIZ];\r
9033   DWORD bufsiz = MSG_SIZ;\r
9034 \r
9035   if (!GetComputerName(buf, &bufsiz)) {\r
9036     /*DisplayError("Error getting host name", GetLastError());*/\r
9037     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
9038   }\r
9039   return buf;\r
9040 }\r
9041 \r
9042 \r
9043 int\r
9044 ClockTimerRunning()\r
9045 {\r
9046   return clockTimerEvent != 0;\r
9047 }\r
9048 \r
9049 int\r
9050 StopClockTimer()\r
9051 {\r
9052   if (clockTimerEvent == 0) return FALSE;\r
9053   KillTimer(hwndMain, clockTimerEvent);\r
9054   clockTimerEvent = 0;\r
9055   return TRUE;\r
9056 }\r
9057 \r
9058 void\r
9059 StartClockTimer(long millisec)\r
9060 {\r
9061   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9062                              (UINT) millisec, NULL);\r
9063 }\r
9064 \r
9065 void\r
9066 DisplayWhiteClock(long timeRemaining, int highlight)\r
9067 {\r
9068   HDC hdc;\r
9069   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9070 \r
9071   if(appData.noGUI) return;\r
9072   hdc = GetDC(hwndMain);\r
9073   if (!IsIconic(hwndMain)) {\r
9074     DisplayAClock(hdc, timeRemaining, highlight, \r
9075                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
9076   }\r
9077   if (highlight && iconCurrent == iconBlack) {\r
9078     iconCurrent = iconWhite;\r
9079     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9080     if (IsIconic(hwndMain)) {\r
9081       DrawIcon(hdc, 2, 2, iconCurrent);\r
9082     }\r
9083   }\r
9084   (void) ReleaseDC(hwndMain, hdc);\r
9085   if (hwndConsole)\r
9086     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9087 }\r
9088 \r
9089 void\r
9090 DisplayBlackClock(long timeRemaining, int highlight)\r
9091 {\r
9092   HDC hdc;\r
9093   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9094 \r
9095 \r
9096   if(appData.noGUI) return;\r
9097   hdc = GetDC(hwndMain);\r
9098   if (!IsIconic(hwndMain)) {\r
9099     DisplayAClock(hdc, timeRemaining, highlight, \r
9100                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
9101   }\r
9102   if (highlight && iconCurrent == iconWhite) {\r
9103     iconCurrent = iconBlack;\r
9104     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9105     if (IsIconic(hwndMain)) {\r
9106       DrawIcon(hdc, 2, 2, iconCurrent);\r
9107     }\r
9108   }\r
9109   (void) ReleaseDC(hwndMain, hdc);\r
9110   if (hwndConsole)\r
9111     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9112 }\r
9113 \r
9114 \r
9115 int\r
9116 LoadGameTimerRunning()\r
9117 {\r
9118   return loadGameTimerEvent != 0;\r
9119 }\r
9120 \r
9121 int\r
9122 StopLoadGameTimer()\r
9123 {\r
9124   if (loadGameTimerEvent == 0) return FALSE;\r
9125   KillTimer(hwndMain, loadGameTimerEvent);\r
9126   loadGameTimerEvent = 0;\r
9127   return TRUE;\r
9128 }\r
9129 \r
9130 void\r
9131 StartLoadGameTimer(long millisec)\r
9132 {\r
9133   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9134                                 (UINT) millisec, NULL);\r
9135 }\r
9136 \r
9137 void\r
9138 AutoSaveGame()\r
9139 {\r
9140   char *defName;\r
9141   FILE *f;\r
9142   char fileTitle[MSG_SIZ];\r
9143 \r
9144   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9145   f = OpenFileDialog(hwndMain, "a", defName,\r
9146                      appData.oldSaveStyle ? "gam" : "pgn",\r
9147                      GAME_FILT, \r
9148                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9149   if (f != NULL) {\r
9150     SaveGame(f, 0, "");\r
9151     fclose(f);\r
9152   }\r
9153 }\r
9154 \r
9155 \r
9156 void\r
9157 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9158 {\r
9159   if (delayedTimerEvent != 0) {\r
9160     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9161       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9162     }\r
9163     KillTimer(hwndMain, delayedTimerEvent);\r
9164     delayedTimerEvent = 0;\r
9165     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9166     delayedTimerCallback();\r
9167   }\r
9168   delayedTimerCallback = cb;\r
9169   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9170                                 (UINT) millisec, NULL);\r
9171 }\r
9172 \r
9173 DelayedEventCallback\r
9174 GetDelayedEvent()\r
9175 {\r
9176   if (delayedTimerEvent) {\r
9177     return delayedTimerCallback;\r
9178   } else {\r
9179     return NULL;\r
9180   }\r
9181 }\r
9182 \r
9183 void\r
9184 CancelDelayedEvent()\r
9185 {\r
9186   if (delayedTimerEvent) {\r
9187     KillTimer(hwndMain, delayedTimerEvent);\r
9188     delayedTimerEvent = 0;\r
9189   }\r
9190 }\r
9191 \r
9192 DWORD GetWin32Priority(int nice)\r
9193 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9194 /*\r
9195 REALTIME_PRIORITY_CLASS     0x00000100\r
9196 HIGH_PRIORITY_CLASS         0x00000080\r
9197 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9198 NORMAL_PRIORITY_CLASS       0x00000020\r
9199 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9200 IDLE_PRIORITY_CLASS         0x00000040\r
9201 */\r
9202         if (nice < -15) return 0x00000080;\r
9203         if (nice < 0)   return 0x00008000;\r
9204 \r
9205         if (nice == 0)  return 0x00000020;\r
9206         if (nice < 15)  return 0x00004000;\r
9207         return 0x00000040;\r
9208 }\r
9209 \r
9210 void RunCommand(char *cmdLine)\r
9211 {\r
9212   /* Now create the child process. */\r
9213   STARTUPINFO siStartInfo;\r
9214   PROCESS_INFORMATION piProcInfo;\r
9215 \r
9216   siStartInfo.cb = sizeof(STARTUPINFO);\r
9217   siStartInfo.lpReserved = NULL;\r
9218   siStartInfo.lpDesktop = NULL;\r
9219   siStartInfo.lpTitle = NULL;\r
9220   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9221   siStartInfo.cbReserved2 = 0;\r
9222   siStartInfo.lpReserved2 = NULL;\r
9223   siStartInfo.hStdInput = NULL;\r
9224   siStartInfo.hStdOutput = NULL;\r
9225   siStartInfo.hStdError = NULL;\r
9226 \r
9227   CreateProcess(NULL,\r
9228                 cmdLine,           /* command line */\r
9229                 NULL,      /* process security attributes */\r
9230                 NULL,      /* primary thread security attrs */\r
9231                 TRUE,      /* handles are inherited */\r
9232                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9233                 NULL,      /* use parent's environment */\r
9234                 NULL,\r
9235                 &siStartInfo, /* STARTUPINFO pointer */\r
9236                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9237 \r
9238   CloseHandle(piProcInfo.hThread);\r
9239 }\r
9240 \r
9241 /* Start a child process running the given program.\r
9242    The process's standard output can be read from "from", and its\r
9243    standard input can be written to "to".\r
9244    Exit with fatal error if anything goes wrong.\r
9245    Returns an opaque pointer that can be used to destroy the process\r
9246    later.\r
9247 */\r
9248 int\r
9249 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9250 {\r
9251 #define BUFSIZE 4096\r
9252 \r
9253   HANDLE hChildStdinRd, hChildStdinWr,\r
9254     hChildStdoutRd, hChildStdoutWr;\r
9255   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9256   SECURITY_ATTRIBUTES saAttr;\r
9257   BOOL fSuccess;\r
9258   PROCESS_INFORMATION piProcInfo;\r
9259   STARTUPINFO siStartInfo;\r
9260   ChildProc *cp;\r
9261   char buf[MSG_SIZ];\r
9262   DWORD err;\r
9263 \r
9264   if (appData.debugMode) {\r
9265     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9266   }\r
9267 \r
9268   *pr = NoProc;\r
9269 \r
9270   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9271   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9272   saAttr.bInheritHandle = TRUE;\r
9273   saAttr.lpSecurityDescriptor = NULL;\r
9274 \r
9275   /*\r
9276    * The steps for redirecting child's STDOUT:\r
9277    *     1. Create anonymous pipe to be STDOUT for child.\r
9278    *     2. Create a noninheritable duplicate of read handle,\r
9279    *         and close the inheritable read handle.\r
9280    */\r
9281 \r
9282   /* Create a pipe for the child's STDOUT. */\r
9283   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9284     return GetLastError();\r
9285   }\r
9286 \r
9287   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9288   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9289                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9290                              FALSE,     /* not inherited */\r
9291                              DUPLICATE_SAME_ACCESS);\r
9292   if (! fSuccess) {\r
9293     return GetLastError();\r
9294   }\r
9295   CloseHandle(hChildStdoutRd);\r
9296 \r
9297   /*\r
9298    * The steps for redirecting child's STDIN:\r
9299    *     1. Create anonymous pipe to be STDIN for child.\r
9300    *     2. Create a noninheritable duplicate of write handle,\r
9301    *         and close the inheritable write handle.\r
9302    */\r
9303 \r
9304   /* Create a pipe for the child's STDIN. */\r
9305   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9306     return GetLastError();\r
9307   }\r
9308 \r
9309   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9310   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9311                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9312                              FALSE,     /* not inherited */\r
9313                              DUPLICATE_SAME_ACCESS);\r
9314   if (! fSuccess) {\r
9315     return GetLastError();\r
9316   }\r
9317   CloseHandle(hChildStdinWr);\r
9318 \r
9319   /* Arrange to (1) look in dir for the child .exe file, and\r
9320    * (2) have dir be the child's working directory.  Interpret\r
9321    * dir relative to the directory WinBoard loaded from. */\r
9322   GetCurrentDirectory(MSG_SIZ, buf);\r
9323   SetCurrentDirectory(installDir);\r
9324   SetCurrentDirectory(dir);\r
9325 \r
9326   /* Now create the child process. */\r
9327 \r
9328   siStartInfo.cb = sizeof(STARTUPINFO);\r
9329   siStartInfo.lpReserved = NULL;\r
9330   siStartInfo.lpDesktop = NULL;\r
9331   siStartInfo.lpTitle = NULL;\r
9332   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9333   siStartInfo.cbReserved2 = 0;\r
9334   siStartInfo.lpReserved2 = NULL;\r
9335   siStartInfo.hStdInput = hChildStdinRd;\r
9336   siStartInfo.hStdOutput = hChildStdoutWr;\r
9337   siStartInfo.hStdError = hChildStdoutWr;\r
9338 \r
9339   fSuccess = CreateProcess(NULL,\r
9340                            cmdLine,        /* command line */\r
9341                            NULL,           /* process security attributes */\r
9342                            NULL,           /* primary thread security attrs */\r
9343                            TRUE,           /* handles are inherited */\r
9344                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9345                            NULL,           /* use parent's environment */\r
9346                            NULL,\r
9347                            &siStartInfo, /* STARTUPINFO pointer */\r
9348                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9349 \r
9350   err = GetLastError();\r
9351   SetCurrentDirectory(buf); /* return to prev directory */\r
9352   if (! fSuccess) {\r
9353     return err;\r
9354   }\r
9355 \r
9356   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9357     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9358     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9359   }\r
9360 \r
9361   /* Close the handles we don't need in the parent */\r
9362   CloseHandle(piProcInfo.hThread);\r
9363   CloseHandle(hChildStdinRd);\r
9364   CloseHandle(hChildStdoutWr);\r
9365 \r
9366   /* Prepare return value */\r
9367   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9368   cp->kind = CPReal;\r
9369   cp->hProcess = piProcInfo.hProcess;\r
9370   cp->pid = piProcInfo.dwProcessId;\r
9371   cp->hFrom = hChildStdoutRdDup;\r
9372   cp->hTo = hChildStdinWrDup;\r
9373 \r
9374   *pr = (void *) cp;\r
9375 \r
9376   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9377      2000 where engines sometimes don't see the initial command(s)\r
9378      from WinBoard and hang.  I don't understand how that can happen,\r
9379      but the Sleep is harmless, so I've put it in.  Others have also\r
9380      reported what may be the same problem, so hopefully this will fix\r
9381      it for them too.  */\r
9382   Sleep(500);\r
9383 \r
9384   return NO_ERROR;\r
9385 }\r
9386 \r
9387 \r
9388 void\r
9389 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9390 {\r
9391   ChildProc *cp; int result;\r
9392 \r
9393   cp = (ChildProc *) pr;\r
9394   if (cp == NULL) return;\r
9395 \r
9396   switch (cp->kind) {\r
9397   case CPReal:\r
9398     /* TerminateProcess is considered harmful, so... */\r
9399     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9400     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9401     /* The following doesn't work because the chess program\r
9402        doesn't "have the same console" as WinBoard.  Maybe\r
9403        we could arrange for this even though neither WinBoard\r
9404        nor the chess program uses a console for stdio? */\r
9405     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9406 \r
9407     /* [AS] Special termination modes for misbehaving programs... */\r
9408     if( signal & 8 ) { \r
9409         result = TerminateProcess( cp->hProcess, 0 );\r
9410 \r
9411         if ( appData.debugMode) {\r
9412             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9413         }\r
9414     }\r
9415     else if( signal & 4 ) {\r
9416         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9417 \r
9418         if( dw != WAIT_OBJECT_0 ) {\r
9419             result = TerminateProcess( cp->hProcess, 0 );\r
9420 \r
9421             if ( appData.debugMode) {\r
9422                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9423             }\r
9424 \r
9425         }\r
9426     }\r
9427 \r
9428     CloseHandle(cp->hProcess);\r
9429     break;\r
9430 \r
9431   case CPComm:\r
9432     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9433     break;\r
9434 \r
9435   case CPSock:\r
9436     closesocket(cp->sock);\r
9437     WSACleanup();\r
9438     break;\r
9439 \r
9440   case CPRcmd:\r
9441     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9442     closesocket(cp->sock);\r
9443     closesocket(cp->sock2);\r
9444     WSACleanup();\r
9445     break;\r
9446   }\r
9447   free(cp);\r
9448 }\r
9449 \r
9450 void\r
9451 InterruptChildProcess(ProcRef pr)\r
9452 {\r
9453   ChildProc *cp;\r
9454 \r
9455   cp = (ChildProc *) pr;\r
9456   if (cp == NULL) return;\r
9457   switch (cp->kind) {\r
9458   case CPReal:\r
9459     /* The following doesn't work because the chess program\r
9460        doesn't "have the same console" as WinBoard.  Maybe\r
9461        we could arrange for this even though neither WinBoard\r
9462        nor the chess program uses a console for stdio */\r
9463     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9464     break;\r
9465 \r
9466   case CPComm:\r
9467   case CPSock:\r
9468     /* Can't interrupt */\r
9469     break;\r
9470 \r
9471   case CPRcmd:\r
9472     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9473     break;\r
9474   }\r
9475 }\r
9476 \r
9477 \r
9478 int\r
9479 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9480 {\r
9481   char cmdLine[MSG_SIZ];\r
9482 \r
9483   if (port[0] == NULLCHAR) {\r
9484     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9485   } else {\r
9486     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9487   }\r
9488   return StartChildProcess(cmdLine, "", pr);\r
9489 }\r
9490 \r
9491 \r
9492 /* Code to open TCP sockets */\r
9493 \r
9494 int\r
9495 OpenTCP(char *host, char *port, ProcRef *pr)\r
9496 {\r
9497   ChildProc *cp;\r
9498   int err;\r
9499   SOCKET s;\r
9500 \r
9501   struct sockaddr_in sa, mysa;\r
9502   struct hostent FAR *hp;\r
9503   unsigned short uport;\r
9504   WORD wVersionRequested;\r
9505   WSADATA wsaData;\r
9506 \r
9507   /* Initialize socket DLL */\r
9508   wVersionRequested = MAKEWORD(1, 1);\r
9509   err = WSAStartup(wVersionRequested, &wsaData);\r
9510   if (err != 0) return err;\r
9511 \r
9512   /* Make socket */\r
9513   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9514     err = WSAGetLastError();\r
9515     WSACleanup();\r
9516     return err;\r
9517   }\r
9518 \r
9519   /* Bind local address using (mostly) don't-care values.\r
9520    */\r
9521   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9522   mysa.sin_family = AF_INET;\r
9523   mysa.sin_addr.s_addr = INADDR_ANY;\r
9524   uport = (unsigned short) 0;\r
9525   mysa.sin_port = htons(uport);\r
9526   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9527       == SOCKET_ERROR) {\r
9528     err = WSAGetLastError();\r
9529     WSACleanup();\r
9530     return err;\r
9531   }\r
9532 \r
9533   /* Resolve remote host name */\r
9534   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9535   if (!(hp = gethostbyname(host))) {\r
9536     unsigned int b0, b1, b2, b3;\r
9537 \r
9538     err = WSAGetLastError();\r
9539 \r
9540     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9541       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9542       hp->h_addrtype = AF_INET;\r
9543       hp->h_length = 4;\r
9544       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9545       hp->h_addr_list[0] = (char *) malloc(4);\r
9546       hp->h_addr_list[0][0] = (char) b0;\r
9547       hp->h_addr_list[0][1] = (char) b1;\r
9548       hp->h_addr_list[0][2] = (char) b2;\r
9549       hp->h_addr_list[0][3] = (char) b3;\r
9550     } else {\r
9551       WSACleanup();\r
9552       return err;\r
9553     }\r
9554   }\r
9555   sa.sin_family = hp->h_addrtype;\r
9556   uport = (unsigned short) atoi(port);\r
9557   sa.sin_port = htons(uport);\r
9558   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9559 \r
9560   /* Make connection */\r
9561   if (connect(s, (struct sockaddr *) &sa,\r
9562               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9563     err = WSAGetLastError();\r
9564     WSACleanup();\r
9565     return err;\r
9566   }\r
9567 \r
9568   /* Prepare return value */\r
9569   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9570   cp->kind = CPSock;\r
9571   cp->sock = s;\r
9572   *pr = (ProcRef *) cp;\r
9573 \r
9574   return NO_ERROR;\r
9575 }\r
9576 \r
9577 int\r
9578 OpenCommPort(char *name, ProcRef *pr)\r
9579 {\r
9580   HANDLE h;\r
9581   COMMTIMEOUTS ct;\r
9582   ChildProc *cp;\r
9583   char fullname[MSG_SIZ];\r
9584 \r
9585   if (*name != '\\')\r
9586     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9587   else\r
9588     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9589 \r
9590   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9591                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9592   if (h == (HANDLE) -1) {\r
9593     return GetLastError();\r
9594   }\r
9595   hCommPort = h;\r
9596 \r
9597   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9598 \r
9599   /* Accumulate characters until a 100ms pause, then parse */\r
9600   ct.ReadIntervalTimeout = 100;\r
9601   ct.ReadTotalTimeoutMultiplier = 0;\r
9602   ct.ReadTotalTimeoutConstant = 0;\r
9603   ct.WriteTotalTimeoutMultiplier = 0;\r
9604   ct.WriteTotalTimeoutConstant = 0;\r
9605   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9606 \r
9607   /* Prepare return value */\r
9608   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9609   cp->kind = CPComm;\r
9610   cp->hFrom = h;\r
9611   cp->hTo = h;\r
9612   *pr = (ProcRef *) cp;\r
9613 \r
9614   return NO_ERROR;\r
9615 }\r
9616 \r
9617 int\r
9618 OpenLoopback(ProcRef *pr)\r
9619 {\r
9620   DisplayFatalError(_("Not implemented"), 0, 1);\r
9621   return NO_ERROR;\r
9622 }\r
9623 \r
9624 \r
9625 int\r
9626 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9627 {\r
9628   ChildProc *cp;\r
9629   int err;\r
9630   SOCKET s, s2, s3;\r
9631   struct sockaddr_in sa, mysa;\r
9632   struct hostent FAR *hp;\r
9633   unsigned short uport;\r
9634   WORD wVersionRequested;\r
9635   WSADATA wsaData;\r
9636   int fromPort;\r
9637   char stderrPortStr[MSG_SIZ];\r
9638 \r
9639   /* Initialize socket DLL */\r
9640   wVersionRequested = MAKEWORD(1, 1);\r
9641   err = WSAStartup(wVersionRequested, &wsaData);\r
9642   if (err != 0) return err;\r
9643 \r
9644   /* Resolve remote host name */\r
9645   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9646   if (!(hp = gethostbyname(host))) {\r
9647     unsigned int b0, b1, b2, b3;\r
9648 \r
9649     err = WSAGetLastError();\r
9650 \r
9651     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9652       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9653       hp->h_addrtype = AF_INET;\r
9654       hp->h_length = 4;\r
9655       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9656       hp->h_addr_list[0] = (char *) malloc(4);\r
9657       hp->h_addr_list[0][0] = (char) b0;\r
9658       hp->h_addr_list[0][1] = (char) b1;\r
9659       hp->h_addr_list[0][2] = (char) b2;\r
9660       hp->h_addr_list[0][3] = (char) b3;\r
9661     } else {\r
9662       WSACleanup();\r
9663       return err;\r
9664     }\r
9665   }\r
9666   sa.sin_family = hp->h_addrtype;\r
9667   uport = (unsigned short) 514;\r
9668   sa.sin_port = htons(uport);\r
9669   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9670 \r
9671   /* Bind local socket to unused "privileged" port address\r
9672    */\r
9673   s = INVALID_SOCKET;\r
9674   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9675   mysa.sin_family = AF_INET;\r
9676   mysa.sin_addr.s_addr = INADDR_ANY;\r
9677   for (fromPort = 1023;; fromPort--) {\r
9678     if (fromPort < 0) {\r
9679       WSACleanup();\r
9680       return WSAEADDRINUSE;\r
9681     }\r
9682     if (s == INVALID_SOCKET) {\r
9683       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9684         err = WSAGetLastError();\r
9685         WSACleanup();\r
9686         return err;\r
9687       }\r
9688     }\r
9689     uport = (unsigned short) fromPort;\r
9690     mysa.sin_port = htons(uport);\r
9691     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9692         == SOCKET_ERROR) {\r
9693       err = WSAGetLastError();\r
9694       if (err == WSAEADDRINUSE) continue;\r
9695       WSACleanup();\r
9696       return err;\r
9697     }\r
9698     if (connect(s, (struct sockaddr *) &sa,\r
9699       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9700       err = WSAGetLastError();\r
9701       if (err == WSAEADDRINUSE) {\r
9702         closesocket(s);\r
9703         s = -1;\r
9704         continue;\r
9705       }\r
9706       WSACleanup();\r
9707       return err;\r
9708     }\r
9709     break;\r
9710   }\r
9711 \r
9712   /* Bind stderr local socket to unused "privileged" port address\r
9713    */\r
9714   s2 = INVALID_SOCKET;\r
9715   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9716   mysa.sin_family = AF_INET;\r
9717   mysa.sin_addr.s_addr = INADDR_ANY;\r
9718   for (fromPort = 1023;; fromPort--) {\r
9719     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9720     if (fromPort < 0) {\r
9721       (void) closesocket(s);\r
9722       WSACleanup();\r
9723       return WSAEADDRINUSE;\r
9724     }\r
9725     if (s2 == INVALID_SOCKET) {\r
9726       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9727         err = WSAGetLastError();\r
9728         closesocket(s);\r
9729         WSACleanup();\r
9730         return err;\r
9731       }\r
9732     }\r
9733     uport = (unsigned short) fromPort;\r
9734     mysa.sin_port = htons(uport);\r
9735     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9736         == SOCKET_ERROR) {\r
9737       err = WSAGetLastError();\r
9738       if (err == WSAEADDRINUSE) continue;\r
9739       (void) closesocket(s);\r
9740       WSACleanup();\r
9741       return err;\r
9742     }\r
9743     if (listen(s2, 1) == SOCKET_ERROR) {\r
9744       err = WSAGetLastError();\r
9745       if (err == WSAEADDRINUSE) {\r
9746         closesocket(s2);\r
9747         s2 = INVALID_SOCKET;\r
9748         continue;\r
9749       }\r
9750       (void) closesocket(s);\r
9751       (void) closesocket(s2);\r
9752       WSACleanup();\r
9753       return err;\r
9754     }\r
9755     break;\r
9756   }\r
9757   prevStderrPort = fromPort; // remember port used\r
9758   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9759 \r
9760   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9761     err = WSAGetLastError();\r
9762     (void) closesocket(s);\r
9763     (void) closesocket(s2);\r
9764     WSACleanup();\r
9765     return err;\r
9766   }\r
9767 \r
9768   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9769     err = WSAGetLastError();\r
9770     (void) closesocket(s);\r
9771     (void) closesocket(s2);\r
9772     WSACleanup();\r
9773     return err;\r
9774   }\r
9775   if (*user == NULLCHAR) user = UserName();\r
9776   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9777     err = WSAGetLastError();\r
9778     (void) closesocket(s);\r
9779     (void) closesocket(s2);\r
9780     WSACleanup();\r
9781     return err;\r
9782   }\r
9783   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9784     err = WSAGetLastError();\r
9785     (void) closesocket(s);\r
9786     (void) closesocket(s2);\r
9787     WSACleanup();\r
9788     return err;\r
9789   }\r
9790 \r
9791   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9792     err = WSAGetLastError();\r
9793     (void) closesocket(s);\r
9794     (void) closesocket(s2);\r
9795     WSACleanup();\r
9796     return err;\r
9797   }\r
9798   (void) closesocket(s2);  /* Stop listening */\r
9799 \r
9800   /* Prepare return value */\r
9801   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9802   cp->kind = CPRcmd;\r
9803   cp->sock = s;\r
9804   cp->sock2 = s3;\r
9805   *pr = (ProcRef *) cp;\r
9806 \r
9807   return NO_ERROR;\r
9808 }\r
9809 \r
9810 \r
9811 InputSourceRef\r
9812 AddInputSource(ProcRef pr, int lineByLine,\r
9813                InputCallback func, VOIDSTAR closure)\r
9814 {\r
9815   InputSource *is, *is2 = NULL;\r
9816   ChildProc *cp = (ChildProc *) pr;\r
9817 \r
9818   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9819   is->lineByLine = lineByLine;\r
9820   is->func = func;\r
9821   is->closure = closure;\r
9822   is->second = NULL;\r
9823   is->next = is->buf;\r
9824   if (pr == NoProc) {\r
9825     is->kind = CPReal;\r
9826     consoleInputSource = is;\r
9827   } else {\r
9828     is->kind = cp->kind;\r
9829     /* \r
9830         [AS] Try to avoid a race condition if the thread is given control too early:\r
9831         we create all threads suspended so that the is->hThread variable can be\r
9832         safely assigned, then let the threads start with ResumeThread.\r
9833     */\r
9834     switch (cp->kind) {\r
9835     case CPReal:\r
9836       is->hFile = cp->hFrom;\r
9837       cp->hFrom = NULL; /* now owned by InputThread */\r
9838       is->hThread =\r
9839         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9840                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9841       break;\r
9842 \r
9843     case CPComm:\r
9844       is->hFile = cp->hFrom;\r
9845       cp->hFrom = NULL; /* now owned by InputThread */\r
9846       is->hThread =\r
9847         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9848                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9849       break;\r
9850 \r
9851     case CPSock:\r
9852       is->sock = cp->sock;\r
9853       is->hThread =\r
9854         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9855                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9856       break;\r
9857 \r
9858     case CPRcmd:\r
9859       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9860       *is2 = *is;\r
9861       is->sock = cp->sock;\r
9862       is->second = is2;\r
9863       is2->sock = cp->sock2;\r
9864       is2->second = is2;\r
9865       is->hThread =\r
9866         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9867                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9868       is2->hThread =\r
9869         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9870                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9871       break;\r
9872     }\r
9873 \r
9874     if( is->hThread != NULL ) {\r
9875         ResumeThread( is->hThread );\r
9876     }\r
9877 \r
9878     if( is2 != NULL && is2->hThread != NULL ) {\r
9879         ResumeThread( is2->hThread );\r
9880     }\r
9881   }\r
9882 \r
9883   return (InputSourceRef) is;\r
9884 }\r
9885 \r
9886 void\r
9887 RemoveInputSource(InputSourceRef isr)\r
9888 {\r
9889   InputSource *is;\r
9890 \r
9891   is = (InputSource *) isr;\r
9892   is->hThread = NULL;  /* tell thread to stop */\r
9893   CloseHandle(is->hThread);\r
9894   if (is->second != NULL) {\r
9895     is->second->hThread = NULL;\r
9896     CloseHandle(is->second->hThread);\r
9897   }\r
9898 }\r
9899 \r
9900 int no_wrap(char *message, int count)\r
9901 {\r
9902     ConsoleOutput(message, count, FALSE);\r
9903     return count;\r
9904 }\r
9905 \r
9906 int\r
9907 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9908 {\r
9909   DWORD dOutCount;\r
9910   int outCount = SOCKET_ERROR;\r
9911   ChildProc *cp = (ChildProc *) pr;\r
9912   static OVERLAPPED ovl;\r
9913 \r
9914   static int line = 0;\r
9915 \r
9916   if (pr == NoProc)\r
9917   {\r
9918     if (appData.noJoin || !appData.useInternalWrap)\r
9919       return no_wrap(message, count);\r
9920     else\r
9921     {\r
9922       int width = get_term_width();\r
9923       int len = wrap(NULL, message, count, width, &line);\r
9924       char *msg = malloc(len);\r
9925       int dbgchk;\r
9926 \r
9927       if (!msg)\r
9928         return no_wrap(message, count);\r
9929       else\r
9930       {\r
9931         dbgchk = wrap(msg, message, count, width, &line);\r
9932         if (dbgchk != len && appData.debugMode)\r
9933             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9934         ConsoleOutput(msg, len, FALSE);\r
9935         free(msg);\r
9936         return len;\r
9937       }\r
9938     }\r
9939   }\r
9940 \r
9941   if (ovl.hEvent == NULL) {\r
9942     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9943   }\r
9944   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9945 \r
9946   switch (cp->kind) {\r
9947   case CPSock:\r
9948   case CPRcmd:\r
9949     outCount = send(cp->sock, message, count, 0);\r
9950     if (outCount == SOCKET_ERROR) {\r
9951       *outError = WSAGetLastError();\r
9952     } else {\r
9953       *outError = NO_ERROR;\r
9954     }\r
9955     break;\r
9956 \r
9957   case CPReal:\r
9958     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9959                   &dOutCount, NULL)) {\r
9960       *outError = NO_ERROR;\r
9961       outCount = (int) dOutCount;\r
9962     } else {\r
9963       *outError = GetLastError();\r
9964     }\r
9965     break;\r
9966 \r
9967   case CPComm:\r
9968     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9969                             &dOutCount, &ovl);\r
9970     if (*outError == NO_ERROR) {\r
9971       outCount = (int) dOutCount;\r
9972     }\r
9973     break;\r
9974   }\r
9975   return outCount;\r
9976 }\r
9977 \r
9978 void\r
9979 DoSleep(int n)\r
9980 {\r
9981     if(n != 0) Sleep(n);\r
9982 }\r
9983 \r
9984 int\r
9985 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9986                        long msdelay)\r
9987 {\r
9988   /* Ignore delay, not implemented for WinBoard */\r
9989   return OutputToProcess(pr, message, count, outError);\r
9990 }\r
9991 \r
9992 \r
9993 void\r
9994 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9995                         char *buf, int count, int error)\r
9996 {\r
9997   DisplayFatalError(_("Not implemented"), 0, 1);\r
9998 }\r
9999 \r
10000 /* see wgamelist.c for Game List functions */\r
10001 /* see wedittags.c for Edit Tags functions */\r
10002 \r
10003 \r
10004 int\r
10005 ICSInitScript()\r
10006 {\r
10007   FILE *f;\r
10008   char buf[MSG_SIZ];\r
10009   char *dummy;\r
10010 \r
10011   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10012     f = fopen(buf, "r");\r
10013     if (f != NULL) {\r
10014       ProcessICSInitScript(f);\r
10015       fclose(f);\r
10016       return TRUE;\r
10017     }\r
10018   }\r
10019   return FALSE;\r
10020 }\r
10021 \r
10022 \r
10023 VOID\r
10024 StartAnalysisClock()\r
10025 {\r
10026   if (analysisTimerEvent) return;\r
10027   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10028                                         (UINT) 2000, NULL);\r
10029 }\r
10030 \r
10031 VOID\r
10032 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10033 {\r
10034   highlightInfo.sq[0].x = fromX;\r
10035   highlightInfo.sq[0].y = fromY;\r
10036   highlightInfo.sq[1].x = toX;\r
10037   highlightInfo.sq[1].y = toY;\r
10038 }\r
10039 \r
10040 VOID\r
10041 ClearHighlights()\r
10042 {\r
10043   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10044     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10045 }\r
10046 \r
10047 VOID\r
10048 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10049 {\r
10050   premoveHighlightInfo.sq[0].x = fromX;\r
10051   premoveHighlightInfo.sq[0].y = fromY;\r
10052   premoveHighlightInfo.sq[1].x = toX;\r
10053   premoveHighlightInfo.sq[1].y = toY;\r
10054 }\r
10055 \r
10056 VOID\r
10057 ClearPremoveHighlights()\r
10058 {\r
10059   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10060     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10061 }\r
10062 \r
10063 VOID\r
10064 ShutDownFrontEnd()\r
10065 {\r
10066   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10067   DeleteClipboardTempFiles();\r
10068 }\r
10069 \r
10070 void\r
10071 BoardToTop()\r
10072 {\r
10073     if (IsIconic(hwndMain))\r
10074       ShowWindow(hwndMain, SW_RESTORE);\r
10075 \r
10076     SetActiveWindow(hwndMain);\r
10077 }\r
10078 \r
10079 /*\r
10080  * Prototypes for animation support routines\r
10081  */\r
10082 static void ScreenSquare(int column, int row, POINT * pt);\r
10083 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10084      POINT frames[], int * nFrames);\r
10085 \r
10086 \r
10087 #define kFactor 4\r
10088 \r
10089 void\r
10090 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
10091 {       // [HGM] atomic: animate blast wave\r
10092         int i;\r
10093 \r
10094         explodeInfo.fromX = fromX;\r
10095         explodeInfo.fromY = fromY;\r
10096         explodeInfo.toX = toX;\r
10097         explodeInfo.toY = toY;\r
10098         for(i=1; i<4*kFactor; i++) {\r
10099             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
10100             DrawPosition(FALSE, board);\r
10101             Sleep(appData.animSpeed);\r
10102         }\r
10103         explodeInfo.radius = 0;\r
10104         DrawPosition(TRUE, board);\r
10105 }\r
10106 \r
10107 void\r
10108 AnimateMove(board, fromX, fromY, toX, toY)\r
10109      Board board;\r
10110      int fromX;\r
10111      int fromY;\r
10112      int toX;\r
10113      int toY;\r
10114 {\r
10115   ChessSquare piece, victim = EmptySquare, victim2 = EmptySquare;\r
10116   int x = toX, y = toY, x2 = kill2X;\r
10117   POINT start, finish, mid;\r
10118   POINT frames[kFactor * 2 + 1];\r
10119   int nFrames, n;\r
10120 \r
10121   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10122 \r
10123   if (!appData.animate) return;\r
10124   if (doingSizing) return;\r
10125   if (fromY < 0 || fromX < 0) return;\r
10126   piece = board[fromY][fromX];\r
10127   if (piece >= EmptySquare) return;\r
10128 \r
10129   if(x2 >= 0) toX = kill2X, toY = kill2Y,  victim = board[killY][killX], victim2 = board[kill2Y][kill2X]; else\r
10130   if(killX >= 0) toX = killX, toY = killY, victim = board[killY][killX]; // [HGM] lion: first to kill square\r
10131 \r
10132   animInfo.from.x = fromX;\r
10133   animInfo.from.y = fromY;\r
10134 \r
10135 again:\r
10136 \r
10137   ScreenSquare(fromX, fromY, &start);\r
10138   ScreenSquare(toX, toY, &finish);\r
10139 \r
10140   /* All moves except knight jumps move in straight line */\r
10141   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10142     mid.x = start.x + (finish.x - start.x) / 2;\r
10143     mid.y = start.y + (finish.y - start.y) / 2;\r
10144   } else {\r
10145     /* Knight: make straight movement then diagonal */\r
10146     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10147        mid.x = start.x + (finish.x - start.x) / 2;\r
10148        mid.y = start.y;\r
10149      } else {\r
10150        mid.x = start.x;\r
10151        mid.y = start.y + (finish.y - start.y) / 2;\r
10152      }\r
10153   }\r
10154   \r
10155   /* Don't use as many frames for very short moves */\r
10156   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10157     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10158   else\r
10159     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10160 \r
10161   animInfo.to.x = toX;\r
10162   animInfo.to.y = toY;\r
10163   animInfo.lastpos = start;\r
10164   animInfo.piece = piece;\r
10165   for (n = 0; n < nFrames; n++) {\r
10166     animInfo.pos = frames[n];\r
10167     DrawPosition(FALSE, board);\r
10168     animInfo.lastpos = animInfo.pos;\r
10169     Sleep(appData.animSpeed);\r
10170   }\r
10171   animInfo.pos = finish;\r
10172   DrawPosition(FALSE, board);\r
10173 \r
10174   if(toX == x2 && toY == kill2Y) {\r
10175     fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1;\r
10176     board[kill2Y][kill2X] = EmptySquare; goto again;\r
10177   } // second leg\r
10178   if(toX != x || toY != y) {\r
10179     fromX = toX; fromY = toY; toX = x; toY = y;\r
10180     board[killY][killX] = EmptySquare; goto again;\r
10181   } // second leg\r
10182 \r
10183 if(victim2 != EmptySquare) board[kill2Y][kill2X] = victim2;\r
10184 if(victim  != EmptySquare) board[killY][killX] = victim;\r
10185 \r
10186   animInfo.piece = EmptySquare;\r
10187   Explode(board, fromX, fromY, toX, toY);\r
10188 }\r
10189 \r
10190 /*      Convert board position to corner of screen rect and color       */\r
10191 \r
10192 static void\r
10193 ScreenSquare(column, row, pt)\r
10194      int column; int row; POINT * pt;\r
10195 {\r
10196   if (flipView) {\r
10197     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10198     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10199   } else {\r
10200     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10201     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10202   }\r
10203 }\r
10204 \r
10205 /*      Generate a series of frame coords from start->mid->finish.\r
10206         The movement rate doubles until the half way point is\r
10207         reached, then halves back down to the final destination,\r
10208         which gives a nice slow in/out effect. The algorithmn\r
10209         may seem to generate too many intermediates for short\r
10210         moves, but remember that the purpose is to attract the\r
10211         viewers attention to the piece about to be moved and\r
10212         then to where it ends up. Too few frames would be less\r
10213         noticeable.                                             */\r
10214 \r
10215 static void\r
10216 Tween(start, mid, finish, factor, frames, nFrames)\r
10217      POINT * start; POINT * mid;\r
10218      POINT * finish; int factor;\r
10219      POINT frames[]; int * nFrames;\r
10220 {\r
10221   int n, fraction = 1, count = 0;\r
10222 \r
10223   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10224   for (n = 0; n < factor; n++)\r
10225     fraction *= 2;\r
10226   for (n = 0; n < factor; n++) {\r
10227     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10228     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10229     count ++;\r
10230     fraction = fraction / 2;\r
10231   }\r
10232   \r
10233   /* Midpoint */\r
10234   frames[count] = *mid;\r
10235   count ++;\r
10236   \r
10237   /* Slow out, stepping 1/2, then 1/4, ... */\r
10238   fraction = 2;\r
10239   for (n = 0; n < factor; n++) {\r
10240     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10241     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10242     count ++;\r
10243     fraction = fraction * 2;\r
10244   }\r
10245   *nFrames = count;\r
10246 }\r
10247 \r
10248 void\r
10249 SettingsPopUp(ChessProgramState *cps)\r
10250 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10251       EngineOptionsPopup(savedHwnd, cps);\r
10252 }\r
10253 \r
10254 int flock(int fid, int code)\r
10255 {\r
10256     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10257     OVERLAPPED ov;\r
10258     ov.hEvent = NULL;\r
10259     ov.Offset = 0;\r
10260     ov.OffsetHigh = 0;\r
10261     switch(code) {\r
10262       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10263 \r
10264       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10265       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10266       default: return -1;\r
10267     }\r
10268     return 0;\r
10269 }\r
10270 \r
10271 char *\r
10272 Col2Text (int n)\r
10273 {\r
10274     static int i=0;\r
10275     static char col[8][20];\r
10276     COLORREF color = *(COLORREF *) colorVariable[n];\r
10277     i = i+1 & 7;\r
10278     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10279     return col[i];\r
10280 }\r
10281 \r
10282 void\r
10283 ActivateTheme (int new)\r
10284 {   // Redo initialization of features depending on options that can occur in themes\r
10285    InitTextures();\r
10286    if(new) InitDrawingColors();\r
10287    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10288    InitDrawingSizes(boardSize, 0);\r
10289    InvalidateRect(hwndMain, NULL, TRUE);\r
10290 }\r