293b643e9a5b3653501b39f1870bda687555d3e1
[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         comboLine = 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         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6460         ParseArgs(StringGet, &p);\r
6461         SwapEngines(singleList); // ... and then make it 'second'\r
6462 \r
6463         appData.noChessProgram = FALSE;\r
6464         appData.icsActive = FALSE;\r
6465       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6466         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6467         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6468         p = buf;\r
6469         ParseArgs(StringGet, &p);\r
6470         if (appData.zippyPlay) {\r
6471           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6472           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6473           p = buf;\r
6474           ParseArgs(StringGet, &p);\r
6475         }\r
6476       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6477         appData.noChessProgram = TRUE;\r
6478         appData.icsActive = FALSE;\r
6479       } else {\r
6480         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6481                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6482         return TRUE;\r
6483       }\r
6484       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6485         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6486         p = buf;\r
6487         ParseArgs(StringGet, &p);\r
6488       }\r
6489       EndDialog(hDlg, TRUE);\r
6490       return TRUE;\r
6491 \r
6492     case IDCANCEL:\r
6493       ExitEvent(0);\r
6494       return TRUE;\r
6495 \r
6496     case IDM_HELPCONTENTS:\r
6497       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6498         MessageBox (GetFocus(),\r
6499                     _("Unable to activate help"),\r
6500                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6501       }\r
6502       break;\r
6503 \r
6504     default:\r
6505       SetStartupDialogEnables(hDlg);\r
6506       break;\r
6507     }\r
6508     break;\r
6509   }\r
6510   return FALSE;\r
6511 }\r
6512 \r
6513 /*---------------------------------------------------------------------------*\\r
6514  *\r
6515  * About box dialog functions\r
6516  *\r
6517 \*---------------------------------------------------------------------------*/\r
6518 \r
6519 /* Process messages for "About" dialog box */\r
6520 LRESULT CALLBACK\r
6521 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6522 {\r
6523   switch (message) {\r
6524   case WM_INITDIALOG: /* message: initialize dialog box */\r
6525     /* Center the dialog over the application window */\r
6526     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6527     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6528     Translate(hDlg, ABOUTBOX);\r
6529     JAWS_COPYRIGHT\r
6530     return (TRUE);\r
6531 \r
6532   case WM_COMMAND: /* message: received a command */\r
6533     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6534         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6535       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6536       return (TRUE);\r
6537     }\r
6538     break;\r
6539   }\r
6540   return (FALSE);\r
6541 }\r
6542 \r
6543 /*---------------------------------------------------------------------------*\\r
6544  *\r
6545  * Comment Dialog functions\r
6546  *\r
6547 \*---------------------------------------------------------------------------*/\r
6548 \r
6549 LRESULT CALLBACK\r
6550 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6551 {\r
6552   static HANDLE hwndText = NULL;\r
6553   int len, newSizeX, newSizeY;\r
6554   static int sizeX, sizeY;\r
6555   char *str;\r
6556   RECT rect;\r
6557   MINMAXINFO *mmi;\r
6558 \r
6559   switch (message) {\r
6560   case WM_INITDIALOG: /* message: initialize dialog box */\r
6561     /* Initialize the dialog items */\r
6562     Translate(hDlg, DLG_EditComment);\r
6563     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6564     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6565     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6566     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6567     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6568     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6569     SetWindowText(hDlg, commentTitle);\r
6570     if (editComment) {\r
6571       SetFocus(hwndText);\r
6572     } else {\r
6573       SetFocus(GetDlgItem(hDlg, IDOK));\r
6574     }\r
6575     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6576                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6577                 MAKELPARAM(FALSE, 0));\r
6578     /* Size and position the dialog */\r
6579     if (!commentDialog) {\r
6580       commentDialog = hDlg;\r
6581       GetClientRect(hDlg, &rect);\r
6582       sizeX = rect.right;\r
6583       sizeY = rect.bottom;\r
6584       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6585           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6586         WINDOWPLACEMENT wp;\r
6587         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6588         wp.length = sizeof(WINDOWPLACEMENT);\r
6589         wp.flags = 0;\r
6590         wp.showCmd = SW_SHOW;\r
6591         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6592         wp.rcNormalPosition.left = wpComment.x;\r
6593         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6594         wp.rcNormalPosition.top = wpComment.y;\r
6595         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6596         SetWindowPlacement(hDlg, &wp);\r
6597 \r
6598         GetClientRect(hDlg, &rect);\r
6599         newSizeX = rect.right;\r
6600         newSizeY = rect.bottom;\r
6601         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6602                               newSizeX, newSizeY);\r
6603         sizeX = newSizeX;\r
6604         sizeY = newSizeY;\r
6605       }\r
6606     }\r
6607     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6608     return FALSE;\r
6609 \r
6610   case WM_COMMAND: /* message: received a command */\r
6611     switch (LOWORD(wParam)) {\r
6612     case IDOK:\r
6613       if (editComment) {\r
6614         char *p, *q;\r
6615         /* Read changed options from the dialog box */\r
6616         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6617         len = GetWindowTextLength(hwndText);\r
6618         str = (char *) malloc(len + 1);\r
6619         GetWindowText(hwndText, str, len + 1);\r
6620         p = q = str;\r
6621         while (*q) {\r
6622           if (*q == '\r')\r
6623             q++;\r
6624           else\r
6625             *p++ = *q++;\r
6626         }\r
6627         *p = NULLCHAR;\r
6628         ReplaceComment(commentIndex, str);\r
6629         free(str);\r
6630       }\r
6631       CommentPopDown();\r
6632       return TRUE;\r
6633 \r
6634     case IDCANCEL:\r
6635     case OPT_CancelComment:\r
6636       CommentPopDown();\r
6637       return TRUE;\r
6638 \r
6639     case OPT_ClearComment:\r
6640       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6641       break;\r
6642 \r
6643     case OPT_EditComment:\r
6644       EditCommentEvent();\r
6645       return TRUE;\r
6646 \r
6647     default:\r
6648       break;\r
6649     }\r
6650     break;\r
6651 \r
6652   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6653         if( wParam == OPT_CommentText ) {\r
6654             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6655 \r
6656             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6657                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6658                 POINTL pt;\r
6659                 LRESULT index;\r
6660 \r
6661                 pt.x = LOWORD( lpMF->lParam );\r
6662                 pt.y = HIWORD( lpMF->lParam );\r
6663 \r
6664                 if(lpMF->msg == WM_CHAR) {\r
6665                         CHARRANGE sel;\r
6666                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6667                         index = sel.cpMin;\r
6668                 } else\r
6669                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6670 \r
6671                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6672                 len = GetWindowTextLength(hwndText);\r
6673                 str = (char *) malloc(len + 1);\r
6674                 GetWindowText(hwndText, str, len + 1);\r
6675                 ReplaceComment(commentIndex, str);\r
6676                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6677                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6678                 free(str);\r
6679 \r
6680                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6681                 lpMF->msg = WM_USER;\r
6682 \r
6683                 return TRUE;\r
6684             }\r
6685         }\r
6686         break;\r
6687 \r
6688   case WM_SIZE:\r
6689     newSizeX = LOWORD(lParam);\r
6690     newSizeY = HIWORD(lParam);\r
6691     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6692     sizeX = newSizeX;\r
6693     sizeY = newSizeY;\r
6694     break;\r
6695 \r
6696   case WM_GETMINMAXINFO:\r
6697     /* Prevent resizing window too small */\r
6698     mmi = (MINMAXINFO *) lParam;\r
6699     mmi->ptMinTrackSize.x = 100;\r
6700     mmi->ptMinTrackSize.y = 100;\r
6701     break;\r
6702   }\r
6703   return FALSE;\r
6704 }\r
6705 \r
6706 VOID\r
6707 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6708 {\r
6709   FARPROC lpProc;\r
6710   char *p, *q;\r
6711 \r
6712   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6713 \r
6714   if (str == NULL) str = "";\r
6715   p = (char *) malloc(2 * strlen(str) + 2);\r
6716   q = p;\r
6717   while (*str) {\r
6718     if (*str == '\n') *q++ = '\r';\r
6719     *q++ = *str++;\r
6720   }\r
6721   *q = NULLCHAR;\r
6722   if (commentText != NULL) free(commentText);\r
6723 \r
6724   commentIndex = index;\r
6725   commentTitle = title;\r
6726   commentText = p;\r
6727   editComment = edit;\r
6728 \r
6729   if (commentDialog) {\r
6730     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6731     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6732   } else {\r
6733     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6734     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6735                  hwndMain, (DLGPROC)lpProc);\r
6736     FreeProcInstance(lpProc);\r
6737   }\r
6738   commentUp = TRUE;\r
6739 }\r
6740 \r
6741 \r
6742 /*---------------------------------------------------------------------------*\\r
6743  *\r
6744  * Type-in move dialog functions\r
6745  * \r
6746 \*---------------------------------------------------------------------------*/\r
6747 \r
6748 LRESULT CALLBACK\r
6749 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6750 {\r
6751   char move[MSG_SIZ];\r
6752   HWND hInput;\r
6753 \r
6754   switch (message) {\r
6755   case WM_INITDIALOG:\r
6756     move[0] = (char) lParam;\r
6757     move[1] = NULLCHAR;\r
6758     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6759     Translate(hDlg, DLG_TypeInMove);\r
6760     hInput = GetDlgItem(hDlg, OPT_Move);\r
6761     SetWindowText(hInput, move);\r
6762     SetFocus(hInput);\r
6763     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6764     return FALSE;\r
6765 \r
6766   case WM_COMMAND:\r
6767     switch (LOWORD(wParam)) {\r
6768     case IDOK:\r
6769 \r
6770       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6771       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6772       TypeInDoneEvent(move);\r
6773       EndDialog(hDlg, TRUE);\r
6774       return TRUE;\r
6775     case IDCANCEL:\r
6776       EndDialog(hDlg, FALSE);\r
6777       return TRUE;\r
6778     default:\r
6779       break;\r
6780     }\r
6781     break;\r
6782   }\r
6783   return FALSE;\r
6784 }\r
6785 \r
6786 VOID\r
6787 PopUpMoveDialog(char firstchar)\r
6788 {\r
6789     FARPROC lpProc;\r
6790 \r
6791       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6792       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6793         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6794       FreeProcInstance(lpProc);\r
6795 }\r
6796 \r
6797 /*---------------------------------------------------------------------------*\\r
6798  *\r
6799  * Type-in name dialog functions\r
6800  * \r
6801 \*---------------------------------------------------------------------------*/\r
6802 \r
6803 LRESULT CALLBACK\r
6804 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6805 {\r
6806   char move[MSG_SIZ];\r
6807   HWND hInput;\r
6808 \r
6809   switch (message) {\r
6810   case WM_INITDIALOG:\r
6811     move[0] = (char) lParam;\r
6812     move[1] = NULLCHAR;\r
6813     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6814     Translate(hDlg, DLG_TypeInName);\r
6815     hInput = GetDlgItem(hDlg, OPT_Name);\r
6816     SetWindowText(hInput, move);\r
6817     SetFocus(hInput);\r
6818     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6819     return FALSE;\r
6820 \r
6821   case WM_COMMAND:\r
6822     switch (LOWORD(wParam)) {\r
6823     case IDOK:\r
6824       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6825       appData.userName = strdup(move);\r
6826       SetUserLogo(); DisplayLogos();\r
6827       SetGameInfo();\r
6828       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6829         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6830         DisplayTitle(move);\r
6831       }\r
6832 \r
6833 \r
6834       EndDialog(hDlg, TRUE);\r
6835       return TRUE;\r
6836     case IDCANCEL:\r
6837       EndDialog(hDlg, FALSE);\r
6838       return TRUE;\r
6839     default:\r
6840       break;\r
6841     }\r
6842     break;\r
6843   }\r
6844   return FALSE;\r
6845 }\r
6846 \r
6847 VOID\r
6848 PopUpNameDialog(char firstchar)\r
6849 {\r
6850     FARPROC lpProc;\r
6851     \r
6852       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6853       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6854         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6855       FreeProcInstance(lpProc);\r
6856 }\r
6857 \r
6858 /*---------------------------------------------------------------------------*\\r
6859  *\r
6860  *  Error dialogs\r
6861  * \r
6862 \*---------------------------------------------------------------------------*/\r
6863 \r
6864 /* Nonmodal error box */\r
6865 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6866                              WPARAM wParam, LPARAM lParam);\r
6867 \r
6868 VOID\r
6869 ErrorPopUp(char *title, char *content)\r
6870 {\r
6871   FARPROC lpProc;\r
6872   char *p, *q;\r
6873   BOOLEAN modal = hwndMain == NULL;\r
6874 \r
6875   p = content;\r
6876   q = errorMessage;\r
6877   while (*p) {\r
6878     if (*p == '\n') {\r
6879       if (modal) {\r
6880         *q++ = ' ';\r
6881         p++;\r
6882       } else {\r
6883         *q++ = '\r';\r
6884         *q++ = *p++;\r
6885       }\r
6886     } else {\r
6887       *q++ = *p++;\r
6888     }\r
6889   }\r
6890   *q = NULLCHAR;\r
6891   strncpy(errorTitle, title, sizeof(errorTitle));\r
6892   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6893   \r
6894   if (modal) {\r
6895     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6896   } else {\r
6897     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6898     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6899                  hwndMain, (DLGPROC)lpProc);\r
6900     FreeProcInstance(lpProc);\r
6901   }\r
6902 }\r
6903 \r
6904 VOID\r
6905 ErrorPopDown()\r
6906 {\r
6907   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6908   if (errorDialog == NULL) return;\r
6909   DestroyWindow(errorDialog);\r
6910   errorDialog = NULL;\r
6911   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6912 }\r
6913 \r
6914 LRESULT CALLBACK\r
6915 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6916 {\r
6917   RECT rChild;\r
6918 \r
6919   switch (message) {\r
6920   case WM_INITDIALOG:\r
6921     GetWindowRect(hDlg, &rChild);\r
6922 \r
6923     /*\r
6924     SetWindowPos(hDlg, NULL, rChild.left,\r
6925       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6926       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6927     */\r
6928 \r
6929     /* \r
6930         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6931         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6932         and it doesn't work when you resize the dialog.\r
6933         For now, just give it a default position.\r
6934     */\r
6935     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6936     Translate(hDlg, DLG_Error);\r
6937 \r
6938     errorDialog = hDlg;\r
6939     SetWindowText(hDlg, errorTitle);\r
6940     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6941     return FALSE;\r
6942 \r
6943   case WM_COMMAND:\r
6944     switch (LOWORD(wParam)) {\r
6945     case IDOK:\r
6946     case IDCANCEL:\r
6947       if (errorDialog == hDlg) errorDialog = NULL;\r
6948       DestroyWindow(hDlg);\r
6949       return TRUE;\r
6950 \r
6951     default:\r
6952       break;\r
6953     }\r
6954     break;\r
6955   }\r
6956   return FALSE;\r
6957 }\r
6958 \r
6959 #ifdef GOTHIC\r
6960 HWND gothicDialog = NULL;\r
6961 \r
6962 LRESULT CALLBACK\r
6963 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6964 {\r
6965   RECT rChild;\r
6966   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6967 \r
6968   switch (message) {\r
6969   case WM_INITDIALOG:\r
6970     GetWindowRect(hDlg, &rChild);\r
6971 \r
6972     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6973                                                              SWP_NOZORDER);\r
6974 \r
6975     /* \r
6976         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6977         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6978         and it doesn't work when you resize the dialog.\r
6979         For now, just give it a default position.\r
6980     */\r
6981     gothicDialog = hDlg;\r
6982     SetWindowText(hDlg, errorTitle);\r
6983     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6984     return FALSE;\r
6985 \r
6986   case WM_COMMAND:\r
6987     switch (LOWORD(wParam)) {\r
6988     case IDOK:\r
6989     case IDCANCEL:\r
6990       if (errorDialog == hDlg) errorDialog = NULL;\r
6991       DestroyWindow(hDlg);\r
6992       return TRUE;\r
6993 \r
6994     default:\r
6995       break;\r
6996     }\r
6997     break;\r
6998   }\r
6999   return FALSE;\r
7000 }\r
7001 \r
7002 VOID\r
7003 GothicPopUp(char *title, VariantClass variant)\r
7004 {\r
7005   FARPROC lpProc;\r
7006   static char *lastTitle;\r
7007 \r
7008   strncpy(errorTitle, title, sizeof(errorTitle));\r
7009   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7010 \r
7011   if(lastTitle != title && gothicDialog != NULL) {\r
7012     DestroyWindow(gothicDialog);\r
7013     gothicDialog = NULL;\r
7014   }\r
7015   if(variant != VariantNormal && gothicDialog == NULL) {\r
7016     title = lastTitle;\r
7017     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7018     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7019                  hwndMain, (DLGPROC)lpProc);\r
7020     FreeProcInstance(lpProc);\r
7021   }\r
7022 }\r
7023 #endif\r
7024 \r
7025 /*---------------------------------------------------------------------------*\\r
7026  *\r
7027  *  Ics Interaction console functions\r
7028  *\r
7029 \*---------------------------------------------------------------------------*/\r
7030 \r
7031 #define HISTORY_SIZE 64\r
7032 static char *history[HISTORY_SIZE];\r
7033 int histIn = 0, histP = 0;\r
7034 \r
7035 \r
7036 VOID\r
7037 SaveInHistory(char *cmd)\r
7038 {\r
7039   if (history[histIn] != NULL) {\r
7040     free(history[histIn]);\r
7041     history[histIn] = NULL;\r
7042   }\r
7043   if (*cmd == NULLCHAR) return;\r
7044   history[histIn] = StrSave(cmd);\r
7045   histIn = (histIn + 1) % HISTORY_SIZE;\r
7046   if (history[histIn] != NULL) {\r
7047     free(history[histIn]);\r
7048 \r
7049     history[histIn] = NULL;\r
7050   }\r
7051   histP = histIn;\r
7052 }\r
7053 \r
7054 char *\r
7055 PrevInHistory(char *cmd)\r
7056 {\r
7057   int newhp;\r
7058   if (histP == histIn) {\r
7059     if (history[histIn] != NULL) free(history[histIn]);\r
7060     history[histIn] = StrSave(cmd);\r
7061   }\r
7062   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7063   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7064   histP = newhp;\r
7065   return history[histP];\r
7066 }\r
7067 \r
7068 char *\r
7069 NextInHistory()\r
7070 {\r
7071   if (histP == histIn) return NULL;\r
7072   histP = (histP + 1) % HISTORY_SIZE;\r
7073   return history[histP];   \r
7074 }\r
7075 \r
7076 HMENU\r
7077 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7078 {\r
7079   HMENU hmenu, h;\r
7080   int i = 0;\r
7081   hmenu = LoadMenu(hInst, "TextMenu");\r
7082   h = GetSubMenu(hmenu, 0);\r
7083   while (e->item) {\r
7084     if (strcmp(e->item, "-") == 0) {\r
7085       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7086     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
7087       int flags = MF_STRING, j = 0;\r
7088       if (e->item[0] == '|') {\r
7089         flags |= MF_MENUBARBREAK;\r
7090         j++;\r
7091       }\r
7092       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
7093       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
7094     }\r
7095     e++;\r
7096     i++;\r
7097   } \r
7098   return hmenu;\r
7099 }\r
7100 \r
7101 WNDPROC consoleTextWindowProc;\r
7102 \r
7103 void\r
7104 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7105 {\r
7106   char buf[MSG_SIZ], name[MSG_SIZ];\r
7107   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7108   CHARRANGE sel;\r
7109 \r
7110   if (!getname) {\r
7111     SetWindowText(hInput, command);\r
7112     if (immediate) {\r
7113       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7114     } else {\r
7115       sel.cpMin = 999999;\r
7116       sel.cpMax = 999999;\r
7117       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7118       SetFocus(hInput);\r
7119     }\r
7120     return;\r
7121   }    \r
7122   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7123   if (sel.cpMin == sel.cpMax) {\r
7124     /* Expand to surrounding word */\r
7125     TEXTRANGE tr;\r
7126     do {\r
7127       tr.chrg.cpMax = sel.cpMin;\r
7128       tr.chrg.cpMin = --sel.cpMin;\r
7129       if (sel.cpMin < 0) break;\r
7130       tr.lpstrText = name;\r
7131       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7132     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7133     sel.cpMin++;\r
7134 \r
7135     do {\r
7136       tr.chrg.cpMin = sel.cpMax;\r
7137       tr.chrg.cpMax = ++sel.cpMax;\r
7138       tr.lpstrText = name;\r
7139       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7140     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7141     sel.cpMax--;\r
7142 \r
7143     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7144       MessageBeep(MB_ICONEXCLAMATION);\r
7145       return;\r
7146     }\r
7147     tr.chrg = sel;\r
7148     tr.lpstrText = name;\r
7149     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7150   } else {\r
7151     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7152       MessageBeep(MB_ICONEXCLAMATION);\r
7153       return;\r
7154     }\r
7155     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7156   }\r
7157   if (immediate) {\r
7158     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7159     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7160     SetWindowText(hInput, buf);\r
7161     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7162   } else {\r
7163     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7164       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7165     SetWindowText(hInput, buf);\r
7166     sel.cpMin = 999999;\r
7167     sel.cpMax = 999999;\r
7168     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7169     SetFocus(hInput);\r
7170   }\r
7171 }\r
7172 \r
7173 LRESULT CALLBACK \r
7174 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7175 {\r
7176   HWND hInput;\r
7177   CHARRANGE sel;\r
7178 \r
7179   switch (message) {\r
7180   case WM_KEYDOWN:\r
7181     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7182     if(wParam=='R') return 0;\r
7183     switch (wParam) {\r
7184     case VK_PRIOR:\r
7185       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7186       return 0;\r
7187     case VK_NEXT:\r
7188       sel.cpMin = 999999;\r
7189       sel.cpMax = 999999;\r
7190       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7191       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7192       return 0;\r
7193     }\r
7194     break;\r
7195   case WM_CHAR:\r
7196    if(wParam != '\022') {\r
7197     if (wParam == '\t') {\r
7198       if (GetKeyState(VK_SHIFT) < 0) {\r
7199         /* shifted */\r
7200         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7201         if (buttonDesc[0].hwnd) {\r
7202           SetFocus(buttonDesc[0].hwnd);\r
7203         } else {\r
7204           SetFocus(hwndMain);\r
7205         }\r
7206       } else {\r
7207         /* unshifted */\r
7208         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7209       }\r
7210     } else {\r
7211       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7212       JAWS_DELETE( SetFocus(hInput); )\r
7213       SendMessage(hInput, message, wParam, lParam);\r
7214     }\r
7215     return 0;\r
7216    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7217    lParam = -1;\r
7218   case WM_RBUTTONDOWN:\r
7219     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7220       /* Move selection here if it was empty */\r
7221       POINT pt;\r
7222       pt.x = LOWORD(lParam);\r
7223       pt.y = HIWORD(lParam);\r
7224       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7225       if (sel.cpMin == sel.cpMax) {\r
7226         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7227         sel.cpMax = sel.cpMin;\r
7228         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7229       }\r
7230       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7231 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7232       POINT pt;\r
7233       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7234       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7235       if (sel.cpMin == sel.cpMax) {\r
7236         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7237         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7238       }\r
7239       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7240         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7241       }\r
7242       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7243       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7244       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7245       MenuPopup(hwnd, pt, hmenu, -1);\r
7246 }\r
7247     }\r
7248     return 0;\r
7249   case WM_RBUTTONUP:\r
7250     if (GetKeyState(VK_SHIFT) & ~1) {\r
7251       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7252         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7253     }\r
7254     return 0;\r
7255   case WM_PASTE:\r
7256     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7257     SetFocus(hInput);\r
7258     return SendMessage(hInput, message, wParam, lParam);\r
7259   case WM_MBUTTONDOWN:\r
7260     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7261   case WM_COMMAND:\r
7262     switch (LOWORD(wParam)) {\r
7263     case IDM_QuickPaste:\r
7264       {\r
7265         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7266         if (sel.cpMin == sel.cpMax) {\r
7267           MessageBeep(MB_ICONEXCLAMATION);\r
7268           return 0;\r
7269         }\r
7270         SendMessage(hwnd, WM_COPY, 0, 0);\r
7271         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7272         SendMessage(hInput, WM_PASTE, 0, 0);\r
7273         SetFocus(hInput);\r
7274         return 0;\r
7275       }\r
7276     case IDM_Cut:\r
7277       SendMessage(hwnd, WM_CUT, 0, 0);\r
7278       return 0;\r
7279     case IDM_Paste:\r
7280       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7281       return 0;\r
7282     case IDM_Copy:\r
7283       SendMessage(hwnd, WM_COPY, 0, 0);\r
7284       return 0;\r
7285     default:\r
7286       {\r
7287         int i = LOWORD(wParam) - IDM_CommandX;\r
7288         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7289             icsTextMenuEntry[i].command != NULL) {\r
7290           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7291                    icsTextMenuEntry[i].getname,\r
7292                    icsTextMenuEntry[i].immediate);\r
7293           return 0;\r
7294         }\r
7295       }\r
7296       break;\r
7297     }\r
7298     break;\r
7299   }\r
7300   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7301 }\r
7302 \r
7303 WNDPROC consoleInputWindowProc;\r
7304 \r
7305 LRESULT CALLBACK\r
7306 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7307 {\r
7308   char buf[MSG_SIZ];\r
7309   char *p;\r
7310   static BOOL sendNextChar = FALSE;\r
7311   static BOOL quoteNextChar = FALSE;\r
7312   InputSource *is = consoleInputSource;\r
7313   CHARFORMAT cf;\r
7314   CHARRANGE sel;\r
7315 \r
7316   switch (message) {\r
7317   case WM_CHAR:\r
7318     if (!appData.localLineEditing || sendNextChar) {\r
7319       is->buf[0] = (CHAR) wParam;\r
7320       is->count = 1;\r
7321       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7322       sendNextChar = FALSE;\r
7323       return 0;\r
7324     }\r
7325     if (quoteNextChar) {\r
7326       buf[0] = (char) wParam;\r
7327       buf[1] = NULLCHAR;\r
7328       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7329       quoteNextChar = FALSE;\r
7330       return 0;\r
7331     }\r
7332     switch (wParam) {\r
7333     case '\r':   /* Enter key */\r
7334       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7335       if (consoleEcho) SaveInHistory(is->buf);\r
7336       is->buf[is->count++] = '\n';\r
7337       is->buf[is->count] = NULLCHAR;\r
7338       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7339       if (consoleEcho) {\r
7340         ConsoleOutput(is->buf, is->count, TRUE);\r
7341       } else if (appData.localLineEditing) {\r
7342         ConsoleOutput("\n", 1, TRUE);\r
7343       }\r
7344       /* fall thru */\r
7345     case '\033': /* Escape key */\r
7346       SetWindowText(hwnd, "");\r
7347       cf.cbSize = sizeof(CHARFORMAT);\r
7348       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7349       if (consoleEcho) {\r
7350         cf.crTextColor = textAttribs[ColorNormal].color;\r
7351       } else {\r
7352         cf.crTextColor = COLOR_ECHOOFF;\r
7353       }\r
7354       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7355       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7356       return 0;\r
7357     case '\t':   /* Tab key */\r
7358       if (GetKeyState(VK_SHIFT) < 0) {\r
7359         /* shifted */\r
7360         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7361       } else {\r
7362         /* unshifted */\r
7363         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7364         if (buttonDesc[0].hwnd) {\r
7365           SetFocus(buttonDesc[0].hwnd);\r
7366         } else {\r
7367           SetFocus(hwndMain);\r
7368         }\r
7369       }\r
7370       return 0;\r
7371     case '\023': /* Ctrl+S */\r
7372       sendNextChar = TRUE;\r
7373       return 0;\r
7374     case '\021': /* Ctrl+Q */\r
7375       quoteNextChar = TRUE;\r
7376       return 0;\r
7377     JAWS_REPLAY\r
7378     default:\r
7379       break;\r
7380     }\r
7381     break;\r
7382   case WM_KEYDOWN:\r
7383     switch (wParam) {\r
7384     case VK_UP:\r
7385       GetWindowText(hwnd, buf, MSG_SIZ);\r
7386       p = PrevInHistory(buf);\r
7387       if (p != NULL) {\r
7388         SetWindowText(hwnd, p);\r
7389         sel.cpMin = 999999;\r
7390         sel.cpMax = 999999;\r
7391         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7392         return 0;\r
7393       }\r
7394       break;\r
7395     case VK_DOWN:\r
7396       p = NextInHistory();\r
7397       if (p != NULL) {\r
7398         SetWindowText(hwnd, p);\r
7399         sel.cpMin = 999999;\r
7400         sel.cpMax = 999999;\r
7401         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7402         return 0;\r
7403       }\r
7404       break;\r
7405     case VK_HOME:\r
7406     case VK_END:\r
7407       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7408       /* fall thru */\r
7409     case VK_PRIOR:\r
7410     case VK_NEXT:\r
7411       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7412       return 0;\r
7413     }\r
7414     break;\r
7415   case WM_MBUTTONDOWN:\r
7416     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7417       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7418     break;\r
7419   case WM_RBUTTONUP:\r
7420     if (GetKeyState(VK_SHIFT) & ~1) {\r
7421       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7422         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7423     } else {\r
7424       POINT pt;\r
7425       HMENU hmenu;\r
7426       hmenu = LoadMenu(hInst, "InputMenu");\r
7427       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7428       if (sel.cpMin == sel.cpMax) {\r
7429         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7430         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7431       }\r
7432       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7433         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7434       }\r
7435       pt.x = LOWORD(lParam);\r
7436       pt.y = HIWORD(lParam);\r
7437       MenuPopup(hwnd, pt, hmenu, -1);\r
7438     }\r
7439     return 0;\r
7440   case WM_COMMAND:\r
7441     switch (LOWORD(wParam)) { \r
7442     case IDM_Undo:\r
7443       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7444       return 0;\r
7445     case IDM_SelectAll:\r
7446       sel.cpMin = 0;\r
7447       sel.cpMax = -1; /*999999?*/\r
7448       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7449       return 0;\r
7450     case IDM_Cut:\r
7451       SendMessage(hwnd, WM_CUT, 0, 0);\r
7452       return 0;\r
7453     case IDM_Paste:\r
7454       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7455       return 0;\r
7456     case IDM_Copy:\r
7457       SendMessage(hwnd, WM_COPY, 0, 0);\r
7458       return 0;\r
7459     }\r
7460     break;\r
7461   }\r
7462   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7463 }\r
7464 \r
7465 #define CO_MAX  100000\r
7466 #define CO_TRIM   1000\r
7467 \r
7468 LRESULT CALLBACK\r
7469 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7470 {\r
7471   static SnapData sd;\r
7472   HWND hText, hInput;\r
7473   RECT rect;\r
7474   static int sizeX, sizeY;\r
7475   int newSizeX, newSizeY;\r
7476   MINMAXINFO *mmi;\r
7477   WORD wMask;\r
7478 \r
7479   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7480   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7481 \r
7482   switch (message) {\r
7483   case WM_NOTIFY:\r
7484     if (((NMHDR*)lParam)->code == EN_LINK)\r
7485     {\r
7486       ENLINK *pLink = (ENLINK*)lParam;\r
7487       if (pLink->msg == WM_LBUTTONUP)\r
7488       {\r
7489         TEXTRANGE tr;\r
7490 \r
7491         tr.chrg = pLink->chrg;\r
7492         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7493         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7494         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7495         free(tr.lpstrText);\r
7496       }\r
7497     }\r
7498     break;\r
7499   case WM_INITDIALOG: /* message: initialize dialog box */\r
7500     hwndConsole = hDlg;\r
7501     SetFocus(hInput);\r
7502     consoleTextWindowProc = (WNDPROC)\r
7503       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7504     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7505     consoleInputWindowProc = (WNDPROC)\r
7506       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7507     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7508     Colorize(ColorNormal, TRUE);\r
7509     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7510     ChangedConsoleFont();\r
7511     GetClientRect(hDlg, &rect);\r
7512     sizeX = rect.right;\r
7513     sizeY = rect.bottom;\r
7514     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7515         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7516       WINDOWPLACEMENT wp;\r
7517       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7518       wp.length = sizeof(WINDOWPLACEMENT);\r
7519       wp.flags = 0;\r
7520       wp.showCmd = SW_SHOW;\r
7521       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7522       wp.rcNormalPosition.left = wpConsole.x;\r
7523       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7524       wp.rcNormalPosition.top = wpConsole.y;\r
7525       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7526       SetWindowPlacement(hDlg, &wp);\r
7527     }\r
7528 \r
7529    // [HGM] Chessknight's change 2004-07-13\r
7530    else { /* Determine Defaults */\r
7531        WINDOWPLACEMENT wp;\r
7532        wpConsole.x = wpMain.width + 1;\r
7533        wpConsole.y = wpMain.y;\r
7534        wpConsole.width = screenWidth -  wpMain.width;\r
7535        wpConsole.height = wpMain.height;\r
7536        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7537        wp.length = sizeof(WINDOWPLACEMENT);\r
7538        wp.flags = 0;\r
7539        wp.showCmd = SW_SHOW;\r
7540        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7541        wp.rcNormalPosition.left = wpConsole.x;\r
7542        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7543        wp.rcNormalPosition.top = wpConsole.y;\r
7544        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7545        SetWindowPlacement(hDlg, &wp);\r
7546     }\r
7547 \r
7548    // Allow hText to highlight URLs and send notifications on them\r
7549    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7550    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7551    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7552    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7553 \r
7554     return FALSE;\r
7555 \r
7556   case WM_SETFOCUS:\r
7557     SetFocus(hInput);\r
7558     return 0;\r
7559 \r
7560   case WM_CLOSE:\r
7561     ExitEvent(0);\r
7562     /* not reached */\r
7563     break;\r
7564 \r
7565   case WM_SIZE:\r
7566     if (IsIconic(hDlg)) break;\r
7567     newSizeX = LOWORD(lParam);\r
7568     newSizeY = HIWORD(lParam);\r
7569     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7570       RECT rectText, rectInput;\r
7571       POINT pt;\r
7572       int newTextHeight, newTextWidth;\r
7573       GetWindowRect(hText, &rectText);\r
7574       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7575       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7576       if (newTextHeight < 0) {\r
7577         newSizeY += -newTextHeight;\r
7578         newTextHeight = 0;\r
7579       }\r
7580       SetWindowPos(hText, NULL, 0, 0,\r
7581         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7582       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7583       pt.x = rectInput.left;\r
7584       pt.y = rectInput.top + newSizeY - sizeY;\r
7585       ScreenToClient(hDlg, &pt);\r
7586       SetWindowPos(hInput, NULL, \r
7587         pt.x, pt.y, /* needs client coords */   \r
7588         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7589         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7590     }\r
7591     sizeX = newSizeX;\r
7592     sizeY = newSizeY;\r
7593     break;\r
7594 \r
7595   case WM_GETMINMAXINFO:\r
7596     /* Prevent resizing window too small */\r
7597     mmi = (MINMAXINFO *) lParam;\r
7598     mmi->ptMinTrackSize.x = 100;\r
7599     mmi->ptMinTrackSize.y = 100;\r
7600     break;\r
7601 \r
7602   /* [AS] Snapping */\r
7603   case WM_ENTERSIZEMOVE:\r
7604     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7605 \r
7606   case WM_SIZING:\r
7607     return OnSizing( &sd, hDlg, wParam, lParam );\r
7608 \r
7609   case WM_MOVING:\r
7610     return OnMoving( &sd, hDlg, wParam, lParam );\r
7611 \r
7612   case WM_EXITSIZEMOVE:\r
7613         UpdateICSWidth(hText);\r
7614     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7615   }\r
7616 \r
7617   return DefWindowProc(hDlg, message, wParam, lParam);\r
7618 }\r
7619 \r
7620 \r
7621 VOID\r
7622 ConsoleCreate()\r
7623 {\r
7624   HWND hCons;\r
7625   if (hwndConsole) return;\r
7626   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7627   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7628 }\r
7629 \r
7630 \r
7631 VOID\r
7632 ConsoleOutput(char* data, int length, int forceVisible)\r
7633 {\r
7634   HWND hText;\r
7635   int trim, exlen;\r
7636   char *p, *q;\r
7637   char buf[CO_MAX+1];\r
7638   POINT pEnd;\r
7639   RECT rect;\r
7640   static int delayLF = 0;\r
7641   CHARRANGE savesel, sel;\r
7642 \r
7643   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7644   p = data;\r
7645   q = buf;\r
7646   if (delayLF) {\r
7647     *q++ = '\r';\r
7648     *q++ = '\n';\r
7649     delayLF = 0;\r
7650   }\r
7651   while (length--) {\r
7652     if (*p == '\n') {\r
7653       if (*++p) {\r
7654         *q++ = '\r';\r
7655         *q++ = '\n';\r
7656       } else {\r
7657         delayLF = 1;\r
7658       }\r
7659     } else if (*p == '\007') {\r
7660        MyPlaySound(&sounds[(int)SoundBell]);\r
7661        p++;\r
7662     } else {\r
7663       *q++ = *p++;\r
7664     }\r
7665   }\r
7666   *q = NULLCHAR;\r
7667   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7668   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7669   /* Save current selection */\r
7670   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7671   exlen = GetWindowTextLength(hText);\r
7672   /* Find out whether current end of text is visible */\r
7673   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7674   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7675   /* Trim existing text if it's too long */\r
7676   if (exlen + (q - buf) > CO_MAX) {\r
7677     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7678     sel.cpMin = 0;\r
7679     sel.cpMax = trim;\r
7680     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7681     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7682     exlen -= trim;\r
7683     savesel.cpMin -= trim;\r
7684     savesel.cpMax -= trim;\r
7685     if (exlen < 0) exlen = 0;\r
7686     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7687     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7688   }\r
7689   /* Append the new text */\r
7690   sel.cpMin = exlen;\r
7691   sel.cpMax = exlen;\r
7692   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7693   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7694   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7695   if (forceVisible || exlen == 0 ||\r
7696       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7697        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7698     /* Scroll to make new end of text visible if old end of text\r
7699        was visible or new text is an echo of user typein */\r
7700     sel.cpMin = 9999999;\r
7701     sel.cpMax = 9999999;\r
7702     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7703     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7704     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7705     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7706   }\r
7707   if (savesel.cpMax == exlen || forceVisible) {\r
7708     /* Move insert point to new end of text if it was at the old\r
7709        end of text or if the new text is an echo of user typein */\r
7710     sel.cpMin = 9999999;\r
7711     sel.cpMax = 9999999;\r
7712     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7713   } else {\r
7714     /* Restore previous selection */\r
7715     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7716   }\r
7717   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7718 }\r
7719 \r
7720 /*---------*/\r
7721 \r
7722 \r
7723 void\r
7724 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7725 {\r
7726   char buf[100];\r
7727   char *str;\r
7728   COLORREF oldFg, oldBg;\r
7729   HFONT oldFont;\r
7730   RECT rect;\r
7731 \r
7732   if(copyNumber > 1)\r
7733     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7734 \r
7735   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7736   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7737   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7738 \r
7739   rect.left = x;\r
7740   rect.right = x + squareSize;\r
7741   rect.top  = y;\r
7742   rect.bottom = y + squareSize;\r
7743   str = buf;\r
7744 \r
7745   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7746                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7747              y, ETO_CLIPPED|ETO_OPAQUE,\r
7748              &rect, str, strlen(str), NULL);\r
7749 \r
7750   (void) SetTextColor(hdc, oldFg);\r
7751   (void) SetBkColor(hdc, oldBg);\r
7752   (void) SelectObject(hdc, oldFont);\r
7753 }\r
7754 \r
7755 void\r
7756 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7757               RECT *rect, char *color, char *flagFell)\r
7758 {\r
7759   char buf[100];\r
7760   char *str;\r
7761   COLORREF oldFg, oldBg;\r
7762   HFONT oldFont;\r
7763 \r
7764   if (twoBoards && partnerUp) return;\r
7765   if (appData.clockMode) {\r
7766     if (tinyLayout == 2)\r
7767       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7768     else\r
7769       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7770     str = buf;\r
7771   } else {\r
7772     str = color;\r
7773   }\r
7774 \r
7775   if (highlight) {\r
7776     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7777     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7778   } else {\r
7779     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7780     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7781   }\r
7782 \r
7783   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7784 \r
7785   JAWS_SILENCE\r
7786 \r
7787   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7788              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7789              rect, str, strlen(str), NULL);\r
7790   if(logoHeight > 0 && appData.clockMode) {\r
7791       RECT r;\r
7792       str += strlen(color)+2;\r
7793       r.top = rect->top + logoHeight/2;\r
7794       r.left = rect->left;\r
7795       r.right = rect->right;\r
7796       r.bottom = rect->bottom;\r
7797       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7798                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7799                  &r, str, strlen(str), NULL);\r
7800   }\r
7801   (void) SetTextColor(hdc, oldFg);\r
7802   (void) SetBkColor(hdc, oldBg);\r
7803   (void) SelectObject(hdc, oldFont);\r
7804 }\r
7805 \r
7806 \r
7807 int\r
7808 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7809            OVERLAPPED *ovl)\r
7810 {\r
7811   int ok, err;\r
7812 \r
7813   /* [AS]  */\r
7814   if( count <= 0 ) {\r
7815     if (appData.debugMode) {\r
7816       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7817     }\r
7818 \r
7819     return ERROR_INVALID_USER_BUFFER;\r
7820   }\r
7821 \r
7822   ResetEvent(ovl->hEvent);\r
7823   ovl->Offset = ovl->OffsetHigh = 0;\r
7824   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7825   if (ok) {\r
7826     err = NO_ERROR;\r
7827   } else {\r
7828     err = GetLastError();\r
7829     if (err == ERROR_IO_PENDING) {\r
7830       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7831       if (ok)\r
7832         err = NO_ERROR;\r
7833       else\r
7834         err = GetLastError();\r
7835     }\r
7836   }\r
7837   return err;\r
7838 }\r
7839 \r
7840 int\r
7841 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7842             OVERLAPPED *ovl)\r
7843 {\r
7844   int ok, err;\r
7845 \r
7846   ResetEvent(ovl->hEvent);\r
7847   ovl->Offset = ovl->OffsetHigh = 0;\r
7848   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7849   if (ok) {\r
7850     err = NO_ERROR;\r
7851   } else {\r
7852     err = GetLastError();\r
7853     if (err == ERROR_IO_PENDING) {\r
7854       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7855       if (ok)\r
7856         err = NO_ERROR;\r
7857       else\r
7858         err = GetLastError();\r
7859     }\r
7860 \r
7861   }\r
7862   return err;\r
7863 }\r
7864 \r
7865 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7866 void CheckForInputBufferFull( InputSource * is )\r
7867 {\r
7868     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7869         /* Look for end of line */\r
7870         char * p = is->buf;\r
7871         \r
7872         while( p < is->next && *p != '\n' ) {\r
7873             p++;\r
7874         }\r
7875 \r
7876         if( p >= is->next ) {\r
7877             if (appData.debugMode) {\r
7878                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7879             }\r
7880 \r
7881             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7882             is->count = (DWORD) -1;\r
7883             is->next = is->buf;\r
7884         }\r
7885     }\r
7886 }\r
7887 \r
7888 DWORD\r
7889 InputThread(LPVOID arg)\r
7890 {\r
7891   InputSource *is;\r
7892   OVERLAPPED ovl;\r
7893 \r
7894   is = (InputSource *) arg;\r
7895   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7896   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7897   while (is->hThread != NULL) {\r
7898     is->error = DoReadFile(is->hFile, is->next,\r
7899                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7900                            &is->count, &ovl);\r
7901     if (is->error == NO_ERROR) {\r
7902       is->next += is->count;\r
7903     } else {\r
7904       if (is->error == ERROR_BROKEN_PIPE) {\r
7905         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7906         is->count = 0;\r
7907       } else {\r
7908         is->count = (DWORD) -1;\r
7909         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7910         break; \r
7911       }\r
7912     }\r
7913 \r
7914     CheckForInputBufferFull( is );\r
7915 \r
7916     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7917 \r
7918     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7919 \r
7920     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7921   }\r
7922 \r
7923   CloseHandle(ovl.hEvent);\r
7924   CloseHandle(is->hFile);\r
7925 \r
7926   if (appData.debugMode) {\r
7927     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7928   }\r
7929 \r
7930   return 0;\r
7931 }\r
7932 \r
7933 \r
7934 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7935 DWORD\r
7936 NonOvlInputThread(LPVOID arg)\r
7937 {\r
7938   InputSource *is;\r
7939   char *p, *q;\r
7940   int i;\r
7941   char prev;\r
7942 \r
7943   is = (InputSource *) arg;\r
7944   while (is->hThread != NULL) {\r
7945     is->error = ReadFile(is->hFile, is->next,\r
7946                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7947                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7948     if (is->error == NO_ERROR) {\r
7949       /* Change CRLF to LF */\r
7950       if (is->next > is->buf) {\r
7951         p = is->next - 1;\r
7952         i = is->count + 1;\r
7953       } else {\r
7954         p = is->next;\r
7955         i = is->count;\r
7956       }\r
7957       q = p;\r
7958       prev = NULLCHAR;\r
7959       while (i > 0) {\r
7960         if (prev == '\r' && *p == '\n') {\r
7961           *(q-1) = '\n';\r
7962           is->count--;\r
7963         } else { \r
7964           *q++ = *p;\r
7965         }\r
7966         prev = *p++;\r
7967         i--;\r
7968       }\r
7969       *q = NULLCHAR;\r
7970       is->next = q;\r
7971     } else {\r
7972       if (is->error == ERROR_BROKEN_PIPE) {\r
7973         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7974         is->count = 0; \r
7975       } else {\r
7976         is->count = (DWORD) -1;\r
7977       }\r
7978     }\r
7979 \r
7980     CheckForInputBufferFull( is );\r
7981 \r
7982     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7983 \r
7984     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7985 \r
7986     if (is->count < 0) break;  /* Quit on error */\r
7987   }\r
7988   CloseHandle(is->hFile);\r
7989   return 0;\r
7990 }\r
7991 \r
7992 DWORD\r
7993 SocketInputThread(LPVOID arg)\r
7994 {\r
7995   InputSource *is;\r
7996 \r
7997   is = (InputSource *) arg;\r
7998   while (is->hThread != NULL) {\r
7999     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8000     if ((int)is->count == SOCKET_ERROR) {\r
8001       is->count = (DWORD) -1;\r
8002       is->error = WSAGetLastError();\r
8003     } else {\r
8004       is->error = NO_ERROR;\r
8005       is->next += is->count;\r
8006       if (is->count == 0 && is->second == is) {\r
8007         /* End of file on stderr; quit with no message */\r
8008         break;\r
8009       }\r
8010     }\r
8011     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8012 \r
8013     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8014 \r
8015     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8016   }\r
8017   return 0;\r
8018 }\r
8019 \r
8020 VOID\r
8021 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8022 {\r
8023   InputSource *is;\r
8024 \r
8025   is = (InputSource *) lParam;\r
8026   if (is->lineByLine) {\r
8027     /* Feed in lines one by one */\r
8028     char *p = is->buf;\r
8029     char *q = p;\r
8030     while (q < is->next) {\r
8031       if (*q++ == '\n') {\r
8032         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8033         p = q;\r
8034       }\r
8035     }\r
8036     \r
8037     /* Move any partial line to the start of the buffer */\r
8038     q = is->buf;\r
8039     while (p < is->next) {\r
8040       *q++ = *p++;\r
8041     }\r
8042     is->next = q;\r
8043 \r
8044     if (is->error != NO_ERROR || is->count == 0) {\r
8045       /* Notify backend of the error.  Note: If there was a partial\r
8046          line at the end, it is not flushed through. */\r
8047       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8048     }\r
8049   } else {\r
8050     /* Feed in the whole chunk of input at once */\r
8051     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8052     is->next = is->buf;\r
8053   }\r
8054 }\r
8055 \r
8056 /*---------------------------------------------------------------------------*\\r
8057  *\r
8058  *  Menu enables. Used when setting various modes.\r
8059  *\r
8060 \*---------------------------------------------------------------------------*/\r
8061 \r
8062 typedef struct {\r
8063   int item;\r
8064   int flags;\r
8065 } Enables;\r
8066 \r
8067 VOID\r
8068 GreyRevert(Boolean grey)\r
8069 { // [HGM] vari: for retracting variations in local mode\r
8070   HMENU hmenu = GetMenu(hwndMain);\r
8071   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8072   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8073 }\r
8074 \r
8075 VOID\r
8076 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8077 {\r
8078   while (enab->item > 0) {\r
8079     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8080     enab++;\r
8081   }\r
8082 }\r
8083 \r
8084 Enables gnuEnables[] = {\r
8085   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8086   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8087   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8088   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8089   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8090   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8091   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8092   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8093   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8094   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
8095   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8096   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8098 \r
8099 \r
8100   // Needed to switch from ncp to GNU mode on Engine Load\r
8101   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8102   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8103   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8104   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8105   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8106   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8107   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8108   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8109   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8110   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8111   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8112   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8113   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8114   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8115   { -1, -1 }\r
8116 };\r
8117 \r
8118 Enables icsEnables[] = {\r
8119   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8120   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8121   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8122   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8123   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8124   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8125   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8126   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8127   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8128   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8129   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8130   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8131   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8132   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8133   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8134   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8135   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8136   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8137   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8138   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8139   { -1, -1 }\r
8140 };\r
8141 \r
8142 #if ZIPPY\r
8143 Enables zippyEnables[] = {\r
8144   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8145   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8146   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8147   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8148   { -1, -1 }\r
8149 };\r
8150 #endif\r
8151 \r
8152 Enables ncpEnables[] = {\r
8153   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8154   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8155   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8156   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8157   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8158   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8159   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8160   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8161   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8162   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8163   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8164   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8165   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8166   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8167   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8168   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8169   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8170   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8171   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8172   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8173   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8174   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8175   { -1, -1 }\r
8176 };\r
8177 \r
8178 Enables trainingOnEnables[] = {\r
8179   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8180   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8181   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8182   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8183   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8184   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8185   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8186   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8187   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8188   { -1, -1 }\r
8189 };\r
8190 \r
8191 Enables trainingOffEnables[] = {\r
8192   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8193   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8194   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8195   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8196   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8197   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8198   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8199   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8200   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8201   { -1, -1 }\r
8202 };\r
8203 \r
8204 /* These modify either ncpEnables or gnuEnables */\r
8205 Enables cmailEnables[] = {\r
8206   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8207   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8208   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8209   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8210   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8211   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8212   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8213   { -1, -1 }\r
8214 };\r
8215 \r
8216 Enables machineThinkingEnables[] = {\r
8217   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8218   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8219   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8220   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8221   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8222   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8223   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8224   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8225   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8226   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8227   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8228   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8229   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8230 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8231   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8232   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8233   { -1, -1 }\r
8234 };\r
8235 \r
8236 Enables userThinkingEnables[] = {\r
8237   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8238   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8239   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8240   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8241   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8242   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8243   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8244   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8245   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8246   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8247   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8248   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8249   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8250 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8251   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8252   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8253   { -1, -1 }\r
8254 };\r
8255 \r
8256 /*---------------------------------------------------------------------------*\\r
8257  *\r
8258  *  Front-end interface functions exported by XBoard.\r
8259  *  Functions appear in same order as prototypes in frontend.h.\r
8260  * \r
8261 \*---------------------------------------------------------------------------*/\r
8262 VOID\r
8263 CheckMark(UINT item, int state)\r
8264 {\r
8265     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8266 }\r
8267 \r
8268 VOID\r
8269 ModeHighlight()\r
8270 {\r
8271   static UINT prevChecked = 0;\r
8272   static int prevPausing = 0;\r
8273   UINT nowChecked;\r
8274 \r
8275   if (pausing != prevPausing) {\r
8276     prevPausing = pausing;\r
8277     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8278                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8279     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8280   }\r
8281 \r
8282   switch (gameMode) {\r
8283   case BeginningOfGame:\r
8284     if (appData.icsActive)\r
8285       nowChecked = IDM_IcsClient;\r
8286     else if (appData.noChessProgram)\r
8287       nowChecked = IDM_EditGame;\r
8288     else\r
8289       nowChecked = IDM_MachineBlack;\r
8290     break;\r
8291   case MachinePlaysBlack:\r
8292     nowChecked = IDM_MachineBlack;\r
8293     break;\r
8294   case MachinePlaysWhite:\r
8295     nowChecked = IDM_MachineWhite;\r
8296     break;\r
8297   case TwoMachinesPlay:\r
8298     nowChecked = IDM_TwoMachines;\r
8299     break;\r
8300   case AnalyzeMode:\r
8301     nowChecked = IDM_AnalysisMode;\r
8302     break;\r
8303   case AnalyzeFile:\r
8304     nowChecked = IDM_AnalyzeFile;\r
8305     break;\r
8306   case EditGame:\r
8307     nowChecked = IDM_EditGame;\r
8308     break;\r
8309   case PlayFromGameFile:\r
8310     nowChecked = IDM_LoadGame;\r
8311     break;\r
8312   case EditPosition:\r
8313     nowChecked = IDM_EditPosition;\r
8314     break;\r
8315   case Training:\r
8316     nowChecked = IDM_Training;\r
8317     break;\r
8318   case IcsPlayingWhite:\r
8319   case IcsPlayingBlack:\r
8320   case IcsObserving:\r
8321   case IcsIdle:\r
8322     nowChecked = IDM_IcsClient;\r
8323     break;\r
8324   default:\r
8325   case EndOfGame:\r
8326     nowChecked = 0;\r
8327     break;\r
8328   }\r
8329   if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8330     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
8331   CheckMark(prevChecked, MF_UNCHECKED);\r
8332   CheckMark(nowChecked, MF_CHECKED);\r
8333   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8334 \r
8335   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8336     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8337                           MF_BYCOMMAND|MF_ENABLED);\r
8338   } else {\r
8339     (void) EnableMenuItem(GetMenu(hwndMain), \r
8340                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8341   }\r
8342 \r
8343   prevChecked = nowChecked;\r
8344 \r
8345   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8346   if (appData.icsActive) {\r
8347        if (appData.icsEngineAnalyze) {\r
8348                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8349        } else {\r
8350                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8351        }\r
8352   }\r
8353   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8354 }\r
8355 \r
8356 VOID\r
8357 SetICSMode()\r
8358 {\r
8359   HMENU hmenu = GetMenu(hwndMain);\r
8360   SetMenuEnables(hmenu, icsEnables);\r
8361   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8362     MF_BYCOMMAND|MF_ENABLED);\r
8363 #if ZIPPY\r
8364   if (appData.zippyPlay) {\r
8365     SetMenuEnables(hmenu, zippyEnables);\r
8366     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8367          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8368           MF_BYCOMMAND|MF_ENABLED);\r
8369   }\r
8370 #endif\r
8371 }\r
8372 \r
8373 VOID\r
8374 SetGNUMode()\r
8375 {\r
8376   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8377 }\r
8378 \r
8379 VOID\r
8380 SetNCPMode()\r
8381 {\r
8382   HMENU hmenu = GetMenu(hwndMain);\r
8383   SetMenuEnables(hmenu, ncpEnables);\r
8384     DrawMenuBar(hwndMain);\r
8385 }\r
8386 \r
8387 VOID\r
8388 SetCmailMode()\r
8389 {\r
8390   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8391 }\r
8392 \r
8393 VOID \r
8394 SetTrainingModeOn()\r
8395 {\r
8396   int i;\r
8397   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8398   for (i = 0; i < N_BUTTONS; i++) {\r
8399     if (buttonDesc[i].hwnd != NULL)\r
8400       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8401   }\r
8402   CommentPopDown();\r
8403 }\r
8404 \r
8405 VOID SetTrainingModeOff()\r
8406 {\r
8407   int i;\r
8408   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8409   for (i = 0; i < N_BUTTONS; i++) {\r
8410     if (buttonDesc[i].hwnd != NULL)\r
8411       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8412   }\r
8413 }\r
8414 \r
8415 \r
8416 VOID\r
8417 SetUserThinkingEnables()\r
8418 {\r
8419   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8420 }\r
8421 \r
8422 VOID\r
8423 SetMachineThinkingEnables()\r
8424 {\r
8425   HMENU hMenu = GetMenu(hwndMain);\r
8426   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8427 \r
8428   SetMenuEnables(hMenu, machineThinkingEnables);\r
8429 \r
8430   if (gameMode == MachinePlaysBlack) {\r
8431     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8432   } else if (gameMode == MachinePlaysWhite) {\r
8433     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8434   } else if (gameMode == TwoMachinesPlay) {\r
8435     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8436   }\r
8437 }\r
8438 \r
8439 \r
8440 VOID\r
8441 DisplayTitle(char *str)\r
8442 {\r
8443   char title[MSG_SIZ], *host;\r
8444   if (str[0] != NULLCHAR) {\r
8445     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8446   } else if (appData.icsActive) {\r
8447     if (appData.icsCommPort[0] != NULLCHAR)\r
8448       host = "ICS";\r
8449     else \r
8450       host = appData.icsHost;\r
8451       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8452   } else if (appData.noChessProgram) {\r
8453     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8454   } else {\r
8455     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8456     strcat(title, ": ");\r
8457     strcat(title, first.tidy);\r
8458   }\r
8459   SetWindowText(hwndMain, title);\r
8460 }\r
8461 \r
8462 \r
8463 VOID\r
8464 DisplayMessage(char *str1, char *str2)\r
8465 {\r
8466   HDC hdc;\r
8467   HFONT oldFont;\r
8468   int remain = MESSAGE_TEXT_MAX - 1;\r
8469   int len;\r
8470 \r
8471   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8472   messageText[0] = NULLCHAR;\r
8473   if (*str1) {\r
8474     len = strlen(str1);\r
8475     if (len > remain) len = remain;\r
8476     strncpy(messageText, str1, len);\r
8477     messageText[len] = NULLCHAR;\r
8478     remain -= len;\r
8479   }\r
8480   if (*str2 && remain >= 2) {\r
8481     if (*str1) {\r
8482       strcat(messageText, "  ");\r
8483       remain -= 2;\r
8484     }\r
8485     len = strlen(str2);\r
8486     if (len > remain) len = remain;\r
8487     strncat(messageText, str2, len);\r
8488   }\r
8489   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8490   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8491 \r
8492   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8493 \r
8494   SAYMACHINEMOVE();\r
8495 \r
8496   hdc = GetDC(hwndMain);\r
8497   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8498   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8499              &messageRect, messageText, strlen(messageText), NULL);\r
8500   (void) SelectObject(hdc, oldFont);\r
8501   (void) ReleaseDC(hwndMain, hdc);\r
8502 }\r
8503 \r
8504 VOID\r
8505 DisplayError(char *str, int error)\r
8506 {\r
8507   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8508   int len;\r
8509 \r
8510   if (error == 0) {\r
8511     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8512   } else {\r
8513     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8514                         NULL, error, LANG_NEUTRAL,\r
8515                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8516     if (len > 0) {\r
8517       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8518     } else {\r
8519       ErrorMap *em = errmap;\r
8520       while (em->err != 0 && em->err != error) em++;\r
8521       if (em->err != 0) {\r
8522         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8523       } else {\r
8524         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8525       }\r
8526     }\r
8527   }\r
8528   \r
8529   ErrorPopUp(_("Error"), buf);\r
8530 }\r
8531 \r
8532 \r
8533 VOID\r
8534 DisplayMoveError(char *str)\r
8535 {\r
8536   fromX = fromY = -1;\r
8537   ClearHighlights();\r
8538   DrawPosition(FALSE, NULL);\r
8539   if (appData.popupMoveErrors) {\r
8540     ErrorPopUp(_("Error"), str);\r
8541   } else {\r
8542     DisplayMessage(str, "");\r
8543     moveErrorMessageUp = TRUE;\r
8544   }\r
8545 }\r
8546 \r
8547 VOID\r
8548 DisplayFatalError(char *str, int error, int exitStatus)\r
8549 {\r
8550   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8551   int len;\r
8552   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8553 \r
8554   if (error != 0) {\r
8555     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8556                         NULL, error, LANG_NEUTRAL,\r
8557                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8558     if (len > 0) {\r
8559       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8560     } else {\r
8561       ErrorMap *em = errmap;\r
8562       while (em->err != 0 && em->err != error) em++;\r
8563       if (em->err != 0) {\r
8564         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8565       } else {\r
8566         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8567       }\r
8568     }\r
8569     str = buf;\r
8570   }\r
8571   if (appData.debugMode) {\r
8572     fprintf(debugFP, "%s: %s\n", label, str);\r
8573   }\r
8574   if (appData.popupExitMessage) {\r
8575     if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!\r
8576     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8577                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8578   }\r
8579   ExitEvent(exitStatus);\r
8580 }\r
8581 \r
8582 \r
8583 VOID\r
8584 DisplayInformation(char *str)\r
8585 {\r
8586   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8587 }\r
8588 \r
8589 char *\r
8590 Shorten (char *s)\r
8591 {\r
8592   return s;\r
8593 }\r
8594 \r
8595 VOID\r
8596 DisplayNote(char *str)\r
8597 {\r
8598   ErrorPopUp(_("Note"), str);\r
8599 }\r
8600 \r
8601 \r
8602 typedef struct {\r
8603   char *title, *question, *replyPrefix;\r
8604   ProcRef pr;\r
8605 } QuestionParams;\r
8606 \r
8607 LRESULT CALLBACK\r
8608 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8609 {\r
8610   static QuestionParams *qp;\r
8611   char reply[MSG_SIZ];\r
8612   int len, err;\r
8613 \r
8614   switch (message) {\r
8615   case WM_INITDIALOG:\r
8616     qp = (QuestionParams *) lParam;\r
8617     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8618     Translate(hDlg, DLG_Question);\r
8619     SetWindowText(hDlg, qp->title);\r
8620     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8621     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8622     return FALSE;\r
8623 \r
8624   case WM_COMMAND:\r
8625     switch (LOWORD(wParam)) {\r
8626     case IDOK:\r
8627       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8628       if (*reply) strcat(reply, " ");\r
8629       len = strlen(reply);\r
8630       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8631       strcat(reply, "\n");\r
8632       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8633       EndDialog(hDlg, TRUE);\r
8634       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8635       return TRUE;\r
8636     case IDCANCEL:\r
8637       EndDialog(hDlg, FALSE);\r
8638       return TRUE;\r
8639     default:\r
8640       break;\r
8641     }\r
8642     break;\r
8643   }\r
8644   return FALSE;\r
8645 }\r
8646 \r
8647 VOID\r
8648 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8649 {\r
8650     QuestionParams qp;\r
8651     FARPROC lpProc;\r
8652     \r
8653     qp.title = title;\r
8654     qp.question = question;\r
8655     qp.replyPrefix = replyPrefix;\r
8656     qp.pr = pr;\r
8657     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8658     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8659       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8660     FreeProcInstance(lpProc);\r
8661 }\r
8662 \r
8663 /* [AS] Pick FRC position */\r
8664 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8665 {\r
8666     static int * lpIndexFRC;\r
8667     BOOL index_is_ok;\r
8668     char buf[16];\r
8669 \r
8670     switch( message )\r
8671     {\r
8672     case WM_INITDIALOG:\r
8673         lpIndexFRC = (int *) lParam;\r
8674 \r
8675         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8676         Translate(hDlg, DLG_NewGameFRC);\r
8677 \r
8678         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8679         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8680         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8681         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8682 \r
8683         break;\r
8684 \r
8685     case WM_COMMAND:\r
8686         switch( LOWORD(wParam) ) {\r
8687         case IDOK:\r
8688             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8689             EndDialog( hDlg, 0 );\r
8690             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8691             return TRUE;\r
8692         case IDCANCEL:\r
8693             EndDialog( hDlg, 1 );   \r
8694             return TRUE;\r
8695         case IDC_NFG_Edit:\r
8696             if( HIWORD(wParam) == EN_CHANGE ) {\r
8697                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8698 \r
8699                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8700             }\r
8701             return TRUE;\r
8702         case IDC_NFG_Random:\r
8703           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8704             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8705             return TRUE;\r
8706         }\r
8707 \r
8708         break;\r
8709     }\r
8710 \r
8711     return FALSE;\r
8712 }\r
8713 \r
8714 int NewGameFRC()\r
8715 {\r
8716     int result;\r
8717     int index = appData.defaultFrcPosition;\r
8718     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8719 \r
8720     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8721 \r
8722     if( result == 0 ) {\r
8723         appData.defaultFrcPosition = index;\r
8724     }\r
8725 \r
8726     return result;\r
8727 }\r
8728 \r
8729 /* [AS] Game list options. Refactored by HGM */\r
8730 \r
8731 HWND gameListOptionsDialog;\r
8732 \r
8733 // low-level front-end: clear text edit / list widget\r
8734 void\r
8735 \r
8736 GLT_ClearList()\r
8737 {\r
8738     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8739 }\r
8740 \r
8741 // low-level front-end: clear text edit / list widget\r
8742 void\r
8743 GLT_DeSelectList()\r
8744 {\r
8745     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8746 }\r
8747 \r
8748 // low-level front-end: append line to text edit / list widget\r
8749 void\r
8750 GLT_AddToList( char *name )\r
8751 {\r
8752     if( name != 0 ) {\r
8753             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8754     }\r
8755 }\r
8756 \r
8757 // low-level front-end: get line from text edit / list widget\r
8758 Boolean\r
8759 GLT_GetFromList( int index, char *name )\r
8760 {\r
8761     if( name != 0 ) {\r
8762             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8763                 return TRUE;\r
8764     }\r
8765     return FALSE;\r
8766 }\r
8767 \r
8768 void GLT_MoveSelection( HWND hDlg, int delta )\r
8769 {\r
8770     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8771     int idx2 = idx1 + delta;\r
8772     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8773 \r
8774     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8775         char buf[128];\r
8776 \r
8777         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8778         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8779         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8780         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8781     }\r
8782 }\r
8783 \r
8784 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8785 {\r
8786     switch( message )\r
8787     {\r
8788     case WM_INITDIALOG:\r
8789         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8790         \r
8791         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8792         Translate(hDlg, DLG_GameListOptions);\r
8793 \r
8794         /* Initialize list */\r
8795         GLT_TagsToList( lpUserGLT );\r
8796 \r
8797         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8798 \r
8799         break;\r
8800 \r
8801     case WM_COMMAND:\r
8802         switch( LOWORD(wParam) ) {\r
8803         case IDOK:\r
8804             GLT_ParseList();\r
8805             EndDialog( hDlg, 0 );\r
8806             return TRUE;\r
8807         case IDCANCEL:\r
8808             EndDialog( hDlg, 1 );\r
8809             return TRUE;\r
8810 \r
8811         case IDC_GLT_Default:\r
8812             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8813             return TRUE;\r
8814 \r
8815         case IDC_GLT_Restore:\r
8816             GLT_TagsToList( appData.gameListTags );\r
8817             return TRUE;\r
8818 \r
8819         case IDC_GLT_Up:\r
8820             GLT_MoveSelection( hDlg, -1 );\r
8821             return TRUE;\r
8822 \r
8823         case IDC_GLT_Down:\r
8824             GLT_MoveSelection( hDlg, +1 );\r
8825             return TRUE;\r
8826         }\r
8827 \r
8828         break;\r
8829     }\r
8830 \r
8831     return FALSE;\r
8832 }\r
8833 \r
8834 int GameListOptions()\r
8835 {\r
8836     int result;\r
8837     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8838 \r
8839       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8840 \r
8841     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8842 \r
8843     if( result == 0 ) {\r
8844         char *oldTags = appData.gameListTags;\r
8845         /* [AS] Memory leak here! */\r
8846         appData.gameListTags = strdup( lpUserGLT ); \r
8847         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8848             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8849     }\r
8850 \r
8851     return result;\r
8852 }\r
8853 \r
8854 VOID\r
8855 DisplayIcsInteractionTitle(char *str)\r
8856 {\r
8857   char consoleTitle[MSG_SIZ];\r
8858 \r
8859     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8860     SetWindowText(hwndConsole, consoleTitle);\r
8861 \r
8862     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8863       char buf[MSG_SIZ], *p = buf, *q;\r
8864         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8865       do {\r
8866         q = strchr(p, ';');\r
8867         if(q) *q++ = 0;\r
8868         if(*p) ChatPopUp(p);\r
8869       } while(p=q);\r
8870     }\r
8871 \r
8872     SetActiveWindow(hwndMain);\r
8873 }\r
8874 \r
8875 void\r
8876 DrawPosition(int fullRedraw, Board board)\r
8877 {\r
8878   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8879 }\r
8880 \r
8881 void NotifyFrontendLogin()\r
8882 {\r
8883         if (hwndConsole)\r
8884                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8885 }\r
8886 \r
8887 VOID\r
8888 ResetFrontEnd()\r
8889 {\r
8890   fromX = fromY = -1;\r
8891   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8892     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8893     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8894     dragInfo.lastpos = dragInfo.pos;\r
8895     dragInfo.start.x = dragInfo.start.y = -1;\r
8896     dragInfo.from = dragInfo.start;\r
8897     ReleaseCapture();\r
8898     DrawPosition(TRUE, NULL);\r
8899   }\r
8900   TagsPopDown();\r
8901 }\r
8902 \r
8903 \r
8904 VOID\r
8905 CommentPopUp(char *title, char *str)\r
8906 {\r
8907   HWND hwnd = GetActiveWindow();\r
8908   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8909   SAY(str);\r
8910   SetActiveWindow(hwnd);\r
8911 }\r
8912 \r
8913 VOID\r
8914 CommentPopDown(void)\r
8915 {\r
8916   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8917   if (commentDialog) {\r
8918     ShowWindow(commentDialog, SW_HIDE);\r
8919   }\r
8920   commentUp = FALSE;\r
8921 }\r
8922 \r
8923 VOID\r
8924 EditCommentPopUp(int index, char *title, char *str)\r
8925 {\r
8926   EitherCommentPopUp(index, title, str, TRUE);\r
8927 }\r
8928 \r
8929 \r
8930 int\r
8931 Roar()\r
8932 {\r
8933   MyPlaySound(&sounds[(int)SoundRoar]);\r
8934   return 1;\r
8935 }\r
8936 \r
8937 VOID\r
8938 RingBell()\r
8939 {\r
8940   MyPlaySound(&sounds[(int)SoundMove]);\r
8941 }\r
8942 \r
8943 VOID PlayIcsWinSound()\r
8944 {\r
8945   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8946 }\r
8947 \r
8948 VOID PlayIcsLossSound()\r
8949 {\r
8950   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8951 }\r
8952 \r
8953 VOID PlayIcsDrawSound()\r
8954 {\r
8955   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8956 }\r
8957 \r
8958 VOID PlayIcsUnfinishedSound()\r
8959 {\r
8960   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8961 }\r
8962 \r
8963 VOID\r
8964 PlayAlarmSound()\r
8965 {\r
8966   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8967 }\r
8968 \r
8969 VOID\r
8970 PlayTellSound()\r
8971 {\r
8972   MyPlaySound(&textAttribs[ColorTell].sound);\r
8973 }\r
8974 \r
8975 \r
8976 VOID\r
8977 EchoOn()\r
8978 {\r
8979   HWND hInput;\r
8980   consoleEcho = TRUE;\r
8981   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8982   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8983   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8984 }\r
8985 \r
8986 \r
8987 VOID\r
8988 EchoOff()\r
8989 {\r
8990   CHARFORMAT cf;\r
8991   HWND hInput;\r
8992   consoleEcho = FALSE;\r
8993   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8994   /* This works OK: set text and background both to the same color */\r
8995   cf = consoleCF;\r
8996   cf.crTextColor = COLOR_ECHOOFF;\r
8997   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8998   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8999 }\r
9000 \r
9001 /* No Raw()...? */\r
9002 \r
9003 void Colorize(ColorClass cc, int continuation)\r
9004 {\r
9005   currentColorClass = cc;\r
9006   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9007   consoleCF.crTextColor = textAttribs[cc].color;\r
9008   consoleCF.dwEffects = textAttribs[cc].effects;\r
9009   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9010 }\r
9011 \r
9012 char *\r
9013 UserName()\r
9014 {\r
9015   static char buf[MSG_SIZ];\r
9016   DWORD bufsiz = MSG_SIZ;\r
9017 \r
9018   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9019         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9020   }\r
9021   if (!GetUserName(buf, &bufsiz)) {\r
9022     /*DisplayError("Error getting user name", GetLastError());*/\r
9023     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
9024   }\r
9025   return buf;\r
9026 }\r
9027 \r
9028 char *\r
9029 HostName()\r
9030 {\r
9031   static char buf[MSG_SIZ];\r
9032   DWORD bufsiz = MSG_SIZ;\r
9033 \r
9034   if (!GetComputerName(buf, &bufsiz)) {\r
9035     /*DisplayError("Error getting host name", GetLastError());*/\r
9036     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
9037   }\r
9038   return buf;\r
9039 }\r
9040 \r
9041 \r
9042 int\r
9043 ClockTimerRunning()\r
9044 {\r
9045   return clockTimerEvent != 0;\r
9046 }\r
9047 \r
9048 int\r
9049 StopClockTimer()\r
9050 {\r
9051   if (clockTimerEvent == 0) return FALSE;\r
9052   KillTimer(hwndMain, clockTimerEvent);\r
9053   clockTimerEvent = 0;\r
9054   return TRUE;\r
9055 }\r
9056 \r
9057 void\r
9058 StartClockTimer(long millisec)\r
9059 {\r
9060   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9061                              (UINT) millisec, NULL);\r
9062 }\r
9063 \r
9064 void\r
9065 DisplayWhiteClock(long timeRemaining, int highlight)\r
9066 {\r
9067   HDC hdc;\r
9068   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9069 \r
9070   if(appData.noGUI) return;\r
9071   hdc = GetDC(hwndMain);\r
9072   if (!IsIconic(hwndMain)) {\r
9073     DisplayAClock(hdc, timeRemaining, highlight, \r
9074                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
9075   }\r
9076   if (highlight && iconCurrent == iconBlack) {\r
9077     iconCurrent = iconWhite;\r
9078     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9079     if (IsIconic(hwndMain)) {\r
9080       DrawIcon(hdc, 2, 2, iconCurrent);\r
9081     }\r
9082   }\r
9083   (void) ReleaseDC(hwndMain, hdc);\r
9084   if (hwndConsole)\r
9085     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9086 }\r
9087 \r
9088 void\r
9089 DisplayBlackClock(long timeRemaining, int highlight)\r
9090 {\r
9091   HDC hdc;\r
9092   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9093 \r
9094 \r
9095   if(appData.noGUI) return;\r
9096   hdc = GetDC(hwndMain);\r
9097   if (!IsIconic(hwndMain)) {\r
9098     DisplayAClock(hdc, timeRemaining, highlight, \r
9099                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
9100   }\r
9101   if (highlight && iconCurrent == iconWhite) {\r
9102     iconCurrent = iconBlack;\r
9103     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9104     if (IsIconic(hwndMain)) {\r
9105       DrawIcon(hdc, 2, 2, iconCurrent);\r
9106     }\r
9107   }\r
9108   (void) ReleaseDC(hwndMain, hdc);\r
9109   if (hwndConsole)\r
9110     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9111 }\r
9112 \r
9113 \r
9114 int\r
9115 LoadGameTimerRunning()\r
9116 {\r
9117   return loadGameTimerEvent != 0;\r
9118 }\r
9119 \r
9120 int\r
9121 StopLoadGameTimer()\r
9122 {\r
9123   if (loadGameTimerEvent == 0) return FALSE;\r
9124   KillTimer(hwndMain, loadGameTimerEvent);\r
9125   loadGameTimerEvent = 0;\r
9126   return TRUE;\r
9127 }\r
9128 \r
9129 void\r
9130 StartLoadGameTimer(long millisec)\r
9131 {\r
9132   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9133                                 (UINT) millisec, NULL);\r
9134 }\r
9135 \r
9136 void\r
9137 AutoSaveGame()\r
9138 {\r
9139   char *defName;\r
9140   FILE *f;\r
9141   char fileTitle[MSG_SIZ];\r
9142 \r
9143   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9144   f = OpenFileDialog(hwndMain, "a", defName,\r
9145                      appData.oldSaveStyle ? "gam" : "pgn",\r
9146                      GAME_FILT, \r
9147                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9148   if (f != NULL) {\r
9149     SaveGame(f, 0, "");\r
9150     fclose(f);\r
9151   }\r
9152 }\r
9153 \r
9154 \r
9155 void\r
9156 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9157 {\r
9158   if (delayedTimerEvent != 0) {\r
9159     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9160       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9161     }\r
9162     KillTimer(hwndMain, delayedTimerEvent);\r
9163     delayedTimerEvent = 0;\r
9164     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9165     delayedTimerCallback();\r
9166   }\r
9167   delayedTimerCallback = cb;\r
9168   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9169                                 (UINT) millisec, NULL);\r
9170 }\r
9171 \r
9172 DelayedEventCallback\r
9173 GetDelayedEvent()\r
9174 {\r
9175   if (delayedTimerEvent) {\r
9176     return delayedTimerCallback;\r
9177   } else {\r
9178     return NULL;\r
9179   }\r
9180 }\r
9181 \r
9182 void\r
9183 CancelDelayedEvent()\r
9184 {\r
9185   if (delayedTimerEvent) {\r
9186     KillTimer(hwndMain, delayedTimerEvent);\r
9187     delayedTimerEvent = 0;\r
9188   }\r
9189 }\r
9190 \r
9191 DWORD GetWin32Priority(int nice)\r
9192 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9193 /*\r
9194 REALTIME_PRIORITY_CLASS     0x00000100\r
9195 HIGH_PRIORITY_CLASS         0x00000080\r
9196 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9197 NORMAL_PRIORITY_CLASS       0x00000020\r
9198 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9199 IDLE_PRIORITY_CLASS         0x00000040\r
9200 */\r
9201         if (nice < -15) return 0x00000080;\r
9202         if (nice < 0)   return 0x00008000;\r
9203 \r
9204         if (nice == 0)  return 0x00000020;\r
9205         if (nice < 15)  return 0x00004000;\r
9206         return 0x00000040;\r
9207 }\r
9208 \r
9209 void RunCommand(char *cmdLine)\r
9210 {\r
9211   /* Now create the child process. */\r
9212   STARTUPINFO siStartInfo;\r
9213   PROCESS_INFORMATION piProcInfo;\r
9214 \r
9215   siStartInfo.cb = sizeof(STARTUPINFO);\r
9216   siStartInfo.lpReserved = NULL;\r
9217   siStartInfo.lpDesktop = NULL;\r
9218   siStartInfo.lpTitle = NULL;\r
9219   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9220   siStartInfo.cbReserved2 = 0;\r
9221   siStartInfo.lpReserved2 = NULL;\r
9222   siStartInfo.hStdInput = NULL;\r
9223   siStartInfo.hStdOutput = NULL;\r
9224   siStartInfo.hStdError = NULL;\r
9225 \r
9226   CreateProcess(NULL,\r
9227                 cmdLine,           /* command line */\r
9228                 NULL,      /* process security attributes */\r
9229                 NULL,      /* primary thread security attrs */\r
9230                 TRUE,      /* handles are inherited */\r
9231                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9232                 NULL,      /* use parent's environment */\r
9233                 NULL,\r
9234                 &siStartInfo, /* STARTUPINFO pointer */\r
9235                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9236 \r
9237   CloseHandle(piProcInfo.hThread);\r
9238 }\r
9239 \r
9240 /* Start a child process running the given program.\r
9241    The process's standard output can be read from "from", and its\r
9242    standard input can be written to "to".\r
9243    Exit with fatal error if anything goes wrong.\r
9244    Returns an opaque pointer that can be used to destroy the process\r
9245    later.\r
9246 */\r
9247 int\r
9248 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9249 {\r
9250 #define BUFSIZE 4096\r
9251 \r
9252   HANDLE hChildStdinRd, hChildStdinWr,\r
9253     hChildStdoutRd, hChildStdoutWr;\r
9254   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9255   SECURITY_ATTRIBUTES saAttr;\r
9256   BOOL fSuccess;\r
9257   PROCESS_INFORMATION piProcInfo;\r
9258   STARTUPINFO siStartInfo;\r
9259   ChildProc *cp;\r
9260   char buf[MSG_SIZ];\r
9261   DWORD err;\r
9262 \r
9263   if (appData.debugMode) {\r
9264     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9265   }\r
9266 \r
9267   *pr = NoProc;\r
9268 \r
9269   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9270   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9271   saAttr.bInheritHandle = TRUE;\r
9272   saAttr.lpSecurityDescriptor = NULL;\r
9273 \r
9274   /*\r
9275    * The steps for redirecting child's STDOUT:\r
9276    *     1. Create anonymous pipe to be STDOUT for child.\r
9277    *     2. Create a noninheritable duplicate of read handle,\r
9278    *         and close the inheritable read handle.\r
9279    */\r
9280 \r
9281   /* Create a pipe for the child's STDOUT. */\r
9282   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9283     return GetLastError();\r
9284   }\r
9285 \r
9286   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9287   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9288                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9289                              FALSE,     /* not inherited */\r
9290                              DUPLICATE_SAME_ACCESS);\r
9291   if (! fSuccess) {\r
9292     return GetLastError();\r
9293   }\r
9294   CloseHandle(hChildStdoutRd);\r
9295 \r
9296   /*\r
9297    * The steps for redirecting child's STDIN:\r
9298    *     1. Create anonymous pipe to be STDIN for child.\r
9299    *     2. Create a noninheritable duplicate of write handle,\r
9300    *         and close the inheritable write handle.\r
9301    */\r
9302 \r
9303   /* Create a pipe for the child's STDIN. */\r
9304   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9305     return GetLastError();\r
9306   }\r
9307 \r
9308   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9309   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9310                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9311                              FALSE,     /* not inherited */\r
9312                              DUPLICATE_SAME_ACCESS);\r
9313   if (! fSuccess) {\r
9314     return GetLastError();\r
9315   }\r
9316   CloseHandle(hChildStdinWr);\r
9317 \r
9318   /* Arrange to (1) look in dir for the child .exe file, and\r
9319    * (2) have dir be the child's working directory.  Interpret\r
9320    * dir relative to the directory WinBoard loaded from. */\r
9321   GetCurrentDirectory(MSG_SIZ, buf);\r
9322   SetCurrentDirectory(installDir);\r
9323   SetCurrentDirectory(dir);\r
9324 \r
9325   /* Now create the child process. */\r
9326 \r
9327   siStartInfo.cb = sizeof(STARTUPINFO);\r
9328   siStartInfo.lpReserved = NULL;\r
9329   siStartInfo.lpDesktop = NULL;\r
9330   siStartInfo.lpTitle = NULL;\r
9331   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9332   siStartInfo.cbReserved2 = 0;\r
9333   siStartInfo.lpReserved2 = NULL;\r
9334   siStartInfo.hStdInput = hChildStdinRd;\r
9335   siStartInfo.hStdOutput = hChildStdoutWr;\r
9336   siStartInfo.hStdError = hChildStdoutWr;\r
9337 \r
9338   fSuccess = CreateProcess(NULL,\r
9339                            cmdLine,        /* command line */\r
9340                            NULL,           /* process security attributes */\r
9341                            NULL,           /* primary thread security attrs */\r
9342                            TRUE,           /* handles are inherited */\r
9343                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9344                            NULL,           /* use parent's environment */\r
9345                            NULL,\r
9346                            &siStartInfo, /* STARTUPINFO pointer */\r
9347                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9348 \r
9349   err = GetLastError();\r
9350   SetCurrentDirectory(buf); /* return to prev directory */\r
9351   if (! fSuccess) {\r
9352     return err;\r
9353   }\r
9354 \r
9355   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9356     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9357     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9358   }\r
9359 \r
9360   /* Close the handles we don't need in the parent */\r
9361   CloseHandle(piProcInfo.hThread);\r
9362   CloseHandle(hChildStdinRd);\r
9363   CloseHandle(hChildStdoutWr);\r
9364 \r
9365   /* Prepare return value */\r
9366   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9367   cp->kind = CPReal;\r
9368   cp->hProcess = piProcInfo.hProcess;\r
9369   cp->pid = piProcInfo.dwProcessId;\r
9370   cp->hFrom = hChildStdoutRdDup;\r
9371   cp->hTo = hChildStdinWrDup;\r
9372 \r
9373   *pr = (void *) cp;\r
9374 \r
9375   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9376      2000 where engines sometimes don't see the initial command(s)\r
9377      from WinBoard and hang.  I don't understand how that can happen,\r
9378      but the Sleep is harmless, so I've put it in.  Others have also\r
9379      reported what may be the same problem, so hopefully this will fix\r
9380      it for them too.  */\r
9381   Sleep(500);\r
9382 \r
9383   return NO_ERROR;\r
9384 }\r
9385 \r
9386 \r
9387 void\r
9388 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9389 {\r
9390   ChildProc *cp; int result;\r
9391 \r
9392   cp = (ChildProc *) pr;\r
9393   if (cp == NULL) return;\r
9394 \r
9395   switch (cp->kind) {\r
9396   case CPReal:\r
9397     /* TerminateProcess is considered harmful, so... */\r
9398     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9399     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9400     /* The following doesn't work because the chess program\r
9401        doesn't "have the same console" as WinBoard.  Maybe\r
9402        we could arrange for this even though neither WinBoard\r
9403        nor the chess program uses a console for stdio? */\r
9404     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9405 \r
9406     /* [AS] Special termination modes for misbehaving programs... */\r
9407     if( signal & 8 ) { \r
9408         result = TerminateProcess( cp->hProcess, 0 );\r
9409 \r
9410         if ( appData.debugMode) {\r
9411             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9412         }\r
9413     }\r
9414     else if( signal & 4 ) {\r
9415         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9416 \r
9417         if( dw != WAIT_OBJECT_0 ) {\r
9418             result = TerminateProcess( cp->hProcess, 0 );\r
9419 \r
9420             if ( appData.debugMode) {\r
9421                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9422             }\r
9423 \r
9424         }\r
9425     }\r
9426 \r
9427     CloseHandle(cp->hProcess);\r
9428     break;\r
9429 \r
9430   case CPComm:\r
9431     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9432     break;\r
9433 \r
9434   case CPSock:\r
9435     closesocket(cp->sock);\r
9436     WSACleanup();\r
9437     break;\r
9438 \r
9439   case CPRcmd:\r
9440     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9441     closesocket(cp->sock);\r
9442     closesocket(cp->sock2);\r
9443     WSACleanup();\r
9444     break;\r
9445   }\r
9446   free(cp);\r
9447 }\r
9448 \r
9449 void\r
9450 InterruptChildProcess(ProcRef pr)\r
9451 {\r
9452   ChildProc *cp;\r
9453 \r
9454   cp = (ChildProc *) pr;\r
9455   if (cp == NULL) return;\r
9456   switch (cp->kind) {\r
9457   case CPReal:\r
9458     /* The following doesn't work because the chess program\r
9459        doesn't "have the same console" as WinBoard.  Maybe\r
9460        we could arrange for this even though neither WinBoard\r
9461        nor the chess program uses a console for stdio */\r
9462     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9463     break;\r
9464 \r
9465   case CPComm:\r
9466   case CPSock:\r
9467     /* Can't interrupt */\r
9468     break;\r
9469 \r
9470   case CPRcmd:\r
9471     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9472     break;\r
9473   }\r
9474 }\r
9475 \r
9476 \r
9477 int\r
9478 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9479 {\r
9480   char cmdLine[MSG_SIZ];\r
9481 \r
9482   if (port[0] == NULLCHAR) {\r
9483     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9484   } else {\r
9485     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9486   }\r
9487   return StartChildProcess(cmdLine, "", pr);\r
9488 }\r
9489 \r
9490 \r
9491 /* Code to open TCP sockets */\r
9492 \r
9493 int\r
9494 OpenTCP(char *host, char *port, ProcRef *pr)\r
9495 {\r
9496   ChildProc *cp;\r
9497   int err;\r
9498   SOCKET s;\r
9499 \r
9500   struct sockaddr_in sa, mysa;\r
9501   struct hostent FAR *hp;\r
9502   unsigned short uport;\r
9503   WORD wVersionRequested;\r
9504   WSADATA wsaData;\r
9505 \r
9506   /* Initialize socket DLL */\r
9507   wVersionRequested = MAKEWORD(1, 1);\r
9508   err = WSAStartup(wVersionRequested, &wsaData);\r
9509   if (err != 0) return err;\r
9510 \r
9511   /* Make socket */\r
9512   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9513     err = WSAGetLastError();\r
9514     WSACleanup();\r
9515     return err;\r
9516   }\r
9517 \r
9518   /* Bind local address using (mostly) don't-care values.\r
9519    */\r
9520   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9521   mysa.sin_family = AF_INET;\r
9522   mysa.sin_addr.s_addr = INADDR_ANY;\r
9523   uport = (unsigned short) 0;\r
9524   mysa.sin_port = htons(uport);\r
9525   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9526       == SOCKET_ERROR) {\r
9527     err = WSAGetLastError();\r
9528     WSACleanup();\r
9529     return err;\r
9530   }\r
9531 \r
9532   /* Resolve remote host name */\r
9533   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9534   if (!(hp = gethostbyname(host))) {\r
9535     unsigned int b0, b1, b2, b3;\r
9536 \r
9537     err = WSAGetLastError();\r
9538 \r
9539     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9540       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9541       hp->h_addrtype = AF_INET;\r
9542       hp->h_length = 4;\r
9543       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9544       hp->h_addr_list[0] = (char *) malloc(4);\r
9545       hp->h_addr_list[0][0] = (char) b0;\r
9546       hp->h_addr_list[0][1] = (char) b1;\r
9547       hp->h_addr_list[0][2] = (char) b2;\r
9548       hp->h_addr_list[0][3] = (char) b3;\r
9549     } else {\r
9550       WSACleanup();\r
9551       return err;\r
9552     }\r
9553   }\r
9554   sa.sin_family = hp->h_addrtype;\r
9555   uport = (unsigned short) atoi(port);\r
9556   sa.sin_port = htons(uport);\r
9557   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9558 \r
9559   /* Make connection */\r
9560   if (connect(s, (struct sockaddr *) &sa,\r
9561               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9562     err = WSAGetLastError();\r
9563     WSACleanup();\r
9564     return err;\r
9565   }\r
9566 \r
9567   /* Prepare return value */\r
9568   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9569   cp->kind = CPSock;\r
9570   cp->sock = s;\r
9571   *pr = (ProcRef *) cp;\r
9572 \r
9573   return NO_ERROR;\r
9574 }\r
9575 \r
9576 int\r
9577 OpenCommPort(char *name, ProcRef *pr)\r
9578 {\r
9579   HANDLE h;\r
9580   COMMTIMEOUTS ct;\r
9581   ChildProc *cp;\r
9582   char fullname[MSG_SIZ];\r
9583 \r
9584   if (*name != '\\')\r
9585     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9586   else\r
9587     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9588 \r
9589   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9590                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9591   if (h == (HANDLE) -1) {\r
9592     return GetLastError();\r
9593   }\r
9594   hCommPort = h;\r
9595 \r
9596   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9597 \r
9598   /* Accumulate characters until a 100ms pause, then parse */\r
9599   ct.ReadIntervalTimeout = 100;\r
9600   ct.ReadTotalTimeoutMultiplier = 0;\r
9601   ct.ReadTotalTimeoutConstant = 0;\r
9602   ct.WriteTotalTimeoutMultiplier = 0;\r
9603   ct.WriteTotalTimeoutConstant = 0;\r
9604   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9605 \r
9606   /* Prepare return value */\r
9607   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9608   cp->kind = CPComm;\r
9609   cp->hFrom = h;\r
9610   cp->hTo = h;\r
9611   *pr = (ProcRef *) cp;\r
9612 \r
9613   return NO_ERROR;\r
9614 }\r
9615 \r
9616 int\r
9617 OpenLoopback(ProcRef *pr)\r
9618 {\r
9619   DisplayFatalError(_("Not implemented"), 0, 1);\r
9620   return NO_ERROR;\r
9621 }\r
9622 \r
9623 \r
9624 int\r
9625 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9626 {\r
9627   ChildProc *cp;\r
9628   int err;\r
9629   SOCKET s, s2, s3;\r
9630   struct sockaddr_in sa, mysa;\r
9631   struct hostent FAR *hp;\r
9632   unsigned short uport;\r
9633   WORD wVersionRequested;\r
9634   WSADATA wsaData;\r
9635   int fromPort;\r
9636   char stderrPortStr[MSG_SIZ];\r
9637 \r
9638   /* Initialize socket DLL */\r
9639   wVersionRequested = MAKEWORD(1, 1);\r
9640   err = WSAStartup(wVersionRequested, &wsaData);\r
9641   if (err != 0) return err;\r
9642 \r
9643   /* Resolve remote host name */\r
9644   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9645   if (!(hp = gethostbyname(host))) {\r
9646     unsigned int b0, b1, b2, b3;\r
9647 \r
9648     err = WSAGetLastError();\r
9649 \r
9650     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9651       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9652       hp->h_addrtype = AF_INET;\r
9653       hp->h_length = 4;\r
9654       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9655       hp->h_addr_list[0] = (char *) malloc(4);\r
9656       hp->h_addr_list[0][0] = (char) b0;\r
9657       hp->h_addr_list[0][1] = (char) b1;\r
9658       hp->h_addr_list[0][2] = (char) b2;\r
9659       hp->h_addr_list[0][3] = (char) b3;\r
9660     } else {\r
9661       WSACleanup();\r
9662       return err;\r
9663     }\r
9664   }\r
9665   sa.sin_family = hp->h_addrtype;\r
9666   uport = (unsigned short) 514;\r
9667   sa.sin_port = htons(uport);\r
9668   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9669 \r
9670   /* Bind local socket to unused "privileged" port address\r
9671    */\r
9672   s = INVALID_SOCKET;\r
9673   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9674   mysa.sin_family = AF_INET;\r
9675   mysa.sin_addr.s_addr = INADDR_ANY;\r
9676   for (fromPort = 1023;; fromPort--) {\r
9677     if (fromPort < 0) {\r
9678       WSACleanup();\r
9679       return WSAEADDRINUSE;\r
9680     }\r
9681     if (s == INVALID_SOCKET) {\r
9682       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9683         err = WSAGetLastError();\r
9684         WSACleanup();\r
9685         return err;\r
9686       }\r
9687     }\r
9688     uport = (unsigned short) fromPort;\r
9689     mysa.sin_port = htons(uport);\r
9690     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9691         == SOCKET_ERROR) {\r
9692       err = WSAGetLastError();\r
9693       if (err == WSAEADDRINUSE) continue;\r
9694       WSACleanup();\r
9695       return err;\r
9696     }\r
9697     if (connect(s, (struct sockaddr *) &sa,\r
9698       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9699       err = WSAGetLastError();\r
9700       if (err == WSAEADDRINUSE) {\r
9701         closesocket(s);\r
9702         s = -1;\r
9703         continue;\r
9704       }\r
9705       WSACleanup();\r
9706       return err;\r
9707     }\r
9708     break;\r
9709   }\r
9710 \r
9711   /* Bind stderr local socket to unused "privileged" port address\r
9712    */\r
9713   s2 = INVALID_SOCKET;\r
9714   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9715   mysa.sin_family = AF_INET;\r
9716   mysa.sin_addr.s_addr = INADDR_ANY;\r
9717   for (fromPort = 1023;; fromPort--) {\r
9718     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9719     if (fromPort < 0) {\r
9720       (void) closesocket(s);\r
9721       WSACleanup();\r
9722       return WSAEADDRINUSE;\r
9723     }\r
9724     if (s2 == INVALID_SOCKET) {\r
9725       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9726         err = WSAGetLastError();\r
9727         closesocket(s);\r
9728         WSACleanup();\r
9729         return err;\r
9730       }\r
9731     }\r
9732     uport = (unsigned short) fromPort;\r
9733     mysa.sin_port = htons(uport);\r
9734     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9735         == SOCKET_ERROR) {\r
9736       err = WSAGetLastError();\r
9737       if (err == WSAEADDRINUSE) continue;\r
9738       (void) closesocket(s);\r
9739       WSACleanup();\r
9740       return err;\r
9741     }\r
9742     if (listen(s2, 1) == SOCKET_ERROR) {\r
9743       err = WSAGetLastError();\r
9744       if (err == WSAEADDRINUSE) {\r
9745         closesocket(s2);\r
9746         s2 = INVALID_SOCKET;\r
9747         continue;\r
9748       }\r
9749       (void) closesocket(s);\r
9750       (void) closesocket(s2);\r
9751       WSACleanup();\r
9752       return err;\r
9753     }\r
9754     break;\r
9755   }\r
9756   prevStderrPort = fromPort; // remember port used\r
9757   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9758 \r
9759   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9760     err = WSAGetLastError();\r
9761     (void) closesocket(s);\r
9762     (void) closesocket(s2);\r
9763     WSACleanup();\r
9764     return err;\r
9765   }\r
9766 \r
9767   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9768     err = WSAGetLastError();\r
9769     (void) closesocket(s);\r
9770     (void) closesocket(s2);\r
9771     WSACleanup();\r
9772     return err;\r
9773   }\r
9774   if (*user == NULLCHAR) user = UserName();\r
9775   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9776     err = WSAGetLastError();\r
9777     (void) closesocket(s);\r
9778     (void) closesocket(s2);\r
9779     WSACleanup();\r
9780     return err;\r
9781   }\r
9782   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9783     err = WSAGetLastError();\r
9784     (void) closesocket(s);\r
9785     (void) closesocket(s2);\r
9786     WSACleanup();\r
9787     return err;\r
9788   }\r
9789 \r
9790   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9791     err = WSAGetLastError();\r
9792     (void) closesocket(s);\r
9793     (void) closesocket(s2);\r
9794     WSACleanup();\r
9795     return err;\r
9796   }\r
9797   (void) closesocket(s2);  /* Stop listening */\r
9798 \r
9799   /* Prepare return value */\r
9800   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9801   cp->kind = CPRcmd;\r
9802   cp->sock = s;\r
9803   cp->sock2 = s3;\r
9804   *pr = (ProcRef *) cp;\r
9805 \r
9806   return NO_ERROR;\r
9807 }\r
9808 \r
9809 \r
9810 InputSourceRef\r
9811 AddInputSource(ProcRef pr, int lineByLine,\r
9812                InputCallback func, VOIDSTAR closure)\r
9813 {\r
9814   InputSource *is, *is2 = NULL;\r
9815   ChildProc *cp = (ChildProc *) pr;\r
9816 \r
9817   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9818   is->lineByLine = lineByLine;\r
9819   is->func = func;\r
9820   is->closure = closure;\r
9821   is->second = NULL;\r
9822   is->next = is->buf;\r
9823   if (pr == NoProc) {\r
9824     is->kind = CPReal;\r
9825     consoleInputSource = is;\r
9826   } else {\r
9827     is->kind = cp->kind;\r
9828     /* \r
9829         [AS] Try to avoid a race condition if the thread is given control too early:\r
9830         we create all threads suspended so that the is->hThread variable can be\r
9831         safely assigned, then let the threads start with ResumeThread.\r
9832     */\r
9833     switch (cp->kind) {\r
9834     case CPReal:\r
9835       is->hFile = cp->hFrom;\r
9836       cp->hFrom = NULL; /* now owned by InputThread */\r
9837       is->hThread =\r
9838         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9839                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9840       break;\r
9841 \r
9842     case CPComm:\r
9843       is->hFile = cp->hFrom;\r
9844       cp->hFrom = NULL; /* now owned by InputThread */\r
9845       is->hThread =\r
9846         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9847                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9848       break;\r
9849 \r
9850     case CPSock:\r
9851       is->sock = cp->sock;\r
9852       is->hThread =\r
9853         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9854                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9855       break;\r
9856 \r
9857     case CPRcmd:\r
9858       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9859       *is2 = *is;\r
9860       is->sock = cp->sock;\r
9861       is->second = is2;\r
9862       is2->sock = cp->sock2;\r
9863       is2->second = is2;\r
9864       is->hThread =\r
9865         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9866                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9867       is2->hThread =\r
9868         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9869                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9870       break;\r
9871     }\r
9872 \r
9873     if( is->hThread != NULL ) {\r
9874         ResumeThread( is->hThread );\r
9875     }\r
9876 \r
9877     if( is2 != NULL && is2->hThread != NULL ) {\r
9878         ResumeThread( is2->hThread );\r
9879     }\r
9880   }\r
9881 \r
9882   return (InputSourceRef) is;\r
9883 }\r
9884 \r
9885 void\r
9886 RemoveInputSource(InputSourceRef isr)\r
9887 {\r
9888   InputSource *is;\r
9889 \r
9890   is = (InputSource *) isr;\r
9891   is->hThread = NULL;  /* tell thread to stop */\r
9892   CloseHandle(is->hThread);\r
9893   if (is->second != NULL) {\r
9894     is->second->hThread = NULL;\r
9895     CloseHandle(is->second->hThread);\r
9896   }\r
9897 }\r
9898 \r
9899 int no_wrap(char *message, int count)\r
9900 {\r
9901     ConsoleOutput(message, count, FALSE);\r
9902     return count;\r
9903 }\r
9904 \r
9905 int\r
9906 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9907 {\r
9908   DWORD dOutCount;\r
9909   int outCount = SOCKET_ERROR;\r
9910   ChildProc *cp = (ChildProc *) pr;\r
9911   static OVERLAPPED ovl;\r
9912 \r
9913   static int line = 0;\r
9914 \r
9915   if (pr == NoProc)\r
9916   {\r
9917     if (appData.noJoin || !appData.useInternalWrap)\r
9918       return no_wrap(message, count);\r
9919     else\r
9920     {\r
9921       int width = get_term_width();\r
9922       int len = wrap(NULL, message, count, width, &line);\r
9923       char *msg = malloc(len);\r
9924       int dbgchk;\r
9925 \r
9926       if (!msg)\r
9927         return no_wrap(message, count);\r
9928       else\r
9929       {\r
9930         dbgchk = wrap(msg, message, count, width, &line);\r
9931         if (dbgchk != len && appData.debugMode)\r
9932             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9933         ConsoleOutput(msg, len, FALSE);\r
9934         free(msg);\r
9935         return len;\r
9936       }\r
9937     }\r
9938   }\r
9939 \r
9940   if (ovl.hEvent == NULL) {\r
9941     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9942   }\r
9943   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9944 \r
9945   switch (cp->kind) {\r
9946   case CPSock:\r
9947   case CPRcmd:\r
9948     outCount = send(cp->sock, message, count, 0);\r
9949     if (outCount == SOCKET_ERROR) {\r
9950       *outError = WSAGetLastError();\r
9951     } else {\r
9952       *outError = NO_ERROR;\r
9953     }\r
9954     break;\r
9955 \r
9956   case CPReal:\r
9957     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9958                   &dOutCount, NULL)) {\r
9959       *outError = NO_ERROR;\r
9960       outCount = (int) dOutCount;\r
9961     } else {\r
9962       *outError = GetLastError();\r
9963     }\r
9964     break;\r
9965 \r
9966   case CPComm:\r
9967     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9968                             &dOutCount, &ovl);\r
9969     if (*outError == NO_ERROR) {\r
9970       outCount = (int) dOutCount;\r
9971     }\r
9972     break;\r
9973   }\r
9974   return outCount;\r
9975 }\r
9976 \r
9977 void\r
9978 DoSleep(int n)\r
9979 {\r
9980     if(n != 0) Sleep(n);\r
9981 }\r
9982 \r
9983 int\r
9984 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9985                        long msdelay)\r
9986 {\r
9987   /* Ignore delay, not implemented for WinBoard */\r
9988   return OutputToProcess(pr, message, count, outError);\r
9989 }\r
9990 \r
9991 \r
9992 void\r
9993 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9994                         char *buf, int count, int error)\r
9995 {\r
9996   DisplayFatalError(_("Not implemented"), 0, 1);\r
9997 }\r
9998 \r
9999 /* see wgamelist.c for Game List functions */\r
10000 /* see wedittags.c for Edit Tags functions */\r
10001 \r
10002 \r
10003 int\r
10004 ICSInitScript()\r
10005 {\r
10006   FILE *f;\r
10007   char buf[MSG_SIZ];\r
10008   char *dummy;\r
10009 \r
10010   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10011     f = fopen(buf, "r");\r
10012     if (f != NULL) {\r
10013       ProcessICSInitScript(f);\r
10014       fclose(f);\r
10015       return TRUE;\r
10016     }\r
10017   }\r
10018   return FALSE;\r
10019 }\r
10020 \r
10021 \r
10022 VOID\r
10023 StartAnalysisClock()\r
10024 {\r
10025   if (analysisTimerEvent) return;\r
10026   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10027                                         (UINT) 2000, NULL);\r
10028 }\r
10029 \r
10030 VOID\r
10031 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10032 {\r
10033   highlightInfo.sq[0].x = fromX;\r
10034   highlightInfo.sq[0].y = fromY;\r
10035   highlightInfo.sq[1].x = toX;\r
10036   highlightInfo.sq[1].y = toY;\r
10037 }\r
10038 \r
10039 VOID\r
10040 ClearHighlights()\r
10041 {\r
10042   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10043     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10044 }\r
10045 \r
10046 VOID\r
10047 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10048 {\r
10049   premoveHighlightInfo.sq[0].x = fromX;\r
10050   premoveHighlightInfo.sq[0].y = fromY;\r
10051   premoveHighlightInfo.sq[1].x = toX;\r
10052   premoveHighlightInfo.sq[1].y = toY;\r
10053 }\r
10054 \r
10055 VOID\r
10056 ClearPremoveHighlights()\r
10057 {\r
10058   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10059     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10060 }\r
10061 \r
10062 VOID\r
10063 ShutDownFrontEnd()\r
10064 {\r
10065   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10066   DeleteClipboardTempFiles();\r
10067 }\r
10068 \r
10069 void\r
10070 BoardToTop()\r
10071 {\r
10072     if (IsIconic(hwndMain))\r
10073       ShowWindow(hwndMain, SW_RESTORE);\r
10074 \r
10075     SetActiveWindow(hwndMain);\r
10076 }\r
10077 \r
10078 /*\r
10079  * Prototypes for animation support routines\r
10080  */\r
10081 static void ScreenSquare(int column, int row, POINT * pt);\r
10082 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10083      POINT frames[], int * nFrames);\r
10084 \r
10085 \r
10086 #define kFactor 4\r
10087 \r
10088 void\r
10089 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
10090 {       // [HGM] atomic: animate blast wave\r
10091         int i;\r
10092 \r
10093         explodeInfo.fromX = fromX;\r
10094         explodeInfo.fromY = fromY;\r
10095         explodeInfo.toX = toX;\r
10096         explodeInfo.toY = toY;\r
10097         for(i=1; i<4*kFactor; i++) {\r
10098             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
10099             DrawPosition(FALSE, board);\r
10100             Sleep(appData.animSpeed);\r
10101         }\r
10102         explodeInfo.radius = 0;\r
10103         DrawPosition(TRUE, board);\r
10104 }\r
10105 \r
10106 void\r
10107 AnimateMove(board, fromX, fromY, toX, toY)\r
10108      Board board;\r
10109      int fromX;\r
10110      int fromY;\r
10111      int toX;\r
10112      int toY;\r
10113 {\r
10114   ChessSquare piece, victim = EmptySquare, victim2 = EmptySquare;\r
10115   int x = toX, y = toY, x2 = kill2X;\r
10116   POINT start, finish, mid;\r
10117   POINT frames[kFactor * 2 + 1];\r
10118   int nFrames, n;\r
10119 \r
10120   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10121 \r
10122   if (!appData.animate) return;\r
10123   if (doingSizing) return;\r
10124   if (fromY < 0 || fromX < 0) return;\r
10125   piece = board[fromY][fromX];\r
10126   if (piece >= EmptySquare) return;\r
10127 \r
10128   if(x2 >= 0) toX = kill2X, toY = kill2Y,  victim = board[killY][killX], victim2 = board[kill2Y][kill2X]; else\r
10129   if(killX >= 0) toX = killX, toY = killY, victim = board[killY][killX]; // [HGM] lion: first to kill square\r
10130 \r
10131   animInfo.from.x = fromX;\r
10132   animInfo.from.y = fromY;\r
10133 \r
10134 again:\r
10135 \r
10136   ScreenSquare(fromX, fromY, &start);\r
10137   ScreenSquare(toX, toY, &finish);\r
10138 \r
10139   /* All moves except knight jumps move in straight line */\r
10140   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10141     mid.x = start.x + (finish.x - start.x) / 2;\r
10142     mid.y = start.y + (finish.y - start.y) / 2;\r
10143   } else {\r
10144     /* Knight: make straight movement then diagonal */\r
10145     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10146        mid.x = start.x + (finish.x - start.x) / 2;\r
10147        mid.y = start.y;\r
10148      } else {\r
10149        mid.x = start.x;\r
10150        mid.y = start.y + (finish.y - start.y) / 2;\r
10151      }\r
10152   }\r
10153   \r
10154   /* Don't use as many frames for very short moves */\r
10155   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10156     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10157   else\r
10158     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10159 \r
10160   animInfo.to.x = toX;\r
10161   animInfo.to.y = toY;\r
10162   animInfo.lastpos = start;\r
10163   animInfo.piece = piece;\r
10164   for (n = 0; n < nFrames; n++) {\r
10165     animInfo.pos = frames[n];\r
10166     DrawPosition(FALSE, board);\r
10167     animInfo.lastpos = animInfo.pos;\r
10168     Sleep(appData.animSpeed);\r
10169   }\r
10170   animInfo.pos = finish;\r
10171   DrawPosition(FALSE, board);\r
10172 \r
10173   if(toX == x2 && toY == kill2Y) {\r
10174     fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1;\r
10175     board[kill2Y][kill2X] = EmptySquare; goto again;\r
10176   } // second leg\r
10177   if(toX != x || toY != y) {\r
10178     fromX = toX; fromY = toY; toX = x; toY = y;\r
10179     board[killY][killX] = EmptySquare; goto again;\r
10180   } // second leg\r
10181 \r
10182 if(victim2 != EmptySquare) board[kill2Y][kill2X] = victim2;\r
10183 if(victim  != EmptySquare) board[killY][killX] = victim;\r
10184 \r
10185   animInfo.piece = EmptySquare;\r
10186   Explode(board, fromX, fromY, toX, toY);\r
10187 }\r
10188 \r
10189 /*      Convert board position to corner of screen rect and color       */\r
10190 \r
10191 static void\r
10192 ScreenSquare(column, row, pt)\r
10193      int column; int row; POINT * pt;\r
10194 {\r
10195   if (flipView) {\r
10196     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10197     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10198   } else {\r
10199     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10200     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10201   }\r
10202 }\r
10203 \r
10204 /*      Generate a series of frame coords from start->mid->finish.\r
10205         The movement rate doubles until the half way point is\r
10206         reached, then halves back down to the final destination,\r
10207         which gives a nice slow in/out effect. The algorithmn\r
10208         may seem to generate too many intermediates for short\r
10209         moves, but remember that the purpose is to attract the\r
10210         viewers attention to the piece about to be moved and\r
10211         then to where it ends up. Too few frames would be less\r
10212         noticeable.                                             */\r
10213 \r
10214 static void\r
10215 Tween(start, mid, finish, factor, frames, nFrames)\r
10216      POINT * start; POINT * mid;\r
10217      POINT * finish; int factor;\r
10218      POINT frames[]; int * nFrames;\r
10219 {\r
10220   int n, fraction = 1, count = 0;\r
10221 \r
10222   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10223   for (n = 0; n < factor; n++)\r
10224     fraction *= 2;\r
10225   for (n = 0; n < factor; n++) {\r
10226     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10227     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10228     count ++;\r
10229     fraction = fraction / 2;\r
10230   }\r
10231   \r
10232   /* Midpoint */\r
10233   frames[count] = *mid;\r
10234   count ++;\r
10235   \r
10236   /* Slow out, stepping 1/2, then 1/4, ... */\r
10237   fraction = 2;\r
10238   for (n = 0; n < factor; n++) {\r
10239     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10240     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10241     count ++;\r
10242     fraction = fraction * 2;\r
10243   }\r
10244   *nFrames = count;\r
10245 }\r
10246 \r
10247 void\r
10248 SettingsPopUp(ChessProgramState *cps)\r
10249 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10250       EngineOptionsPopup(savedHwnd, cps);\r
10251 }\r
10252 \r
10253 int flock(int fid, int code)\r
10254 {\r
10255     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10256     OVERLAPPED ov;\r
10257     ov.hEvent = NULL;\r
10258     ov.Offset = 0;\r
10259     ov.OffsetHigh = 0;\r
10260     switch(code) {\r
10261       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10262 \r
10263       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10264       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10265       default: return -1;\r
10266     }\r
10267     return 0;\r
10268 }\r
10269 \r
10270 char *\r
10271 Col2Text (int n)\r
10272 {\r
10273     static int i=0;\r
10274     static char col[8][20];\r
10275     COLORREF color = *(COLORREF *) colorVariable[n];\r
10276     i = i+1 & 7;\r
10277     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10278     return col[i];\r
10279 }\r
10280 \r
10281 void\r
10282 ActivateTheme (int new)\r
10283 {   // Redo initialization of features depending on options that can occur in themes\r
10284    InitTextures();\r
10285    if(new) InitDrawingColors();\r
10286    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10287    InitDrawingSizes(boardSize, 0);\r
10288    InvalidateRect(hwndMain, NULL, TRUE);\r
10289 }\r