Make holdings scrollable
[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 \r
2072         return PM_WL;\r
2073     case WhiteFalcon:\r
2074         return PM_WV;\r
2075     case WhiteCobra:\r
2076         return PM_WS;\r
2077     case WhiteCardinal:\r
2078         return PM_WAB;\r
2079     case WhiteDragon:\r
2080         return PM_WD;\r
2081     }\r
2082 \r
2083     return 0;\r
2084 }\r
2085 \r
2086 void CreatePiecesFromFont()\r
2087 {\r
2088     LOGFONT lf;\r
2089     HDC hdc_window = NULL;\r
2090     HDC hdc = NULL;\r
2091     HFONT hfont_old;\r
2092     int fontHeight;\r
2093     int i;\r
2094 \r
2095     if( fontBitmapSquareSize < 0 ) {\r
2096         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2097         return;\r
2098     }\r
2099 \r
2100     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2101             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2102         fontBitmapSquareSize = -1;\r
2103         return;\r
2104     }\r
2105 \r
2106     if( fontBitmapSquareSize != squareSize ) {\r
2107         hdc_window = GetDC( hwndMain );\r
2108         hdc = CreateCompatibleDC( hdc_window );\r
2109 \r
2110         if( hPieceFont != NULL ) {\r
2111             DeleteObject( hPieceFont );\r
2112         }\r
2113         else {\r
2114             for( i=0; i<=(int)BlackKing; i++ ) {\r
2115                 hPieceMask[i] = NULL;\r
2116                 hPieceFace[i] = NULL;\r
2117             }\r
2118         }\r
2119 \r
2120         fontHeight = 75;\r
2121 \r
2122         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2123             fontHeight = appData.fontPieceSize;\r
2124         }\r
2125 \r
2126         fontHeight = (fontHeight * squareSize) / 100;\r
2127 \r
2128         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2129         lf.lfWidth = 0;\r
2130         lf.lfEscapement = 0;\r
2131         lf.lfOrientation = 0;\r
2132         lf.lfWeight = FW_NORMAL;\r
2133         lf.lfItalic = 0;\r
2134         lf.lfUnderline = 0;\r
2135         lf.lfStrikeOut = 0;\r
2136         lf.lfCharSet = DEFAULT_CHARSET;\r
2137         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2138         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2139         lf.lfQuality = PROOF_QUALITY;\r
2140         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2141         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2142         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2143 \r
2144         hPieceFont = CreateFontIndirect( &lf );\r
2145 \r
2146         if( hPieceFont == NULL ) {\r
2147             fontBitmapSquareSize = -2;\r
2148         }\r
2149         else {\r
2150             /* Setup font-to-piece character table */\r
2151             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2152                 /* No (or wrong) global settings, try to detect the font */\r
2153                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2154                     /* Alpha */\r
2155                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2156                 }\r
2157                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2158                     /* DiagramTT* family */\r
2159                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2160                 }\r
2161                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2162                     /* Fairy symbols */\r
2163                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2164                 }\r
2165                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2166                     /* Good Companion (Some characters get warped as literal :-( */\r
2167                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2168                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2169                     SetCharTable(pieceToFontChar, s);\r
2170                 }\r
2171                 else {\r
2172                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2173                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2174                 }\r
2175             }\r
2176 \r
2177             /* Create bitmaps */\r
2178             hfont_old = SelectObject( hdc, hPieceFont );\r
2179             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2180                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2181                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2182 \r
2183             SelectObject( hdc, hfont_old );\r
2184 \r
2185             fontBitmapSquareSize = squareSize;\r
2186         }\r
2187     }\r
2188 \r
2189     if( hdc != NULL ) {\r
2190         DeleteDC( hdc );\r
2191     }\r
2192 \r
2193     if( hdc_window != NULL ) {\r
2194         ReleaseDC( hwndMain, hdc_window );\r
2195     }\r
2196 }\r
2197 \r
2198 HBITMAP\r
2199 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2200 {\r
2201   char name[128], buf[MSG_SIZ], *ids = "pnbrqfeicwmohajgdvlsukaacvdklnwpwnwlwswolfgnuzebracameltowersword", *p;\r
2202 \r
2203     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2204   if(appData.pieceDirectory[0]) {\r
2205     HBITMAP res;\r
2206     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2207     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2208     if(res) return res;\r
2209     p = strstr(ids, piece);\r
2210     if(p) { // if we could reconstruct canonical piece number, try the pieceNNN_ format before falling back on built-ins\r
2211       int n = p - ids;\r
2212       switch(n) {\r
2213         case 21: n = WhiteKing; break;\r
2214         case 22: n = WhiteAngel; break;\r
2215         case 24: n = WhiteSilver; break;\r
2216         case 26: n = WhiteDragon; break;\r
2217         case 28: n = WhiteLion; break;\r
2218         case 30: n = WhiteTokin; break;\r
2219         case 32: n = WhitePKnight; break;\r
2220         case 34: n = WhitePLance; break;\r
2221         case 36: n = WhitePSilver; break;\r
2222         case 38: n = WhiteWolf; break;\r
2223         case 42: n = WhiteGnu; break;\r
2224         case 45: n = WhiteZebra; break;\r
2225         case 50: n = WhiteCamel; break;\r
2226         case 55: n = WhiteTower; break;\r
2227         case 60: n = WhiteSword; break;\r
2228       }\r
2229       snprintf(buf, MSG_SIZ, "%s\\piece%d_%d%s.bmp", appData.pieceDirectory, n, squareSize, suffix);\r
2230       res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2231       if(res) return res;\r
2232     }\r
2233   }\r
2234   if (gameInfo.event &&\r
2235       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2236       strcmp(name, "k80s") == 0) {\r
2237     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2238   }\r
2239   return LoadBitmap(hinst, name);\r
2240 }\r
2241 \r
2242 \r
2243 /* Insert a color into the program's logical palette\r
2244    structure.  This code assumes the given color is\r
2245    the result of the RGB or PALETTERGB macro, and it\r
2246    knows how those macros work (which is documented).\r
2247 */\r
2248 VOID\r
2249 InsertInPalette(COLORREF color)\r
2250 {\r
2251   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2252 \r
2253   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2254     DisplayFatalError(_("Too many colors"), 0, 1);\r
2255     pLogPal->palNumEntries--;\r
2256     return;\r
2257   }\r
2258 \r
2259   pe->peFlags = (char) 0;\r
2260   pe->peRed = (char) (0xFF & color);\r
2261   pe->peGreen = (char) (0xFF & (color >> 8));\r
2262   pe->peBlue = (char) (0xFF & (color >> 16));\r
2263   return;\r
2264 }\r
2265 \r
2266 \r
2267 VOID\r
2268 InitDrawingColors()\r
2269 {\r
2270   int i;\r
2271   if (pLogPal == NULL) {\r
2272     /* Allocate enough memory for a logical palette with\r
2273      * PALETTESIZE entries and set the size and version fields\r
2274      * of the logical palette structure.\r
2275      */\r
2276     pLogPal = (NPLOGPALETTE)\r
2277       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2278                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2279     pLogPal->palVersion    = 0x300;\r
2280   }\r
2281   pLogPal->palNumEntries = 0;\r
2282 \r
2283   InsertInPalette(lightSquareColor);\r
2284   InsertInPalette(darkSquareColor);\r
2285   InsertInPalette(whitePieceColor);\r
2286   InsertInPalette(blackPieceColor);\r
2287   InsertInPalette(highlightSquareColor);\r
2288   InsertInPalette(premoveHighlightColor);\r
2289 \r
2290   /*  create a logical color palette according the information\r
2291    *  in the LOGPALETTE structure.\r
2292    */\r
2293   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2294 \r
2295   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2296   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2297   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2298   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2299   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2300   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2301   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2302     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2303 \r
2304    /* [AS] Force rendering of the font-based pieces */\r
2305   if( fontBitmapSquareSize > 0 ) {\r
2306     fontBitmapSquareSize = 0;\r
2307   }\r
2308 }\r
2309 \r
2310 \r
2311 int\r
2312 BoardWidth(int boardSize, int n)\r
2313 { /* [HGM] argument n added to allow different width and height */\r
2314   int lineGap = sizeInfo[boardSize].lineGap;\r
2315 \r
2316   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2317       lineGap = appData.overrideLineGap;\r
2318   }\r
2319 \r
2320   return (n + 1) * lineGap +\r
2321           n * sizeInfo[boardSize].squareSize;\r
2322 }\r
2323 \r
2324 /* Respond to board resize by dragging edge */\r
2325 VOID\r
2326 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2327 {\r
2328   BoardSize newSize = NUM_SIZES - 1;\r
2329   static int recurse = 0;\r
2330   if (IsIconic(hwndMain)) return;\r
2331   if (recurse > 0) return;\r
2332   recurse++;\r
2333   while (newSize > 0) {\r
2334         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2335         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2336            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2337     newSize--;\r
2338   } \r
2339   boardSize = newSize;\r
2340   InitDrawingSizes(boardSize, flags);\r
2341   recurse--;\r
2342 }\r
2343 \r
2344 \r
2345 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2346 \r
2347 VOID\r
2348 InitDrawingSizes(BoardSize boardSize, int flags)\r
2349 {\r
2350   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2351   ChessSquare piece;\r
2352   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2353   HDC hdc;\r
2354   SIZE clockSize, messageSize;\r
2355   HFONT oldFont;\r
2356   char buf[MSG_SIZ];\r
2357   char *str;\r
2358   HMENU hmenu = GetMenu(hwndMain);\r
2359   RECT crect, wrect, oldRect;\r
2360   int offby;\r
2361   LOGBRUSH logbrush;\r
2362   VariantClass v = gameInfo.variant;\r
2363 \r
2364   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2365   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2366 \r
2367   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2368   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2369   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2370   oldBoardSize = boardSize;\r
2371 \r
2372   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2373   { // correct board size to one where built-in pieces exist\r
2374     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2375        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2376 \r
2377       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2378       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2379       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2380       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2381       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2382                                    boardSize = SizeMiddling;\r
2383     }\r
2384   }\r
2385   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2386 \r
2387   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2388   oldRect.top = wpMain.y;\r
2389   oldRect.right = wpMain.x + wpMain.width;\r
2390   oldRect.bottom = wpMain.y + wpMain.height;\r
2391 \r
2392   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2393   smallLayout = sizeInfo[boardSize].smallLayout;\r
2394   squareSize = sizeInfo[boardSize].squareSize;\r
2395   lineGap = sizeInfo[boardSize].lineGap;\r
2396   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2397   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2398 \r
2399   // [HGM] decide on tininess based on total board width rather than square size\r
2400   tinyLayout = squareSize * (BOARD_WIDTH);\r
2401   tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;\r
2402 \r
2403   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2404       lineGap = appData.overrideLineGap;\r
2405   }\r
2406 \r
2407   if (tinyLayout != oldTinyLayout) {\r
2408     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2409     if (tinyLayout == 2) {\r
2410       style &= ~WS_SYSMENU;\r
2411       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2412                  "&Minimize\tCtrl+F4");\r
2413     } else {\r
2414       style |= WS_SYSMENU;\r
2415       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2416     }\r
2417     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2418 \r
2419     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2420       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2421         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2422     }\r
2423     DrawMenuBar(hwndMain);\r
2424   }\r
2425 \r
2426   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2427   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2428 \r
2429   /* Get text area sizes */\r
2430   hdc = GetDC(hwndMain);\r
2431   if (appData.clockMode) {\r
2432     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2433   } else {\r
2434     snprintf(buf, MSG_SIZ, _("White"));\r
2435   }\r
2436   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2437   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2438   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2439   str = _("We only care about the height here");\r
2440   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2441   SelectObject(hdc, oldFont);\r
2442   ReleaseDC(hwndMain, hdc);\r
2443 \r
2444   /* Compute where everything goes */\r
2445   if((first.programLogo || second.programLogo) && tinyLayout != 2) {\r
2446         /* [HGM] logo: if either logo is on, reserve space for it */\r
2447         logoHeight =  2*clockSize.cy;\r
2448         leftLogoRect.left   = OUTER_MARGIN;\r
2449         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2450         leftLogoRect.top    = OUTER_MARGIN;\r
2451         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2452 \r
2453         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2454         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2455         rightLogoRect.top    = OUTER_MARGIN;\r
2456         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2457 \r
2458 \r
2459     whiteRect.left = leftLogoRect.right;\r
2460     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2461     whiteRect.top = OUTER_MARGIN;\r
2462     whiteRect.bottom = whiteRect.top + logoHeight;\r
2463 \r
2464     blackRect.right = rightLogoRect.left;\r
2465     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2466     blackRect.top = whiteRect.top;\r
2467     blackRect.bottom = whiteRect.bottom;\r
2468   } else {\r
2469     whiteRect.left = OUTER_MARGIN;\r
2470     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2471     whiteRect.top = OUTER_MARGIN;\r
2472     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2473 \r
2474     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2475     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2476     blackRect.top = whiteRect.top;\r
2477     blackRect.bottom = whiteRect.bottom;\r
2478 \r
2479     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2480   }\r
2481 \r
2482   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2483   if (appData.showButtonBar) {\r
2484     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2485       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2486   } else {\r
2487     messageRect.right = OUTER_MARGIN + boardWidth;\r
2488   }\r
2489   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2490   messageRect.bottom = messageRect.top + messageSize.cy;\r
2491 \r
2492   boardRect.left = OUTER_MARGIN;\r
2493   boardRect.right = boardRect.left + boardWidth;\r
2494   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2495   boardRect.bottom = boardRect.top + boardHeight;\r
2496 \r
2497   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2498   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2499   oldTinyLayout = tinyLayout;\r
2500   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2501   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2502     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2503   winW *= 1 + twoBoards;\r
2504   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2505   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2506   wpMain.height = winH; //       without disturbing window attachments\r
2507   GetWindowRect(hwndMain, &wrect);\r
2508   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2509                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2510 \r
2511   // [HGM] placement: let attached windows follow size change.\r
2512   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2513   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2514   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2515   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2516   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2517 \r
2518   /* compensate if menu bar wrapped */\r
2519   GetClientRect(hwndMain, &crect);\r
2520   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2521   wpMain.height += offby;\r
2522   switch (flags) {\r
2523   case WMSZ_TOPLEFT:\r
2524     SetWindowPos(hwndMain, NULL, \r
2525                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2526                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2527     break;\r
2528 \r
2529   case WMSZ_TOPRIGHT:\r
2530   case WMSZ_TOP:\r
2531     SetWindowPos(hwndMain, NULL, \r
2532                  wrect.left, wrect.bottom - wpMain.height, \r
2533                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2534     break;\r
2535 \r
2536   case WMSZ_BOTTOMLEFT:\r
2537   case WMSZ_LEFT:\r
2538     SetWindowPos(hwndMain, NULL, \r
2539                  wrect.right - wpMain.width, wrect.top, \r
2540                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2541     break;\r
2542 \r
2543   case WMSZ_BOTTOMRIGHT:\r
2544   case WMSZ_BOTTOM:\r
2545   case WMSZ_RIGHT:\r
2546   default:\r
2547     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2548                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2549     break;\r
2550   }\r
2551 \r
2552   hwndPause = NULL;\r
2553   for (i = 0; i < N_BUTTONS; i++) {\r
2554     if (buttonDesc[i].hwnd != NULL) {\r
2555       DestroyWindow(buttonDesc[i].hwnd);\r
2556       buttonDesc[i].hwnd = NULL;\r
2557     }\r
2558     if (appData.showButtonBar) {\r
2559       buttonDesc[i].hwnd =\r
2560         CreateWindow("BUTTON", buttonDesc[i].label,\r
2561                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2562                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2563                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2564                      (HMENU) buttonDesc[i].id,\r
2565                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2566       if (tinyLayout == 2) {\r
2567         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2568                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2569                     MAKELPARAM(FALSE, 0));\r
2570       }\r
2571       if (buttonDesc[i].id == IDM_Pause)\r
2572         hwndPause = buttonDesc[i].hwnd;\r
2573       buttonDesc[i].wndproc = (WNDPROC)\r
2574         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2575     }\r
2576   }\r
2577   if (gridPen != NULL) DeleteObject(gridPen);\r
2578   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2579   if (premovePen != NULL) DeleteObject(premovePen);\r
2580   if (lineGap != 0) {\r
2581     logbrush.lbStyle = BS_SOLID;\r
2582     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2583     gridPen =\r
2584       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2585                    lineGap, &logbrush, 0, NULL);\r
2586     logbrush.lbColor = highlightSquareColor;\r
2587     highlightPen =\r
2588       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2589                    lineGap, &logbrush, 0, NULL);\r
2590 \r
2591     logbrush.lbColor = premoveHighlightColor; \r
2592     premovePen =\r
2593       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2594                    lineGap, &logbrush, 0, NULL);\r
2595 \r
2596     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2597     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2598       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2599       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2600         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2601       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2602         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2603       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2604     }\r
2605     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2606       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2607       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2608         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2609         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2610       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2611         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2612       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2613     }\r
2614   }\r
2615 \r
2616   /* [HGM] Licensing requirement */\r
2617 #ifdef GOTHIC\r
2618   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2619 #endif\r
2620 #ifdef FALCON\r
2621   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2622 #endif\r
2623   GothicPopUp( "", VariantNormal);\r
2624 \r
2625 \r
2626 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2627 \r
2628   /* Load piece bitmaps for this board size */\r
2629   for (i=0; i<=2; i++) {\r
2630     for (piece = WhitePawn;\r
2631          (int) piece < (int) BlackPawn;\r
2632          piece = (ChessSquare) ((int) piece + 1)) {\r
2633       if (pieceBitmap[i][piece] != NULL)\r
2634         DeleteObject(pieceBitmap[i][piece]);\r
2635       pieceBitmap[i][piece] = NULL;\r
2636     }\r
2637   }\r
2638 \r
2639   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2640 \r
2641   // Orthodox Chess pieces\r
2642   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2643   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2644   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2645   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2646   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2647   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2648   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2649   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2650   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2651   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2652   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2653   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2654   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2655   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2656   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2657   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2658     // in Shogi, Hijack the unused Queen for Lance\r
2659     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2660     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2661     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2662   } else {\r
2663     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2664     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2665     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2666   }\r
2667 \r
2668   if(squareSize <= 72 && squareSize >= 33) { \r
2669     /* A & C are available in most sizes now */\r
2670     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2671       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2672       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2673       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2674       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2675       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2676       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2677       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2678       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2679       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2680       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2681       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2682       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2683     } else { // Smirf-like\r
2684       if(gameInfo.variant == VariantSChess) {\r
2685         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2686         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2687         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2688       } else {\r
2689         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2690         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2691         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2692       }\r
2693     }\r
2694     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2695       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2696       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2697       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2698     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2699       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2700       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2701       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2702     } else { // WinBoard standard\r
2703       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2704       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2705       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2706     }\r
2707   }\r
2708 \r
2709 \r
2710   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2711     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2712     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2713     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2714     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2715     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2716     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2717     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2718     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2719     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2720     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2721     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2722     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2723     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2724     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2725     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2726     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2727     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2728     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2729     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2730     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2731     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2732     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2733     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2734     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2735     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2736     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2737     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2738     pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2739     pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2740     pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2741     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2742     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2743     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2744     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2745     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2746     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2747     pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2748     pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2749     pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2750     pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");\r
2751     pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");\r
2752     pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");\r
2753     pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");\r
2754     pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");\r
2755     pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");\r
2756     pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");\r
2757     pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");\r
2758     pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2759     pieceBitmap[0][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "s");\r
2760     pieceBitmap[1][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "o");\r
2761     pieceBitmap[2][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "w");\r
2762     pieceBitmap[0][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "s");\r
2763     pieceBitmap[1][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "o");\r
2764     pieceBitmap[2][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "w");\r
2765     pieceBitmap[0][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "s");\r
2766     pieceBitmap[1][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "o");\r
2767     pieceBitmap[2][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "w");\r
2768 \r
2769     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
2770       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2771       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2772       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2773       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2774       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2775       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2776       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2777       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2778       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2779       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2780       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2781       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2782     } else {\r
2783       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2784       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2785       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2786       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2787       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2788       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2789       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2790       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2791       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2792       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2793       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2794       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2795     }\r
2796 \r
2797   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2798     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2799     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2800     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2801     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2802     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2803     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2804     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2805     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2806     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2807     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2808     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2809     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2810     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2811     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2812   }\r
2813 \r
2814 \r
2815   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2816   /* special Shogi support in this size */\r
2817   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2818       for (piece = WhitePawn;\r
2819            (int) piece < (int) BlackPawn;\r
2820            piece = (ChessSquare) ((int) piece + 1)) {\r
2821         if (pieceBitmap[i][piece] != NULL)\r
2822           DeleteObject(pieceBitmap[i][piece]);\r
2823       }\r
2824     }\r
2825   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2826   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2827   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2828   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2829   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2830   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2831   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2832   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2833   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2834   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2835   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2836   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2837   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2838   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2839   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2840   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2841   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2842   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2843   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2844   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2845   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2846   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2847   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2848   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2849   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2850   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2851   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2852   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2853   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2854   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2855   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2856   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2857   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2858   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2859   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2860   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2861   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2862   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2863   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2864   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2865   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2866   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2867   minorSize = 0;\r
2868   }\r
2869 \r
2870   if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention\r
2871     char buf[MSG_SIZ];\r
2872     if(pieceBitmap[0][i]) continue;\r
2873     snprintf(buf, MSG_SIZ, "piece%d_", i);\r
2874     pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");\r
2875     pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");\r
2876     pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");\r
2877   }\r
2878 }\r
2879 \r
2880 HBITMAP\r
2881 PieceBitmap(ChessSquare p, int kind)\r
2882 {\r
2883   if ((int) p >= (int) BlackPawn)\r
2884     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2885 \r
2886   return pieceBitmap[kind][(int) p];\r
2887 }\r
2888 \r
2889 /***************************************************************/\r
2890 \r
2891 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2892 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2893 /*\r
2894 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2895 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2896 */\r
2897 \r
2898 VOID\r
2899 SquareToPos(int row, int column, int * x, int * y)\r
2900 {\r
2901   if (flipView) {\r
2902     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2903     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2904   } else {\r
2905     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2906     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2907   }\r
2908 }\r
2909 \r
2910 VOID\r
2911 DrawCoordsOnDC(HDC hdc)\r
2912 {\r
2913   static char files[] = "0123456789012345678901221098765432109876543210";\r
2914   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2915   char str[2] = { NULLCHAR, NULLCHAR };\r
2916   int oldMode, oldAlign, x, y, start, i;\r
2917   HFONT oldFont;\r
2918   HBRUSH oldBrush;\r
2919 \r
2920   if (!appData.showCoords)\r
2921     return;\r
2922 \r
2923   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2924 \r
2925   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2926   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2927   oldAlign = GetTextAlign(hdc);\r
2928   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2929 \r
2930   y = boardRect.top + lineGap;\r
2931   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2932 \r
2933   if(border) {\r
2934     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2935     x += border - lineGap - 4; y += squareSize - 6;\r
2936   } else\r
2937   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2938   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2939     str[0] = files[start + i];\r
2940     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2941     y += squareSize + lineGap;\r
2942   }\r
2943 \r
2944   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2945 \r
2946   if(border) {\r
2947     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2948     x += -border + 4; y += border - squareSize + 6;\r
2949   } else\r
2950   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2951   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2952     str[0] = ranks[start + i];\r
2953     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2954     x += squareSize + lineGap;\r
2955   }    \r
2956 \r
2957   SelectObject(hdc, oldBrush);\r
2958   SetBkMode(hdc, oldMode);\r
2959   SetTextAlign(hdc, oldAlign);\r
2960   SelectObject(hdc, oldFont);\r
2961 }\r
2962 \r
2963 VOID\r
2964 DrawGridOnDC(HDC hdc)\r
2965 {\r
2966   HPEN oldPen;\r
2967  \r
2968   if (lineGap != 0) {\r
2969     oldPen = SelectObject(hdc, gridPen);\r
2970     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2971     SelectObject(hdc, oldPen);\r
2972   }\r
2973 }\r
2974 \r
2975 #define HIGHLIGHT_PEN 0\r
2976 #define PREMOVE_PEN   1\r
2977 \r
2978 VOID\r
2979 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2980 {\r
2981   int x1, y1;\r
2982   HPEN oldPen, hPen;\r
2983   if (lineGap == 0) return;\r
2984   if (flipView) {\r
2985     x1 = boardRect.left +\r
2986       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2987     y1 = boardRect.top +\r
2988       lineGap/2 + y * (squareSize + lineGap) + border;\r
2989   } else {\r
2990     x1 = boardRect.left +\r
2991       lineGap/2 + x * (squareSize + lineGap) + border;\r
2992     y1 = boardRect.top +\r
2993       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2994   }\r
2995   hPen = pen ? premovePen : highlightPen;\r
2996   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2997   MoveToEx(hdc, x1, y1, NULL);\r
2998   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2999   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3000   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3001   LineTo(hdc, x1, y1);\r
3002   SelectObject(hdc, oldPen);\r
3003 }\r
3004 \r
3005 VOID\r
3006 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
3007 {\r
3008   int i;\r
3009   for (i=0; i<2; i++) {\r
3010     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
3011       DrawHighlightOnDC(hdc, TRUE,\r
3012                         h->sq[i].x, h->sq[i].y,\r
3013                         pen);\r
3014   }\r
3015 }\r
3016 \r
3017 /* Note: sqcolor is used only in monoMode */\r
3018 /* Note that this code is largely duplicated in woptions.c,\r
3019    function DrawSampleSquare, so that needs to be updated too */\r
3020 VOID\r
3021 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3022 {\r
3023   HBITMAP oldBitmap;\r
3024   HBRUSH oldBrush = NULL;\r
3025   int tmpSize;\r
3026 \r
3027   if (appData.blindfold) return;\r
3028 \r
3029   /* [AS] Use font-based pieces if needed */\r
3030   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
3031     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3032     CreatePiecesFromFont();\r
3033 \r
3034     if( fontBitmapSquareSize == squareSize ) {\r
3035         int index = TranslatePieceToFontPiece(piece);\r
3036 \r
3037         SelectObject( tmphdc, hPieceMask[ index ] );\r
3038 \r
3039       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3040         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
3041       else\r
3042         BitBlt( hdc,\r
3043             x, y,\r
3044             squareSize, squareSize,\r
3045             tmphdc,\r
3046             0, 0,\r
3047             SRCAND );\r
3048 \r
3049         SelectObject( tmphdc, hPieceFace[ index ] );\r
3050 \r
3051       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3052         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
3053       else\r
3054         BitBlt( hdc,\r
3055             x, y,\r
3056             squareSize, squareSize,\r
3057             tmphdc,\r
3058             0, 0,\r
3059             SRCPAINT );\r
3060 \r
3061         return;\r
3062     }\r
3063   }\r
3064 \r
3065   if (appData.monoMode) {\r
3066     SelectObject(tmphdc, PieceBitmap(piece, \r
3067       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3068     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3069            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3070   } else {\r
3071     HBRUSH xBrush = whitePieceBrush;\r
3072     tmpSize = squareSize;\r
3073     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
3074     if(minorSize &&\r
3075         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3076          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3077       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3078       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3079       x += (squareSize - minorSize)>>1;\r
3080       y += squareSize - minorSize - 2;\r
3081       tmpSize = minorSize;\r
3082     }\r
3083 #if WINVER >= 0x0500\r
3084     HBITMAP pbm = PieceBitmap(piece, color ? OUTLINE_PIECE : SOLID_PIECE);\r
3085     BITMAP b;\r
3086     GetObject(pbm, sizeof(BITMAP), &b);\r
3087     if(b.bmBitsPixel == 32) { // for now this is a kludge to indicate bitmaps with alpha channel\r
3088         BLENDFUNCTION bf;\r
3089         bf.BlendOp = AC_SRC_OVER;\r
3090         bf.BlendFlags = 0;\r
3091         bf.SourceConstantAlpha = 0xFF;\r
3092         bf.AlphaFormat = AC_SRC_ALPHA;\r
3093         oldBitmap = SelectObject(tmphdc, pbm);\r
3094         AlphaBlend(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, bf);\r
3095     } else\r
3096 #endif\r
3097     if (color || appData.allWhite ) {\r
3098       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3099       if( color )\r
3100               oldBrush = SelectObject(hdc, xBrush);\r
3101       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3102       if(appData.upsideDown && color==flipView)\r
3103         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3104       else\r
3105         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3106       /* Use black for outline of white pieces */\r
3107       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3108       if(appData.upsideDown && color==flipView)\r
3109         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3110       else\r
3111         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3112     } else if(appData.pieceDirectory[0]) {\r
3113       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3114       oldBrush = SelectObject(hdc, xBrush);\r
3115       if(appData.upsideDown && color==flipView)\r
3116         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3117       else\r
3118         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3119       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3120       if(appData.upsideDown && color==flipView)\r
3121         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3122       else\r
3123         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3124     } else {\r
3125       /* Use square color for details of black pieces */\r
3126       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3127       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3128       if(appData.upsideDown && !flipView)\r
3129         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3130       else\r
3131         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3132     }\r
3133     if(oldBrush) SelectObject(hdc, oldBrush);\r
3134     SelectObject(tmphdc, oldBitmap);\r
3135   }\r
3136 }\r
3137 \r
3138 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3139 int GetBackTextureMode( int algo )\r
3140 {\r
3141     int result = BACK_TEXTURE_MODE_DISABLED;\r
3142 \r
3143     switch( algo ) \r
3144     {\r
3145         case BACK_TEXTURE_MODE_PLAIN:\r
3146             result = 1; /* Always use identity map */\r
3147             break;\r
3148         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3149             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3150             break;\r
3151     }\r
3152 \r
3153     return result;\r
3154 }\r
3155 \r
3156 /* \r
3157     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3158     to handle redraws cleanly (as random numbers would always be different).\r
3159 */\r
3160 VOID RebuildTextureSquareInfo()\r
3161 {\r
3162     BITMAP bi;\r
3163     int lite_w = 0;\r
3164     int lite_h = 0;\r
3165     int dark_w = 0;\r
3166     int dark_h = 0;\r
3167     int row;\r
3168     int col;\r
3169 \r
3170     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3171 \r
3172     if( liteBackTexture != NULL ) {\r
3173         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3174             lite_w = bi.bmWidth;\r
3175             lite_h = bi.bmHeight;\r
3176         }\r
3177     }\r
3178 \r
3179     if( darkBackTexture != NULL ) {\r
3180         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3181             dark_w = bi.bmWidth;\r
3182             dark_h = bi.bmHeight;\r
3183         }\r
3184     }\r
3185 \r
3186     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3187         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3188             if( (col + row) & 1 ) {\r
3189                 /* Lite square */\r
3190                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3191                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3192                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3193                   else\r
3194                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3195                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3196                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3197                   else\r
3198                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3199                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3200                 }\r
3201             }\r
3202             else {\r
3203                 /* Dark square */\r
3204                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3205                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3206                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3207                   else\r
3208                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3209                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3210                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3211                   else\r
3212                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3213                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3214                 }\r
3215             }\r
3216         }\r
3217     }\r
3218 }\r
3219 \r
3220 /* [AS] Arrow highlighting support */\r
3221 \r
3222 static double A_WIDTH = 5; /* Width of arrow body */\r
3223 \r
3224 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3225 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3226 \r
3227 static double Sqr( double x )\r
3228 {\r
3229     return x*x;\r
3230 }\r
3231 \r
3232 static int Round( double x )\r
3233 {\r
3234     return (int) (x + 0.5);\r
3235 }\r
3236 \r
3237 /* Draw an arrow between two points using current settings */\r
3238 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3239 {\r
3240     POINT arrow[7];\r
3241     double dx, dy, j, k, x, y;\r
3242 \r
3243     if( d_x == s_x ) {\r
3244         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3245 \r
3246         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3247         arrow[0].y = s_y;\r
3248 \r
3249         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3250         arrow[1].y = d_y - h;\r
3251 \r
3252         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3253         arrow[2].y = d_y - h;\r
3254 \r
3255         arrow[3].x = d_x;\r
3256         arrow[3].y = d_y;\r
3257 \r
3258         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3259         arrow[5].y = d_y - h;\r
3260 \r
3261         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3262         arrow[4].y = d_y - h;\r
3263 \r
3264         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3265         arrow[6].y = s_y;\r
3266     }\r
3267     else if( d_y == s_y ) {\r
3268         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3269 \r
3270         arrow[0].x = s_x;\r
3271         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3272 \r
3273         arrow[1].x = d_x - w;\r
3274         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3275 \r
3276         arrow[2].x = d_x - w;\r
3277         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3278 \r
3279         arrow[3].x = d_x;\r
3280         arrow[3].y = d_y;\r
3281 \r
3282         arrow[5].x = d_x - w;\r
3283         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3284 \r
3285         arrow[4].x = d_x - w;\r
3286         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3287 \r
3288         arrow[6].x = s_x;\r
3289         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3290     }\r
3291     else {\r
3292         /* [AS] Needed a lot of paper for this! :-) */\r
3293         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3294         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3295   \r
3296         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3297 \r
3298         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3299 \r
3300         x = s_x;\r
3301         y = s_y;\r
3302 \r
3303         arrow[0].x = Round(x - j);\r
3304         arrow[0].y = Round(y + j*dx);\r
3305 \r
3306         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3307         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3308 \r
3309         if( d_x > s_x ) {\r
3310             x = (double) d_x - k;\r
3311             y = (double) d_y - k*dy;\r
3312         }\r
3313         else {\r
3314             x = (double) d_x + k;\r
3315             y = (double) d_y + k*dy;\r
3316         }\r
3317 \r
3318         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3319 \r
3320         arrow[6].x = Round(x - j);\r
3321         arrow[6].y = Round(y + j*dx);\r
3322 \r
3323         arrow[2].x = Round(arrow[6].x + 2*j);\r
3324         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3325 \r
3326         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3327         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3328 \r
3329         arrow[4].x = d_x;\r
3330         arrow[4].y = d_y;\r
3331 \r
3332         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3333         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3334     }\r
3335 \r
3336     Polygon( hdc, arrow, 7 );\r
3337 }\r
3338 \r
3339 /* [AS] Draw an arrow between two squares */\r
3340 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3341 {\r
3342     int s_x, s_y, d_x, d_y;\r
3343     HPEN hpen;\r
3344     HPEN holdpen;\r
3345     HBRUSH hbrush;\r
3346     HBRUSH holdbrush;\r
3347     LOGBRUSH stLB;\r
3348 \r
3349     if( s_col == d_col && s_row == d_row ) {\r
3350         return;\r
3351     }\r
3352 \r
3353     /* Get source and destination points */\r
3354     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3355     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3356 \r
3357     if( d_y > s_y ) {\r
3358         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3359     }\r
3360     else if( d_y < s_y ) {\r
3361         d_y += squareSize / 2 + squareSize / 4;\r
3362     }\r
3363     else {\r
3364         d_y += squareSize / 2;\r
3365     }\r
3366 \r
3367     if( d_x > s_x ) {\r
3368         d_x += squareSize / 2 - squareSize / 4;\r
3369     }\r
3370     else if( d_x < s_x ) {\r
3371         d_x += squareSize / 2 + squareSize / 4;\r
3372     }\r
3373     else {\r
3374         d_x += squareSize / 2;\r
3375     }\r
3376 \r
3377     s_x += squareSize / 2;\r
3378     s_y += squareSize / 2;\r
3379 \r
3380     /* Adjust width */\r
3381     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3382 \r
3383     /* Draw */\r
3384     stLB.lbStyle = BS_SOLID;\r
3385     stLB.lbColor = appData.highlightArrowColor;\r
3386     stLB.lbHatch = 0;\r
3387 \r
3388     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3389     holdpen = SelectObject( hdc, hpen );\r
3390     hbrush = CreateBrushIndirect( &stLB );\r
3391     holdbrush = SelectObject( hdc, hbrush );\r
3392 \r
3393     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3394 \r
3395     SelectObject( hdc, holdpen );\r
3396     SelectObject( hdc, holdbrush );\r
3397     DeleteObject( hpen );\r
3398     DeleteObject( hbrush );\r
3399 }\r
3400 \r
3401 BOOL HasHighlightInfo()\r
3402 {\r
3403     BOOL result = FALSE;\r
3404 \r
3405     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3406         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3407     {\r
3408         result = TRUE;\r
3409     }\r
3410 \r
3411     return result;\r
3412 \r
3413 \r
3414 \r
3415 }\r
3416 \r
3417 BOOL IsDrawArrowEnabled()\r
3418 {\r
3419     BOOL result = FALSE;\r
3420 \r
3421     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3422         result = TRUE;\r
3423     }\r
3424 \r
3425     return result;\r
3426 }\r
3427 \r
3428 VOID DrawArrowHighlight( HDC hdc )\r
3429 {\r
3430     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3431         DrawArrowBetweenSquares( hdc,\r
3432             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3433             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3434     }\r
3435 }\r
3436 \r
3437 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3438 {\r
3439     HRGN result = NULL;\r
3440 \r
3441     if( HasHighlightInfo() ) {\r
3442         int x1, y1, x2, y2;\r
3443         int sx, sy, dx, dy;\r
3444 \r
3445         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3446         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3447 \r
3448         sx = MIN( x1, x2 );\r
3449         sy = MIN( y1, y2 );\r
3450         dx = MAX( x1, x2 ) + squareSize;\r
3451         dy = MAX( y1, y2 ) + squareSize;\r
3452 \r
3453         result = CreateRectRgn( sx, sy, dx, dy );\r
3454     }\r
3455 \r
3456     return result;\r
3457 }\r
3458 \r
3459 /*\r
3460     Warning: this function modifies the behavior of several other functions. \r
3461     \r
3462     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3463     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3464     repaint is scattered all over the place, which is not good for features such as\r
3465     "arrow highlighting" that require a full repaint of the board.\r
3466 \r
3467     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3468     user interaction, when speed is not so important) but especially to avoid errors\r
3469     in the displayed graphics.\r
3470 \r
3471     In such patched places, I always try refer to this function so there is a single\r
3472     place to maintain knowledge.\r
3473     \r
3474     To restore the original behavior, just return FALSE unconditionally.\r
3475 */\r
3476 BOOL IsFullRepaintPreferrable()\r
3477 {\r
3478     BOOL result = FALSE;\r
3479 \r
3480     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3481         /* Arrow may appear on the board */\r
3482         result = TRUE;\r
3483     }\r
3484 \r
3485     return result;\r
3486 }\r
3487 \r
3488 /* \r
3489     This function is called by DrawPosition to know whether a full repaint must\r
3490     be forced or not.\r
3491 \r
3492     Only DrawPosition may directly call this function, which makes use of \r
3493     some state information. Other function should call DrawPosition specifying \r
3494     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3495 */\r
3496 BOOL DrawPositionNeedsFullRepaint()\r
3497 {\r
3498     BOOL result = FALSE;\r
3499 \r
3500     /* \r
3501         Probably a slightly better policy would be to trigger a full repaint\r
3502         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3503         but animation is fast enough that it's difficult to notice.\r
3504     */\r
3505     if( animInfo.piece == EmptySquare ) {\r
3506         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3507             result = TRUE;\r
3508         }\r
3509     }\r
3510 \r
3511     return result;\r
3512 }\r
3513 \r
3514 static HBITMAP borderBitmap;\r
3515 \r
3516 VOID\r
3517 DrawBackgroundOnDC(HDC hdc)\r
3518 {\r
3519   \r
3520   BITMAP bi;\r
3521   HDC tmphdc;\r
3522   HBITMAP hbm;\r
3523   static char oldBorder[MSG_SIZ];\r
3524   int w = 600, h = 600, mode;\r
3525 \r
3526   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3527     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3528     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3529   }\r
3530   if(borderBitmap == NULL) { // loading failed, use white\r
3531     FillRect( hdc, &boardRect, whitePieceBrush );\r
3532     return;\r
3533   }\r
3534   tmphdc = CreateCompatibleDC(hdc);\r
3535   hbm = SelectObject(tmphdc, borderBitmap);\r
3536   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3537             w = bi.bmWidth;\r
3538             h = bi.bmHeight;\r
3539   }\r
3540   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3541   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3542                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3543   SetStretchBltMode(hdc, mode);\r
3544   SelectObject(tmphdc, hbm);\r
3545   DeleteDC(tmphdc);\r
3546 }\r
3547 \r
3548 VOID\r
3549 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3550 {\r
3551   int row, column, x, y, square_color, piece_color;\r
3552   ChessSquare piece;\r
3553   HBRUSH oldBrush;\r
3554   HDC texture_hdc = NULL;\r
3555 \r
3556   /* [AS] Initialize background textures if needed */\r
3557   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3558       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3559       if( backTextureSquareSize != squareSize \r
3560        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3561           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3562           backTextureSquareSize = squareSize;\r
3563           RebuildTextureSquareInfo();\r
3564       }\r
3565 \r
3566       texture_hdc = CreateCompatibleDC( hdc );\r
3567   }\r
3568 \r
3569   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3570     for (column = 0; column < BOARD_WIDTH; column++) {\r
3571   \r
3572       SquareToPos(row, column, &x, &y);\r
3573 \r
3574       piece = board[row][column];\r
3575 \r
3576       square_color = ((column + row) % 2) == 1;\r
3577       if( gameInfo.variant == VariantXiangqi ) {\r
3578           square_color = !InPalace(row, column);\r
3579           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3580           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3581       }\r
3582       piece_color = (int) piece < (int) BlackPawn;\r
3583 \r
3584 \r
3585       /* [HGM] holdings file: light square or black */\r
3586       if(column == BOARD_LEFT-2) {\r
3587             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3588                 square_color = 1;\r
3589             else {\r
3590                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3591                 continue;\r
3592             }\r
3593       } else\r
3594       if(column == BOARD_RGHT + 1 ) {\r
3595             if( row < gameInfo.holdingsSize )\r
3596                 square_color = 1;\r
3597             else {\r
3598                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3599                 continue;\r
3600             }\r
3601       }\r
3602       if(column == BOARD_LEFT-1 ) /* left align */\r
3603             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3604       else if( column == BOARD_RGHT) /* right align */\r
3605             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3606       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3607       else\r
3608       if (appData.monoMode) {\r
3609         if (piece == EmptySquare) {\r
3610           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3611                  square_color ? WHITENESS : BLACKNESS);\r
3612         } else {\r
3613           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3614         }\r
3615       } \r
3616       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3617           /* [AS] Draw the square using a texture bitmap */\r
3618           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3619           int r = row, c = column; // [HGM] do not flip board in flipView\r
3620           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3621 \r
3622           DrawTile( x, y, \r
3623               squareSize, squareSize, \r
3624               hdc, \r
3625               texture_hdc,\r
3626               backTextureSquareInfo[r][c].mode,\r
3627               backTextureSquareInfo[r][c].x,\r
3628               backTextureSquareInfo[r][c].y );\r
3629 \r
3630           SelectObject( texture_hdc, hbm );\r
3631 \r
3632           if (piece != EmptySquare) {\r
3633               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3634           }\r
3635       }\r
3636       else {\r
3637         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3638 \r
3639         oldBrush = SelectObject(hdc, brush );\r
3640         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3641         SelectObject(hdc, oldBrush);\r
3642         if (piece != EmptySquare)\r
3643           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3644       }\r
3645     }\r
3646   }\r
3647 \r
3648   if( texture_hdc != NULL ) {\r
3649     DeleteDC( texture_hdc );\r
3650   }\r
3651 }\r
3652 \r
3653 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3654 void fputDW(FILE *f, int x)\r
3655 {\r
3656         fputc(x     & 255, f);\r
3657         fputc(x>>8  & 255, f);\r
3658         fputc(x>>16 & 255, f);\r
3659         fputc(x>>24 & 255, f);\r
3660 }\r
3661 \r
3662 #define MAX_CLIPS 200   /* more than enough */\r
3663 \r
3664 VOID\r
3665 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3666 {\r
3667 //  HBITMAP bufferBitmap;\r
3668   BITMAP bi;\r
3669 //  RECT Rect;\r
3670   HDC tmphdc;\r
3671   HBITMAP hbm;\r
3672   int w = 100, h = 50;\r
3673 \r
3674   if(logo == NULL) {\r
3675     if(!logoHeight) return;\r
3676     FillRect( hdc, &logoRect, whitePieceBrush );\r
3677   }\r
3678 //  GetClientRect(hwndMain, &Rect);\r
3679 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3680 //                                      Rect.bottom-Rect.top+1);\r
3681   tmphdc = CreateCompatibleDC(hdc);\r
3682   hbm = SelectObject(tmphdc, logo);\r
3683   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3684             w = bi.bmWidth;\r
3685             h = bi.bmHeight;\r
3686   }\r
3687   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3688                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3689   SelectObject(tmphdc, hbm);\r
3690   DeleteDC(tmphdc);\r
3691 }\r
3692 \r
3693 VOID\r
3694 DisplayLogos()\r
3695 {\r
3696   if(logoHeight) {\r
3697         HDC hdc = GetDC(hwndMain);\r
3698         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3699         if(appData.autoLogo) {\r
3700           \r
3701           switch(gameMode) { // pick logos based on game mode\r
3702             case IcsObserving:\r
3703                 whiteLogo = second.programLogo; // ICS logo\r
3704                 blackLogo = second.programLogo;\r
3705             default:\r
3706                 break;\r
3707             case IcsPlayingWhite:\r
3708                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3709                 blackLogo = second.programLogo; // ICS logo\r
3710                 break;\r
3711             case IcsPlayingBlack:\r
3712                 whiteLogo = second.programLogo; // ICS logo\r
3713                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3714                 break;\r
3715             case TwoMachinesPlay:\r
3716                 if(first.twoMachinesColor[0] == 'b') {\r
3717                     whiteLogo = second.programLogo;\r
3718                     blackLogo = first.programLogo;\r
3719                 }\r
3720                 break;\r
3721             case MachinePlaysWhite:\r
3722                 blackLogo = userLogo;\r
3723                 break;\r
3724             case MachinePlaysBlack:\r
3725                 whiteLogo = userLogo;\r
3726                 blackLogo = first.programLogo;\r
3727           }\r
3728         }\r
3729         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3730         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3731         ReleaseDC(hwndMain, hdc);\r
3732   }\r
3733 }\r
3734 \r
3735 void\r
3736 UpdateLogos(int display)\r
3737 { // called after loading new engine(s), in tourney or from menu\r
3738   LoadLogo(&first, 0, FALSE);\r
3739   LoadLogo(&second, 1, appData.icsActive);\r
3740   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3741   if(display) DisplayLogos();\r
3742 }\r
3743 \r
3744 static HDC hdcSeek;\r
3745 \r
3746 // [HGM] seekgraph\r
3747 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3748 {\r
3749     POINT stPt;\r
3750     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3751     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3752     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3753     SelectObject( hdcSeek, hp );\r
3754 }\r
3755 \r
3756 // front-end wrapper for drawing functions to do rectangles\r
3757 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3758 {\r
3759     HPEN hp;\r
3760     RECT rc;\r
3761 \r
3762     if (hdcSeek == NULL) {\r
3763     hdcSeek = GetDC(hwndMain);\r
3764       if (!appData.monoMode) {\r
3765         SelectPalette(hdcSeek, hPal, FALSE);\r
3766         RealizePalette(hdcSeek);\r
3767       }\r
3768     }\r
3769     hp = SelectObject( hdcSeek, gridPen );\r
3770     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3771     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3772     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3773     SelectObject( hdcSeek, hp );\r
3774 }\r
3775 \r
3776 // front-end wrapper for putting text in graph\r
3777 void DrawSeekText(char *buf, int x, int y)\r
3778 {\r
3779         SIZE stSize;\r
3780         SetBkMode( hdcSeek, TRANSPARENT );\r
3781         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3782         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3783 }\r
3784 \r
3785 void DrawSeekDot(int x, int y, int color)\r
3786 {\r
3787         int square = color & 0x80;\r
3788         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3789                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3790         color &= 0x7F;\r
3791         if(square)\r
3792             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3793                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3794         else\r
3795             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3796                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3797             SelectObject(hdcSeek, oldBrush);\r
3798 }\r
3799 \r
3800 void DrawSeekOpen()\r
3801 {\r
3802 }\r
3803 \r
3804 void DrawSeekClose()\r
3805 {\r
3806 }\r
3807 \r
3808 \r
3809 \r
3810 \r
3811 \r
3812 VOID\r
3813 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3814 {\r
3815   static Board lastReq[2], lastDrawn[2];\r
3816   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3817   static int lastDrawnFlipView = 0;\r
3818   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3819   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3820   HDC tmphdc;\r
3821   HDC hdcmem;\r
3822   HBITMAP bufferBitmap;\r
3823   HBITMAP oldBitmap;\r
3824   RECT Rect;\r
3825   HRGN clips[MAX_CLIPS];\r
3826   ChessSquare dragged_piece = EmptySquare;\r
3827   int nr = twoBoards*partnerUp;\r
3828 \r
3829   /* I'm undecided on this - this function figures out whether a full\r
3830    * repaint is necessary on its own, so there's no real reason to have the\r
3831    * caller tell it that.  I think this can safely be set to FALSE - but\r
3832    * if we trust the callers not to request full repaints unnessesarily, then\r
3833    * we could skip some clipping work.  In other words, only request a full\r
3834    * redraw when the majority of pieces have changed positions (ie. flip, \r
3835    * gamestart and similar)  --Hawk\r
3836    */\r
3837   Boolean fullrepaint = repaint;\r
3838 \r
3839   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3840 \r
3841   if( DrawPositionNeedsFullRepaint() ) {\r
3842       fullrepaint = TRUE;\r
3843   }\r
3844 \r
3845   if (board == NULL) {\r
3846     if (!lastReqValid[nr]) {\r
3847       return;\r
3848     }\r
3849     board = lastReq[nr];\r
3850   } else {\r
3851     CopyBoard(lastReq[nr], board);\r
3852     lastReqValid[nr] = 1;\r
3853   }\r
3854 \r
3855   if (doingSizing) {\r
3856     return;\r
3857   }\r
3858 \r
3859   if (IsIconic(hwndMain)) {\r
3860     return;\r
3861   }\r
3862 \r
3863   if (hdc == NULL) {\r
3864     hdc = GetDC(hwndMain);\r
3865     if (!appData.monoMode) {\r
3866       SelectPalette(hdc, hPal, FALSE);\r
3867       RealizePalette(hdc);\r
3868     }\r
3869     releaseDC = TRUE;\r
3870   } else {\r
3871     releaseDC = FALSE;\r
3872   }\r
3873 \r
3874   /* Create some work-DCs */\r
3875   hdcmem = CreateCompatibleDC(hdc);\r
3876   tmphdc = CreateCompatibleDC(hdc);\r
3877 \r
3878   /* If dragging is in progress, we temporarely remove the piece */\r
3879   /* [HGM] or temporarily decrease count if stacked              */\r
3880   /*       !! Moved to before board compare !!                   */\r
3881   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3882     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3883     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3884             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3885         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3886     } else \r
3887     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3888             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3889         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3890     } else \r
3891         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3892   }\r
3893 \r
3894   /* Figure out which squares need updating by comparing the \r
3895    * newest board with the last drawn board and checking if\r
3896    * flipping has changed.\r
3897    */\r
3898   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3899     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3900       for (column = 0; column < BOARD_WIDTH; column++) {\r
3901         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3902           SquareToPos(row, column, &x, &y);\r
3903           clips[num_clips++] =\r
3904             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3905         }\r
3906       }\r
3907     }\r
3908    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3909     for (i=0; i<2; i++) {\r
3910       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3911           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3912         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3913             lastDrawnHighlight.sq[i].y >= 0) {\r
3914           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3915                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3916           clips[num_clips++] =\r
3917             CreateRectRgn(x - lineGap, y - lineGap, \r
3918                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3919         }\r
3920         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3921           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3922           clips[num_clips++] =\r
3923             CreateRectRgn(x - lineGap, y - lineGap, \r
3924                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3925         }\r
3926       }\r
3927     }\r
3928     for (i=0; i<2; i++) {\r
3929       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3930           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3931         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3932             lastDrawnPremove.sq[i].y >= 0) {\r
3933           SquareToPos(lastDrawnPremove.sq[i].y,\r
3934                       lastDrawnPremove.sq[i].x, &x, &y);\r
3935           clips[num_clips++] =\r
3936             CreateRectRgn(x - lineGap, y - lineGap, \r
3937                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3938         }\r
3939         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3940             premoveHighlightInfo.sq[i].y >= 0) {\r
3941           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3942                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3943           clips[num_clips++] =\r
3944             CreateRectRgn(x - lineGap, y - lineGap, \r
3945                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3946         }\r
3947       }\r
3948     }\r
3949    } else { // nr == 1\r
3950         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3951         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3952         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3953         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3954       for (i=0; i<2; i++) {\r
3955         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3956             partnerHighlightInfo.sq[i].y >= 0) {\r
3957           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3958                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3959           clips[num_clips++] =\r
3960             CreateRectRgn(x - lineGap, y - lineGap, \r
3961                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3962         }\r
3963         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3964             oldPartnerHighlight.sq[i].y >= 0) {\r
3965           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3966                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3967           clips[num_clips++] =\r
3968             CreateRectRgn(x - lineGap, y - lineGap, \r
3969                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3970         }\r
3971       }\r
3972    }\r
3973   } else {\r
3974     fullrepaint = TRUE;\r
3975   }\r
3976 \r
3977   /* Create a buffer bitmap - this is the actual bitmap\r
3978    * being written to.  When all the work is done, we can\r
3979    * copy it to the real DC (the screen).  This avoids\r
3980    * the problems with flickering.\r
3981    */\r
3982   GetClientRect(hwndMain, &Rect);\r
3983   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3984                                         Rect.bottom-Rect.top+1);\r
3985   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3986   if (!appData.monoMode) {\r
3987     SelectPalette(hdcmem, hPal, FALSE);\r
3988   }\r
3989 \r
3990   /* Create clips for dragging */\r
3991   if (!fullrepaint) {\r
3992     if (dragInfo.from.x >= 0) {\r
3993       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3994       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3995     }\r
3996     if (dragInfo.start.x >= 0) {\r
3997       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3998       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3999     }\r
4000     if (dragInfo.pos.x >= 0) {\r
4001       x = dragInfo.pos.x - squareSize / 2;\r
4002       y = dragInfo.pos.y - squareSize / 2;\r
4003       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4004     }\r
4005     if (dragInfo.lastpos.x >= 0) {\r
4006       x = dragInfo.lastpos.x - squareSize / 2;\r
4007       y = dragInfo.lastpos.y - squareSize / 2;\r
4008       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4009     }\r
4010   }\r
4011 \r
4012   /* Are we animating a move?  \r
4013    * If so, \r
4014    *   - remove the piece from the board (temporarely)\r
4015    *   - calculate the clipping region\r
4016    */\r
4017   if (!fullrepaint) {\r
4018     if (animInfo.piece != EmptySquare) {\r
4019       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4020       x = boardRect.left + animInfo.lastpos.x;\r
4021       y = boardRect.top + animInfo.lastpos.y;\r
4022       x2 = boardRect.left + animInfo.pos.x;\r
4023       y2 = boardRect.top + animInfo.pos.y;\r
4024       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4025       /* Slight kludge.  The real problem is that after AnimateMove is\r
4026          done, the position on the screen does not match lastDrawn.\r
4027          This currently causes trouble only on e.p. captures in\r
4028          atomic, where the piece moves to an empty square and then\r
4029          explodes.  The old and new positions both had an empty square\r
4030          at the destination, but animation has drawn a piece there and\r
4031          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4032 \r
4033       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4034     }\r
4035   }\r
4036 \r
4037   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4038   if (num_clips == 0)\r
4039     fullrepaint = TRUE;\r
4040 \r
4041   /* Set clipping on the memory DC */\r
4042   if (!fullrepaint) {\r
4043     SelectClipRgn(hdcmem, clips[0]);\r
4044     for (x = 1; x < num_clips; x++) {\r
4045       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4046         abort();  // this should never ever happen!\r
4047     }\r
4048   }\r
4049 \r
4050   /* Do all the drawing to the memory DC */\r
4051   if(explodeInfo.radius) { // [HGM] atomic\r
4052         HBRUSH oldBrush;\r
4053         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4054         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
4055         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4056         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4057         x += squareSize/2;\r
4058         y += squareSize/2;\r
4059         if(!fullrepaint) {\r
4060           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4061           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4062         }\r
4063         DrawGridOnDC(hdcmem);\r
4064         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4065         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4066         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4067         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
4068         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4069         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4070         SelectObject(hdcmem, oldBrush);\r
4071   } else {\r
4072     if(border) DrawBackgroundOnDC(hdcmem);\r
4073     DrawGridOnDC(hdcmem);\r
4074     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
4075         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4076         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4077     } else {\r
4078         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
4079         oldPartnerHighlight = partnerHighlightInfo;\r
4080     }\r
4081     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4082   }\r
4083   if(nr == 0) // [HGM] dual: markers only on left board\r
4084   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4085     for (column = 0; column < BOARD_WIDTH; column++) {\r
4086         if (marker[row][column]) { // marker changes only occur with full repaint!\r
4087             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
4088             SquareToPos(row, column, &x, &y);\r
4089             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
4090                           x + 3*squareSize/4, y + 3*squareSize/4);\r
4091             SelectObject(hdcmem, oldBrush);\r
4092         }\r
4093     }\r
4094   }\r
4095 \r
4096   if( appData.highlightMoveWithArrow ) {\r
4097 \r
4098     DrawArrowHighlight(hdcmem);\r
4099   }\r
4100 \r
4101   DrawCoordsOnDC(hdcmem);\r
4102 \r
4103   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4104                  /* to make sure lastDrawn contains what is actually drawn */\r
4105 \r
4106   /* Put the dragged piece back into place and draw it (out of place!) */\r
4107     if (dragged_piece != EmptySquare) {\r
4108     /* [HGM] or restack */\r
4109     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4110                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4111     else\r
4112     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4113                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4114 \r
4115     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4116     x = dragInfo.pos.x - squareSize / 2;\r
4117     y = dragInfo.pos.y - squareSize / 2;\r
4118     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4119                   ((int) dragInfo.piece < (int) BlackPawn), \r
4120                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4121   }   \r
4122   \r
4123   /* Put the animated piece back into place and draw it */\r
4124   if (animInfo.piece != EmptySquare) {\r
4125     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4126     x = boardRect.left + animInfo.pos.x;\r
4127     y = boardRect.top + animInfo.pos.y;\r
4128     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4129                   ((int) animInfo.piece < (int) BlackPawn),\r
4130                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4131   }\r
4132 \r
4133   /* Release the bufferBitmap by selecting in the old bitmap \r
4134    * and delete the memory DC\r
4135    */\r
4136   SelectObject(hdcmem, oldBitmap);\r
4137   DeleteDC(hdcmem);\r
4138 \r
4139   /* Set clipping on the target DC */\r
4140   if (!fullrepaint) {\r
4141     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4142         RECT rect;\r
4143         GetRgnBox(clips[x], &rect);\r
4144         DeleteObject(clips[x]);\r
4145         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4146                           rect.right + wpMain.width/2, rect.bottom);\r
4147     }\r
4148     SelectClipRgn(hdc, clips[0]);\r
4149     for (x = 1; x < num_clips; x++) {\r
4150       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4151         abort();   // this should never ever happen!\r
4152     } \r
4153   }\r
4154 \r
4155   /* Copy the new bitmap onto the screen in one go.\r
4156    * This way we avoid any flickering\r
4157    */\r
4158   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4159   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4160          boardRect.right - boardRect.left,\r
4161          boardRect.bottom - boardRect.top,\r
4162          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4163   if(saveDiagFlag) { \r
4164     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4165     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4166     HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);\r
4167 \r
4168     bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);\r
4169     obmp = SelectObject(tmp, bufferBitmap);\r
4170     BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,\r
4171            tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);\r
4172     GetObject(bufferBitmap, sizeof(b), &b);\r
4173     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4174         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4175         bih.biWidth = b.bmWidth;\r
4176         bih.biHeight = b.bmHeight;\r
4177         bih.biPlanes = 1;\r
4178         bih.biBitCount = b.bmBitsPixel;\r
4179         bih.biCompression = 0;\r
4180         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4181         bih.biXPelsPerMeter = 0;\r
4182         bih.biYPelsPerMeter = 0;\r
4183         bih.biClrUsed = 0;\r
4184         bih.biClrImportant = 0;\r
4185 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4186 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4187         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4188 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4189 \r
4190         wb = b.bmWidthBytes;\r
4191         // count colors\r
4192         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4193                 int k = ((int*) pData)[i];\r
4194                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4195                 if(j >= 16) break;\r
4196                 color[j] = k;\r
4197                 if(j >= nrColors) nrColors = j+1;\r
4198         }\r
4199         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4200                 INT p = 0;\r
4201                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4202                     for(w=0; w<(wb>>2); w+=2) {\r
4203                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4204                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4205                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4206                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4207                         pData[p++] = m | j<<4;\r
4208                     }\r
4209                     while(p&3) pData[p++] = 0;\r
4210                 }\r
4211                 fac = 3;\r
4212                 wb = ((wb+31)>>5)<<2;\r
4213         }\r
4214         // write BITMAPFILEHEADER\r
4215         fprintf(diagFile, "BM");\r
4216         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4217         fputDW(diagFile, 0);\r
4218         fputDW(diagFile, 0x36 + (fac?64:0));\r
4219         // write BITMAPINFOHEADER\r
4220         fputDW(diagFile, 40);\r
4221         fputDW(diagFile, b.bmWidth);\r
4222         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4223         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4224         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\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         fputDW(diagFile, 0);\r
4231         // write color table\r
4232         if(fac)\r
4233         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4234         // write bitmap data\r
4235 \r
4236         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4237                 fputc(pData[i], diagFile);\r
4238         free(pData);\r
4239      }\r
4240      DeleteObject(bufferBitmap); bufferBitmap = src;\r
4241      SelectObject(tmp, obmp);\r
4242      DeleteDC(tmp);\r
4243   }\r
4244 \r
4245   SelectObject(tmphdc, oldBitmap);\r
4246 \r
4247   /* Massive cleanup */\r
4248   for (x = 0; x < num_clips; x++)\r
4249     DeleteObject(clips[x]);\r
4250 \r
4251   DeleteDC(tmphdc);\r
4252   DeleteObject(bufferBitmap);\r
4253 \r
4254   if (releaseDC) \r
4255     ReleaseDC(hwndMain, hdc);\r
4256   \r
4257   if (lastDrawnFlipView != flipView && nr == 0) {\r
4258     if (flipView)\r
4259       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4260     else\r
4261       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4262   }\r
4263 \r
4264 /*  CopyBoard(lastDrawn, board);*/\r
4265   lastDrawnHighlight = highlightInfo;\r
4266   lastDrawnPremove   = premoveHighlightInfo;\r
4267   lastDrawnFlipView = flipView;\r
4268   lastDrawnValid[nr] = 1;\r
4269 }\r
4270 \r
4271 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4272 int\r
4273 SaveDiagram(f)\r
4274      FILE *f;\r
4275 {\r
4276     saveDiagFlag = 1; diagFile = f;\r
4277     HDCDrawPosition(NULL, TRUE, NULL);\r
4278     saveDiagFlag = 0;\r
4279 \r
4280     fclose(f);\r
4281     return TRUE;\r
4282 }\r
4283 \r
4284 \r
4285 /*---------------------------------------------------------------------------*\\r
4286 | CLIENT PAINT PROCEDURE\r
4287 |   This is the main event-handler for the WM_PAINT message.\r
4288 |\r
4289 \*---------------------------------------------------------------------------*/\r
4290 VOID\r
4291 PaintProc(HWND hwnd)\r
4292 {\r
4293   HDC         hdc;\r
4294   PAINTSTRUCT ps;\r
4295   HFONT       oldFont;\r
4296 \r
4297   if((hdc = BeginPaint(hwnd, &ps))) {\r
4298     if (IsIconic(hwnd)) {\r
4299       DrawIcon(hdc, 2, 2, iconCurrent);\r
4300     } else {\r
4301       if (!appData.monoMode) {\r
4302         SelectPalette(hdc, hPal, FALSE);\r
4303         RealizePalette(hdc);\r
4304       }\r
4305       HDCDrawPosition(hdc, 1, NULL);\r
4306       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4307         flipView = !flipView; partnerUp = !partnerUp;\r
4308         HDCDrawPosition(hdc, 1, NULL);\r
4309         flipView = !flipView; partnerUp = !partnerUp;\r
4310       }\r
4311       oldFont =\r
4312         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4313       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4314                  ETO_CLIPPED|ETO_OPAQUE,\r
4315                  &messageRect, messageText, strlen(messageText), NULL);\r
4316       SelectObject(hdc, oldFont);\r
4317       DisplayBothClocks();\r
4318       DisplayLogos();\r
4319     }\r
4320     EndPaint(hwnd,&ps);\r
4321   }\r
4322 \r
4323   return;\r
4324 }\r
4325 \r
4326 \r
4327 /*\r
4328  * If the user selects on a border boundary, return -1; if off the board,\r
4329  *   return -2.  Otherwise map the event coordinate to the square.\r
4330  * The offset boardRect.left or boardRect.top must already have been\r
4331  *   subtracted from x.\r
4332  */\r
4333 int EventToSquare(x, limit)\r
4334      int x, limit;\r
4335 {\r
4336   if (x <= border)\r
4337     return -2;\r
4338   if (x < lineGap + border)\r
4339     return -1;\r
4340   x -= lineGap + border;\r
4341   if ((x % (squareSize + lineGap)) >= squareSize)\r
4342     return -1;\r
4343   x /= (squareSize + lineGap);\r
4344     if (x >= limit)\r
4345     return -2;\r
4346   return x;\r
4347 }\r
4348 \r
4349 typedef struct {\r
4350   char piece;\r
4351   int command;\r
4352   char* name;\r
4353 } DropEnable;\r
4354 \r
4355 DropEnable dropEnables[] = {\r
4356   { 'P', DP_Pawn, N_("Pawn") },\r
4357   { 'N', DP_Knight, N_("Knight") },\r
4358   { 'B', DP_Bishop, N_("Bishop") },\r
4359   { 'R', DP_Rook, N_("Rook") },\r
4360   { 'Q', DP_Queen, N_("Queen") },\r
4361 };\r
4362 \r
4363 VOID\r
4364 SetupDropMenu(HMENU hmenu)\r
4365 {\r
4366   int i, count, enable;\r
4367   char *p;\r
4368   extern char white_holding[], black_holding[];\r
4369   char item[MSG_SIZ];\r
4370 \r
4371   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4372     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4373                dropEnables[i].piece);\r
4374     count = 0;\r
4375     while (p && *p++ == dropEnables[i].piece) count++;\r
4376       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4377     enable = count > 0 || !appData.testLegality\r
4378       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4379                       && !appData.icsActive);\r
4380     ModifyMenu(hmenu, dropEnables[i].command,\r
4381                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4382                dropEnables[i].command, item);\r
4383   }\r
4384 }\r
4385 \r
4386 void DragPieceBegin(int x, int y, Boolean instantly)\r
4387 {\r
4388       dragInfo.lastpos.x = boardRect.left + x;\r
4389       dragInfo.lastpos.y = boardRect.top + y;\r
4390       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4391       dragInfo.from.x = fromX;\r
4392       dragInfo.from.y = fromY;\r
4393       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4394       dragInfo.start = dragInfo.from;\r
4395       SetCapture(hwndMain);\r
4396 }\r
4397 \r
4398 void DragPieceEnd(int x, int y)\r
4399 {\r
4400     ReleaseCapture();\r
4401     dragInfo.start.x = dragInfo.start.y = -1;\r
4402     dragInfo.from = dragInfo.start;\r
4403     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4404 }\r
4405 \r
4406 void ChangeDragPiece(ChessSquare piece)\r
4407 {\r
4408     dragInfo.piece = piece;\r
4409 }\r
4410 \r
4411 /* Event handler for mouse messages */\r
4412 VOID\r
4413 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4414 {\r
4415   int x, y, menuNr;\r
4416   POINT pt;\r
4417   static int recursive = 0;\r
4418   HMENU hmenu;\r
4419   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4420 \r
4421   if (recursive) {\r
4422     if (message == WM_MBUTTONUP) {\r
4423       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4424          to the middle button: we simulate pressing the left button too!\r
4425          */\r
4426       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4427       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4428     }\r
4429     return;\r
4430   }\r
4431   recursive++;\r
4432   \r
4433   pt.x = LOWORD(lParam);\r
4434   pt.y = HIWORD(lParam);\r
4435   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4436   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4437   if (!flipView && y >= 0) {\r
4438     y = BOARD_HEIGHT - 1 - y;\r
4439   }\r
4440   if (flipView && x >= 0) {\r
4441     x = BOARD_WIDTH - 1 - x;\r
4442   }\r
4443 \r
4444   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4445   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4446 \r
4447   switch (message) {\r
4448   case WM_LBUTTONDOWN:\r
4449       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4450         ClockClick(flipClock); break;\r
4451       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4452         ClockClick(!flipClock); break;\r
4453       }\r
4454     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4455       dragInfo.start.x = dragInfo.start.y = -1;\r
4456       dragInfo.from = dragInfo.start;\r
4457     }\r
4458     if(fromX == -1 && frozen) { // not sure where this is for\r
4459                 fromX = fromY = -1; \r
4460       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4461       break;\r
4462     }\r
4463       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4464       DrawPosition(TRUE, NULL);\r
4465     break;\r
4466 \r
4467   case WM_LBUTTONUP:\r
4468       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4469       DrawPosition(TRUE, NULL);\r
4470     break;\r
4471 \r
4472   case WM_MOUSEMOVE:\r
4473     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4474     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4475     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4476     if ((appData.animateDragging || appData.highlightDragging)\r
4477         && (wParam & MK_LBUTTON || dragging == 2)\r
4478         && dragInfo.from.x >= 0) \r
4479     {\r
4480       BOOL full_repaint = FALSE;\r
4481 \r
4482       if (appData.animateDragging) {\r
4483         dragInfo.pos = pt;\r
4484       }\r
4485       if (appData.highlightDragging) {\r
4486         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4487         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4488             full_repaint = TRUE;\r
4489         }\r
4490       }\r
4491       \r
4492       DrawPosition( full_repaint, NULL);\r
4493       \r
4494       dragInfo.lastpos = dragInfo.pos;\r
4495     }\r
4496     break;\r
4497 \r
4498   case WM_MOUSEWHEEL: // [DM]\r
4499     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4500        /* Mouse Wheel is being rolled forward\r
4501         * Play moves forward\r
4502         */\r
4503        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4504                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4505        /* Mouse Wheel is being rolled backward\r
4506         * Play moves backward\r
4507         */\r
4508        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4509                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4510     }\r
4511     break;\r
4512 \r
4513   case WM_MBUTTONUP:\r
4514   case WM_RBUTTONUP:\r
4515     ReleaseCapture();\r
4516     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4517     break;\r
4518  \r
4519   case WM_MBUTTONDOWN:\r
4520   case WM_RBUTTONDOWN:\r
4521     ErrorPopDown();\r
4522     ReleaseCapture();\r
4523     fromX = fromY = -1;\r
4524     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4525     dragInfo.start.x = dragInfo.start.y = -1;\r
4526     dragInfo.from = dragInfo.start;\r
4527     dragInfo.lastpos = dragInfo.pos;\r
4528     if (appData.highlightDragging) {\r
4529       ClearHighlights();\r
4530     }\r
4531     if(y == -2) {\r
4532       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4533       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4534           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4535       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4536           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4537       }\r
4538       break;\r
4539     }\r
4540     DrawPosition(TRUE, NULL);\r
4541 \r
4542     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4543     switch (menuNr) {\r
4544     case 0:\r
4545       if (message == WM_MBUTTONDOWN) {\r
4546         buttonCount = 3;  /* even if system didn't think so */\r
4547         if (wParam & MK_SHIFT) \r
4548           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4549         else\r
4550           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4551       } else { /* message == WM_RBUTTONDOWN */\r
4552         /* Just have one menu, on the right button.  Windows users don't\r
4553            think to try the middle one, and sometimes other software steals\r
4554            it, or it doesn't really exist. */\r
4555         if(gameInfo.variant != VariantShogi)\r
4556             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4557         else\r
4558             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4559       }\r
4560       break;\r
4561     case 2:\r
4562       SetCapture(hwndMain);\r
4563       break;\r
4564     case 1:\r
4565       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4566       SetupDropMenu(hmenu);\r
4567       MenuPopup(hwnd, pt, hmenu, -1);\r
4568     default:\r
4569       break;\r
4570     }\r
4571     break;\r
4572   }\r
4573 \r
4574   recursive--;\r
4575 }\r
4576 \r
4577 /* Preprocess messages for buttons in main window */\r
4578 LRESULT CALLBACK\r
4579 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4580 {\r
4581   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4582   int i, dir;\r
4583 \r
4584   for (i=0; i<N_BUTTONS; i++) {\r
4585     if (buttonDesc[i].id == id) break;\r
4586   }\r
4587   if (i == N_BUTTONS) return 0;\r
4588   switch (message) {\r
4589   case WM_KEYDOWN:\r
4590     switch (wParam) {\r
4591     case VK_LEFT:\r
4592     case VK_RIGHT:\r
4593       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4594       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4595       return TRUE;\r
4596     }\r
4597     break;\r
4598   case WM_CHAR:\r
4599     switch (wParam) {\r
4600     case '\r':\r
4601       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4602       return TRUE;\r
4603     default:\r
4604       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4605         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4606         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4607         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4608         SetFocus(h);\r
4609         SendMessage(h, WM_CHAR, wParam, lParam);\r
4610         return TRUE;\r
4611       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4612         TypeInEvent((char)wParam);\r
4613       }\r
4614       break;\r
4615     }\r
4616     break;\r
4617   }\r
4618   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4619 }\r
4620 \r
4621 static int promoStyle;\r
4622 \r
4623 /* Process messages for Promotion dialog box */\r
4624 LRESULT CALLBACK\r
4625 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4626 {\r
4627 \r
4628   char promoChar;\r
4629 \r
4630   switch (message) {\r
4631 \r
4632   case WM_INITDIALOG: /* message: initialize dialog box */\r
4633     /* Center the dialog over the application window */\r
4634     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4635     Translate(hDlg, DLG_PromotionKing);\r
4636     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4637       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4638        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4639        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4640                SW_SHOW : SW_HIDE);\r
4641     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4642     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4643        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4644          PieceToChar(WhiteAngel) != '~') ||\r
4645         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4646          PieceToChar(BlackAngel) != '~')   ) ?\r
4647                SW_SHOW : SW_HIDE);\r
4648     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4649        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4650          PieceToChar(WhiteMarshall) != '~') ||\r
4651         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4652          PieceToChar(BlackMarshall) != '~')   ) ?\r
4653                SW_SHOW : SW_HIDE);\r
4654     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4655     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4656     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4657     if(promoStyle) {\r
4658         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4659         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4660         SetWindowText(hDlg, "Promote?");\r
4661     }\r
4662     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4663        gameInfo.variant == VariantSuper ?\r
4664                SW_SHOW : SW_HIDE);\r
4665     return TRUE;\r
4666 \r
4667   case WM_COMMAND: /* message: received a command */\r
4668     switch (LOWORD(wParam)) {\r
4669     case IDCANCEL:\r
4670       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4671       ClearHighlights();\r
4672       DrawPosition(FALSE, NULL);\r
4673       return TRUE;\r
4674     case PB_King:\r
4675       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4676       break;\r
4677     case PB_Queen:\r
4678       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4679       break;\r
4680     case PB_Rook:\r
4681       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4682       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4683       break;\r
4684     case PB_Bishop:\r
4685       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4686       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4687       break;\r
4688     case PB_Chancellor:\r
4689       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4690       break;\r
4691     case PB_Archbishop:\r
4692       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4693       break;\r
4694     case PB_Knight:\r
4695       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4696                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4697       break;\r
4698     default:\r
4699       return FALSE;\r
4700     }\r
4701     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4702     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4703     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4704     fromX = fromY = -1;\r
4705     if (!appData.highlightLastMove) {\r
4706       ClearHighlights();\r
4707       DrawPosition(FALSE, NULL);\r
4708     }\r
4709     return TRUE;\r
4710   }\r
4711   return FALSE;\r
4712 }\r
4713 \r
4714 /* Pop up promotion dialog */\r
4715 VOID\r
4716 PromotionPopup(HWND hwnd)\r
4717 {\r
4718   FARPROC lpProc;\r
4719 \r
4720   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4721   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4722     hwnd, (DLGPROC)lpProc);\r
4723   FreeProcInstance(lpProc);\r
4724 }\r
4725 \r
4726 void\r
4727 PromotionPopUp(char choice)\r
4728 {\r
4729   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4730   DrawPosition(TRUE, NULL);\r
4731   PromotionPopup(hwndMain);\r
4732 }\r
4733 \r
4734 VOID\r
4735 LoadGameDialog(HWND hwnd, char* title)\r
4736 {\r
4737   UINT number = 0;\r
4738   FILE *f;\r
4739   char fileTitle[MSG_SIZ];\r
4740   f = OpenFileDialog(hwnd, "rb", "",\r
4741                      appData.oldSaveStyle ? "gam" : "pgn",\r
4742                      GAME_FILT,\r
4743                      title, &number, fileTitle, NULL);\r
4744   if (f != NULL) {\r
4745     cmailMsgLoaded = FALSE;\r
4746     if (number == 0) {\r
4747       int error = GameListBuild(f);\r
4748       if (error) {\r
4749         DisplayError(_("Cannot build game list"), error);\r
4750       } else if (!ListEmpty(&gameList) &&\r
4751                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4752         GameListPopUp(f, fileTitle);\r
4753         return;\r
4754       }\r
4755       GameListDestroy();\r
4756       number = 1;\r
4757     }\r
4758     LoadGame(f, number, fileTitle, FALSE);\r
4759   }\r
4760 }\r
4761 \r
4762 int get_term_width()\r
4763 {\r
4764     HDC hdc;\r
4765     TEXTMETRIC tm;\r
4766     RECT rc;\r
4767     HFONT hfont, hold_font;\r
4768     LOGFONT lf;\r
4769     HWND hText;\r
4770 \r
4771     if (hwndConsole)\r
4772         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4773     else\r
4774         return 79;\r
4775 \r
4776     // get the text metrics\r
4777     hdc = GetDC(hText);\r
4778     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4779     if (consoleCF.dwEffects & CFE_BOLD)\r
4780         lf.lfWeight = FW_BOLD;\r
4781     if (consoleCF.dwEffects & CFE_ITALIC)\r
4782         lf.lfItalic = TRUE;\r
4783     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4784         lf.lfStrikeOut = TRUE;\r
4785     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4786         lf.lfUnderline = TRUE;\r
4787     hfont = CreateFontIndirect(&lf);\r
4788     hold_font = SelectObject(hdc, hfont);\r
4789     GetTextMetrics(hdc, &tm);\r
4790     SelectObject(hdc, hold_font);\r
4791     DeleteObject(hfont);\r
4792     ReleaseDC(hText, hdc);\r
4793 \r
4794     // get the rectangle\r
4795     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4796 \r
4797     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4798 }\r
4799 \r
4800 void UpdateICSWidth(HWND hText)\r
4801 {\r
4802     LONG old_width, new_width;\r
4803 \r
4804     new_width = get_term_width(hText, FALSE);\r
4805     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4806     if (new_width != old_width)\r
4807     {\r
4808         ics_update_width(new_width);\r
4809         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4810     }\r
4811 }\r
4812 \r
4813 VOID\r
4814 ChangedConsoleFont()\r
4815 {\r
4816   CHARFORMAT cfmt;\r
4817   CHARRANGE tmpsel, sel;\r
4818   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4819   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4820   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4821   PARAFORMAT paraf;\r
4822 \r
4823   cfmt.cbSize = sizeof(CHARFORMAT);\r
4824   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4825     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4826                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4827   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4828    * size.  This was undocumented in the version of MSVC++ that I had\r
4829    * when I wrote the code, but is apparently documented now.\r
4830    */\r
4831   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4832   cfmt.bCharSet = f->lf.lfCharSet;\r
4833   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4834   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4835   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4836   /* Why are the following seemingly needed too? */\r
4837   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4838   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4839   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4840   tmpsel.cpMin = 0;\r
4841   tmpsel.cpMax = -1; /*999999?*/\r
4842   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4843   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4844   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4845    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4846    */\r
4847   paraf.cbSize = sizeof(paraf);\r
4848   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4849   paraf.dxStartIndent = 0;\r
4850   paraf.dxOffset = WRAP_INDENT;\r
4851   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4852   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4853   UpdateICSWidth(hText);\r
4854 }\r
4855 \r
4856 /*---------------------------------------------------------------------------*\\r
4857  *\r
4858  * Window Proc for main window\r
4859  *\r
4860 \*---------------------------------------------------------------------------*/\r
4861 \r
4862 /* Process messages for main window, etc. */\r
4863 LRESULT CALLBACK\r
4864 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4865 {\r
4866   FARPROC lpProc;\r
4867   int wmId;\r
4868   char *defName;\r
4869   FILE *f;\r
4870   UINT number;\r
4871   char fileTitle[MSG_SIZ];\r
4872   static SnapData sd;\r
4873   static int peek=0;\r
4874 \r
4875   switch (message) {\r
4876 \r
4877   case WM_PAINT: /* message: repaint portion of window */\r
4878     PaintProc(hwnd);\r
4879     break;\r
4880 \r
4881   case WM_ERASEBKGND:\r
4882     if (IsIconic(hwnd)) {\r
4883       /* Cheat; change the message */\r
4884       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4885     } else {\r
4886       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4887     }\r
4888     break;\r
4889 \r
4890   case WM_LBUTTONDOWN:\r
4891   case WM_MBUTTONDOWN:\r
4892   case WM_RBUTTONDOWN:\r
4893   case WM_LBUTTONUP:\r
4894   case WM_MBUTTONUP:\r
4895   case WM_RBUTTONUP:\r
4896   case WM_MOUSEMOVE:\r
4897   case WM_MOUSEWHEEL:\r
4898     MouseEvent(hwnd, message, wParam, lParam);\r
4899     break;\r
4900 \r
4901   case WM_KEYUP:\r
4902     if((char)wParam == '\b') {\r
4903       ForwardEvent(); peek = 0;\r
4904     }\r
4905 \r
4906     JAWS_KBUP_NAVIGATION\r
4907 \r
4908     break;\r
4909 \r
4910   case WM_KEYDOWN:\r
4911     if((char)wParam == '\b') {\r
4912       if(!peek) BackwardEvent(), peek = 1;\r
4913     }\r
4914 \r
4915     JAWS_KBDOWN_NAVIGATION\r
4916 \r
4917     break;\r
4918 \r
4919   case WM_CHAR:\r
4920     \r
4921     JAWS_ALT_INTERCEPT\r
4922 \r
4923     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4924         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4925         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4926         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4927         SetFocus(h);\r
4928         SendMessage(h, message, wParam, lParam);\r
4929     } else if(lParam != KF_REPEAT) {\r
4930         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4931                 TypeInEvent((char)wParam);\r
4932         } else if((char)wParam == 003) CopyGameToClipboard();\r
4933          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4934     }\r
4935 \r
4936     break;\r
4937 \r
4938   case WM_PALETTECHANGED:\r
4939     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4940       int nnew;\r
4941       HDC hdc = GetDC(hwndMain);\r
4942       SelectPalette(hdc, hPal, TRUE);\r
4943       nnew = RealizePalette(hdc);\r
4944       if (nnew > 0) {\r
4945         paletteChanged = TRUE;\r
4946 \r
4947         InvalidateRect(hwnd, &boardRect, FALSE);\r
4948       }\r
4949       ReleaseDC(hwnd, hdc);\r
4950     }\r
4951     break;\r
4952 \r
4953   case WM_QUERYNEWPALETTE:\r
4954     if (!appData.monoMode /*&& paletteChanged*/) {\r
4955       int nnew;\r
4956       HDC hdc = GetDC(hwndMain);\r
4957       paletteChanged = FALSE;\r
4958       SelectPalette(hdc, hPal, FALSE);\r
4959       nnew = RealizePalette(hdc);\r
4960       if (nnew > 0) {\r
4961         InvalidateRect(hwnd, &boardRect, FALSE);\r
4962       }\r
4963       ReleaseDC(hwnd, hdc);\r
4964       return TRUE;\r
4965     }\r
4966     return FALSE;\r
4967 \r
4968   case WM_COMMAND: /* message: command from application menu */\r
4969     wmId    = LOWORD(wParam);\r
4970 \r
4971     switch (wmId) {\r
4972     case IDM_NewGame:\r
4973       ResetGameEvent();\r
4974       SAY("new game enter a move to play against the computer with white");\r
4975       break;\r
4976 \r
4977     case IDM_NewGameFRC:\r
4978       if( NewGameFRC() == 0 ) {\r
4979         ResetGameEvent();\r
4980       }\r
4981       break;\r
4982 \r
4983     case IDM_NewVariant:\r
4984       NewVariantPopup(hwnd);\r
4985       break;\r
4986 \r
4987     case IDM_LoadGame:\r
4988       LoadGameDialog(hwnd, _("Load Game from File"));\r
4989       break;\r
4990 \r
4991     case IDM_LoadNextGame:\r
4992       ReloadGame(1);\r
4993       break;\r
4994 \r
4995     case IDM_LoadPrevGame:\r
4996       ReloadGame(-1);\r
4997       break;\r
4998 \r
4999     case IDM_ReloadGame:\r
5000       ReloadGame(0);\r
5001       break;\r
5002 \r
5003     case IDM_LoadPosition:\r
5004       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5005         Reset(FALSE, TRUE);\r
5006       }\r
5007       number = 1;\r
5008       f = OpenFileDialog(hwnd, "rb", "",\r
5009                          appData.oldSaveStyle ? "pos" : "fen",\r
5010                          POSITION_FILT,\r
5011                          _("Load Position from File"), &number, fileTitle, NULL);\r
5012       if (f != NULL) {\r
5013         LoadPosition(f, number, fileTitle);\r
5014       }\r
5015       break;\r
5016 \r
5017     case IDM_LoadNextPosition:\r
5018       ReloadPosition(1);\r
5019       break;\r
5020 \r
5021     case IDM_LoadPrevPosition:\r
5022       ReloadPosition(-1);\r
5023       break;\r
5024 \r
5025     case IDM_ReloadPosition:\r
5026       ReloadPosition(0);\r
5027       break;\r
5028 \r
5029     case IDM_SaveGame:\r
5030       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5031       f = OpenFileDialog(hwnd, "a", defName,\r
5032                          appData.oldSaveStyle ? "gam" : "pgn",\r
5033                          GAME_FILT,\r
5034                          _("Save Game to File"), NULL, fileTitle, NULL);\r
5035       if (f != NULL) {\r
5036         SaveGame(f, 0, "");\r
5037       }\r
5038       break;\r
5039 \r
5040     case IDM_SavePosition:\r
5041       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5042       f = OpenFileDialog(hwnd, "a", defName,\r
5043                          appData.oldSaveStyle ? "pos" : "fen",\r
5044                          POSITION_FILT,\r
5045                          _("Save Position to File"), NULL, fileTitle, NULL);\r
5046       if (f != NULL) {\r
5047         SavePosition(f, 0, "");\r
5048       }\r
5049       break;\r
5050 \r
5051     case IDM_SaveDiagram:\r
5052       defName = "diagram";\r
5053       f = OpenFileDialog(hwnd, "wb", defName,\r
5054                          "bmp",\r
5055                          DIAGRAM_FILT,\r
5056                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
5057       if (f != NULL) {\r
5058         SaveDiagram(f);\r
5059       }\r
5060       break;\r
5061 \r
5062     case IDM_SaveSelected:\r
5063       f = OpenFileDialog(hwnd, "a", "",\r
5064                          "pgn",\r
5065                          GAME_FILT,\r
5066                          _("Save Game to File"), NULL, fileTitle, NULL);\r
5067       if (f != NULL) {\r
5068         SaveSelected(f, 0, "");\r
5069       }\r
5070       break;\r
5071 \r
5072     case IDM_CreateBook:\r
5073       CreateBookEvent();\r
5074       break;\r
5075 \r
5076     case IDM_CopyGame:\r
5077       CopyGameToClipboard();\r
5078       break;\r
5079 \r
5080     case IDM_PasteGame:\r
5081       PasteGameFromClipboard();\r
5082       break;\r
5083 \r
5084     case IDM_CopyGameListToClipboard:\r
5085       CopyGameListToClipboard();\r
5086       break;\r
5087 \r
5088     /* [AS] Autodetect FEN or PGN data */\r
5089     case IDM_PasteAny:\r
5090       PasteGameOrFENFromClipboard();\r
5091       break;\r
5092 \r
5093     /* [AS] Move history */\r
5094     case IDM_ShowMoveHistory:\r
5095         if( MoveHistoryIsUp() ) {\r
5096             MoveHistoryPopDown();\r
5097         }\r
5098         else {\r
5099             MoveHistoryPopUp();\r
5100         }\r
5101         break;\r
5102 \r
5103     /* [AS] Eval graph */\r
5104     case IDM_ShowEvalGraph:\r
5105         if( EvalGraphIsUp() ) {\r
5106             EvalGraphPopDown();\r
5107         }\r
5108         else {\r
5109             EvalGraphPopUp();\r
5110             SetFocus(hwndMain);\r
5111         }\r
5112         break;\r
5113 \r
5114     /* [AS] Engine output */\r
5115     case IDM_ShowEngineOutput:\r
5116         if( EngineOutputIsUp() ) {\r
5117             EngineOutputPopDown();\r
5118         }\r
5119         else {\r
5120             EngineOutputPopUp();\r
5121         }\r
5122         break;\r
5123 \r
5124     /* [AS] User adjudication */\r
5125     case IDM_UserAdjudication_White:\r
5126         UserAdjudicationEvent( +1 );\r
5127         break;\r
5128 \r
5129     case IDM_UserAdjudication_Black:\r
5130         UserAdjudicationEvent( -1 );\r
5131         break;\r
5132 \r
5133     case IDM_UserAdjudication_Draw:\r
5134         UserAdjudicationEvent( 0 );\r
5135         break;\r
5136 \r
5137     /* [AS] Game list options dialog */\r
5138     case IDM_GameListOptions:\r
5139       GameListOptions();\r
5140       break;\r
5141 \r
5142     case IDM_NewChat:\r
5143       ChatPopUp(NULL);\r
5144       break;\r
5145 \r
5146     case IDM_CopyPosition:\r
5147       CopyFENToClipboard();\r
5148       break;\r
5149 \r
5150     case IDM_PastePosition:\r
5151       PasteFENFromClipboard();\r
5152       break;\r
5153 \r
5154     case IDM_MailMove:\r
5155       MailMoveEvent();\r
5156       break;\r
5157 \r
5158     case IDM_ReloadCMailMsg:\r
5159       Reset(TRUE, TRUE);\r
5160       ReloadCmailMsgEvent(FALSE);\r
5161       break;\r
5162 \r
5163     case IDM_Minimize:\r
5164       ShowWindow(hwnd, SW_MINIMIZE);\r
5165       break;\r
5166 \r
5167     case IDM_Exit:\r
5168       ExitEvent(0);\r
5169       break;\r
5170 \r
5171     case IDM_MachineWhite:\r
5172       MachineWhiteEvent();\r
5173       /*\r
5174        * refresh the tags dialog only if it's visible\r
5175        */\r
5176       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5177           char *tags;\r
5178           tags = PGNTags(&gameInfo);\r
5179           TagsPopUp(tags, CmailMsg());\r
5180           free(tags);\r
5181       }\r
5182       SAY("computer starts playing white");\r
5183       break;\r
5184 \r
5185     case IDM_MachineBlack:\r
5186       MachineBlackEvent();\r
5187       /*\r
5188        * refresh the tags dialog only if it's visible\r
5189        */\r
5190       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5191           char *tags;\r
5192           tags = PGNTags(&gameInfo);\r
5193           TagsPopUp(tags, CmailMsg());\r
5194           free(tags);\r
5195       }\r
5196       SAY("computer starts playing black");\r
5197       break;\r
5198 \r
5199     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5200       if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
5201       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5202       break;\r
5203 \r
5204     case IDM_TwoMachines:\r
5205       TwoMachinesEvent();\r
5206       /*\r
5207 \r
5208        * refresh the tags dialog only if it's visible\r
5209        */\r
5210       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5211           char *tags;\r
5212           tags = PGNTags(&gameInfo);\r
5213           TagsPopUp(tags, CmailMsg());\r
5214           free(tags);\r
5215       }\r
5216       SAY("computer starts playing both sides");\r
5217       break;\r
5218 \r
5219     case IDM_AnalysisMode:\r
5220       if(AnalyzeModeEvent()) {\r
5221         SAY("analyzing current position");\r
5222       }\r
5223       break;\r
5224 \r
5225     case IDM_AnalyzeFile:\r
5226       AnalyzeFileEvent();\r
5227       break;\r
5228 \r
5229     case IDM_IcsClient:\r
5230       IcsClientEvent();\r
5231       break;\r
5232 \r
5233     case IDM_EditGame:\r
5234     case IDM_EditGame2:\r
5235       EditGameEvent();\r
5236       SAY("edit game");\r
5237       break;\r
5238 \r
5239     case IDM_EditPosition:\r
5240     case IDM_EditPosition2:\r
5241       EditPositionEvent();\r
5242       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5243       break;\r
5244 \r
5245     case IDM_Training:\r
5246       TrainingEvent();\r
5247       break;\r
5248 \r
5249     case IDM_ShowGameList:\r
5250       ShowGameListProc();\r
5251       break;\r
5252 \r
5253     case IDM_EditProgs1:\r
5254       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5255       break;\r
5256 \r
5257     case IDM_LoadProg1:\r
5258      LoadEnginePopUp(hwndMain, 0);\r
5259       break;\r
5260 \r
5261     case IDM_LoadProg2:\r
5262      LoadEnginePopUp(hwndMain, 1);\r
5263       break;\r
5264 \r
5265     case IDM_EditServers:\r
5266       EditTagsPopUp(icsNames, &icsNames);\r
5267       break;\r
5268 \r
5269     case IDM_EditTags:\r
5270     case IDM_Tags:\r
5271       EditTagsProc();\r
5272       break;\r
5273 \r
5274     case IDM_EditBook:\r
5275       EditBookEvent();\r
5276       break;\r
5277 \r
5278     case IDM_EditComment:\r
5279     case IDM_Comment:\r
5280       if (commentUp && editComment) {\r
5281         CommentPopDown();\r
5282       } else {\r
5283         EditCommentEvent();\r
5284       }\r
5285       break;\r
5286 \r
5287     case IDM_Pause:\r
5288       PauseEvent();\r
5289       break;\r
5290 \r
5291     case IDM_Accept:\r
5292       AcceptEvent();\r
5293       break;\r
5294 \r
5295     case IDM_Decline:\r
5296       DeclineEvent();\r
5297       break;\r
5298 \r
5299     case IDM_Rematch:\r
5300 \r
5301       RematchEvent();\r
5302       break;\r
5303 \r
5304     case IDM_CallFlag:\r
5305       CallFlagEvent();\r
5306       break;\r
5307 \r
5308     case IDM_Draw:\r
5309       DrawEvent();\r
5310       break;\r
5311 \r
5312     case IDM_Adjourn:\r
5313       AdjournEvent();\r
5314       break;\r
5315 \r
5316     case IDM_Abort:\r
5317       AbortEvent();\r
5318       break;\r
5319 \r
5320     case IDM_Resign:\r
5321       ResignEvent();\r
5322       break;\r
5323 \r
5324     case IDM_StopObserving:\r
5325       StopObservingEvent();\r
5326       break;\r
5327 \r
5328     case IDM_StopExamining:\r
5329       StopExaminingEvent();\r
5330       break;\r
5331 \r
5332     case IDM_Upload:\r
5333       UploadGameEvent();\r
5334       break;\r
5335 \r
5336     case IDM_TypeInMove:\r
5337       TypeInEvent('\000');\r
5338       break;\r
5339 \r
5340     case IDM_TypeInName:\r
5341       PopUpNameDialog('\000');\r
5342       break;\r
5343 \r
5344     case IDM_Backward:\r
5345       BackwardEvent();\r
5346       SetFocus(hwndMain);\r
5347       break;\r
5348 \r
5349     JAWS_MENU_ITEMS\r
5350 \r
5351     case IDM_Forward:\r
5352       ForwardEvent();\r
5353       SetFocus(hwndMain);\r
5354       break;\r
5355 \r
5356 \r
5357     case IDM_ToStart:\r
5358       ToStartEvent();\r
5359       SetFocus(hwndMain);\r
5360       break;\r
5361 \r
5362     case IDM_ToEnd:\r
5363       ToEndEvent();\r
5364       SetFocus(hwndMain);\r
5365       break;\r
5366 \r
5367     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5368     case OPT_GameListPrev:\r
5369       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5370       break;\r
5371 \r
5372     case IDM_Revert:\r
5373       RevertEvent(FALSE);\r
5374       break;\r
5375 \r
5376     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5377       RevertEvent(TRUE);\r
5378       break;\r
5379 \r
5380     case IDM_TruncateGame:\r
5381       TruncateGameEvent();\r
5382       break;\r
5383 \r
5384     case IDM_MoveNow:\r
5385       MoveNowEvent();\r
5386       break;\r
5387 \r
5388     case IDM_RetractMove:\r
5389       RetractMoveEvent();\r
5390       break;\r
5391 \r
5392     case IDM_FlipView:\r
5393       flipView = !flipView;\r
5394       DrawPosition(FALSE, NULL);\r
5395       break;\r
5396 \r
5397     case IDM_FlipClock:\r
5398       flipClock = !flipClock;\r
5399       DisplayBothClocks();\r
5400       DisplayLogos();\r
5401       break;\r
5402 \r
5403     case IDM_MuteSounds:\r
5404       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5405       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5406                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5407       break;\r
5408 \r
5409     case IDM_GeneralOptions:\r
5410       GeneralOptionsPopup(hwnd);\r
5411       DrawPosition(TRUE, NULL);\r
5412       break;\r
5413 \r
5414     case IDM_BoardOptions:\r
5415       BoardOptionsPopup(hwnd);\r
5416       break;\r
5417 \r
5418     case IDM_ThemeOptions:\r
5419       ThemeOptionsPopup(hwnd);\r
5420       break;\r
5421 \r
5422     case IDM_EnginePlayOptions:\r
5423       EnginePlayOptionsPopup(hwnd);\r
5424       break;\r
5425 \r
5426     case IDM_Engine1Options:\r
5427       EngineOptionsPopup(hwnd, &first);\r
5428       break;\r
5429 \r
5430     case IDM_Engine2Options:\r
5431       savedHwnd = hwnd;\r
5432       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5433       EngineOptionsPopup(hwnd, &second);\r
5434       break;\r
5435 \r
5436     case IDM_OptionsUCI:\r
5437       UciOptionsPopup(hwnd);\r
5438       break;\r
5439 \r
5440     case IDM_Tourney:\r
5441       TourneyPopup(hwnd);\r
5442       break;\r
5443 \r
5444     case IDM_IcsOptions:\r
5445       IcsOptionsPopup(hwnd);\r
5446       break;\r
5447 \r
5448     case IDM_Fonts:\r
5449       FontsOptionsPopup(hwnd);\r
5450       break;\r
5451 \r
5452     case IDM_Sounds:\r
5453       SoundOptionsPopup(hwnd);\r
5454       break;\r
5455 \r
5456     case IDM_CommPort:\r
5457       CommPortOptionsPopup(hwnd);\r
5458       break;\r
5459 \r
5460     case IDM_LoadOptions:\r
5461       LoadOptionsPopup(hwnd);\r
5462       break;\r
5463 \r
5464     case IDM_SaveOptions:\r
5465       SaveOptionsPopup(hwnd);\r
5466       break;\r
5467 \r
5468     case IDM_TimeControl:\r
5469       TimeControlOptionsPopup(hwnd);\r
5470       break;\r
5471 \r
5472     case IDM_SaveSettings:\r
5473       SaveSettings(settingsFileName);\r
5474       break;\r
5475 \r
5476     case IDM_SaveSettingsOnExit:\r
5477       saveSettingsOnExit = !saveSettingsOnExit;\r
5478       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5479                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5480                                          MF_CHECKED : MF_UNCHECKED));\r
5481       break;\r
5482 \r
5483     case IDM_Hint:\r
5484       HintEvent();\r
5485       break;\r
5486 \r
5487     case IDM_Book:\r
5488       BookEvent();\r
5489       break;\r
5490 \r
5491     case IDM_AboutGame:\r
5492       AboutGameEvent();\r
5493       break;\r
5494 \r
5495     case IDM_Debug:\r
5496       appData.debugMode = !appData.debugMode;\r
5497       if (appData.debugMode) {\r
5498         char dir[MSG_SIZ];\r
5499         GetCurrentDirectory(MSG_SIZ, dir);\r
5500         SetCurrentDirectory(installDir);\r
5501         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5502         SetCurrentDirectory(dir);\r
5503         setbuf(debugFP, NULL);\r
5504       } else {\r
5505         fclose(debugFP);\r
5506         debugFP = NULL;\r
5507       }\r
5508       break;\r
5509 \r
5510     case IDM_HELPCONTENTS:\r
5511       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5512           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5513           MessageBox (GetFocus(),\r
5514                     _("Unable to activate help"),\r
5515                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5516       }\r
5517       break;\r
5518 \r
5519     case IDM_HELPSEARCH:\r
5520         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5521             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5522         MessageBox (GetFocus(),\r
5523                     _("Unable to activate help"),\r
5524                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5525       }\r
5526       break;\r
5527 \r
5528     case IDM_HELPHELP:\r
5529       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5530         MessageBox (GetFocus(),\r
5531                     _("Unable to activate help"),\r
5532                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5533       }\r
5534       break;\r
5535 \r
5536     case IDM_ABOUT:\r
5537       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5538       DialogBox(hInst, \r
5539         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5540         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5541       FreeProcInstance(lpProc);\r
5542       break;\r
5543 \r
5544     case IDM_DirectCommand1:\r
5545       AskQuestionEvent(_("Direct Command"),\r
5546                        _("Send to chess program:"), "", "1");\r
5547       break;\r
5548     case IDM_DirectCommand2:\r
5549       AskQuestionEvent(_("Direct Command"),\r
5550                        _("Send to second chess program:"), "", "2");\r
5551       break;\r
5552 \r
5553     case EP_WhitePawn:\r
5554       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5555       fromX = fromY = -1;\r
5556       break;\r
5557 \r
5558     case EP_WhiteKnight:\r
5559       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5560       fromX = fromY = -1;\r
5561       break;\r
5562 \r
5563     case EP_WhiteBishop:\r
5564       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5565       fromX = fromY = -1;\r
5566       break;\r
5567 \r
5568     case EP_WhiteRook:\r
5569       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5570       fromX = fromY = -1;\r
5571       break;\r
5572 \r
5573     case EP_WhiteQueen:\r
5574       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5575       fromX = fromY = -1;\r
5576       break;\r
5577 \r
5578     case EP_WhiteFerz:\r
5579       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5580       fromX = fromY = -1;\r
5581       break;\r
5582 \r
5583     case EP_WhiteWazir:\r
5584       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5585       fromX = fromY = -1;\r
5586       break;\r
5587 \r
5588     case EP_WhiteAlfil:\r
5589       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5590       fromX = fromY = -1;\r
5591       break;\r
5592 \r
5593     case EP_WhiteCannon:\r
5594       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5595       fromX = fromY = -1;\r
5596       break;\r
5597 \r
5598     case EP_WhiteCardinal:\r
5599       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5600       fromX = fromY = -1;\r
5601       break;\r
5602 \r
5603     case EP_WhiteMarshall:\r
5604       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5605       fromX = fromY = -1;\r
5606       break;\r
5607 \r
5608     case EP_WhiteKing:\r
5609       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5610       fromX = fromY = -1;\r
5611       break;\r
5612 \r
5613     case EP_BlackPawn:\r
5614       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5615       fromX = fromY = -1;\r
5616       break;\r
5617 \r
5618     case EP_BlackKnight:\r
5619       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5620       fromX = fromY = -1;\r
5621       break;\r
5622 \r
5623     case EP_BlackBishop:\r
5624       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5625       fromX = fromY = -1;\r
5626       break;\r
5627 \r
5628     case EP_BlackRook:\r
5629       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5630       fromX = fromY = -1;\r
5631       break;\r
5632 \r
5633     case EP_BlackQueen:\r
5634       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5635       fromX = fromY = -1;\r
5636       break;\r
5637 \r
5638     case EP_BlackFerz:\r
5639       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5640       fromX = fromY = -1;\r
5641       break;\r
5642 \r
5643     case EP_BlackWazir:\r
5644       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5645       fromX = fromY = -1;\r
5646       break;\r
5647 \r
5648     case EP_BlackAlfil:\r
5649       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5650       fromX = fromY = -1;\r
5651       break;\r
5652 \r
5653     case EP_BlackCannon:\r
5654       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5655       fromX = fromY = -1;\r
5656       break;\r
5657 \r
5658     case EP_BlackCardinal:\r
5659       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5660       fromX = fromY = -1;\r
5661       break;\r
5662 \r
5663     case EP_BlackMarshall:\r
5664       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5665       fromX = fromY = -1;\r
5666       break;\r
5667 \r
5668     case EP_BlackKing:\r
5669       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5670       fromX = fromY = -1;\r
5671       break;\r
5672 \r
5673     case EP_EmptySquare:\r
5674       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5675       fromX = fromY = -1;\r
5676       break;\r
5677 \r
5678     case EP_ClearBoard:\r
5679       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5680       fromX = fromY = -1;\r
5681       break;\r
5682 \r
5683     case EP_White:\r
5684       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5685       fromX = fromY = -1;\r
5686       break;\r
5687 \r
5688     case EP_Black:\r
5689       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5690       fromX = fromY = -1;\r
5691       break;\r
5692 \r
5693     case EP_Promote:\r
5694       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5695       fromX = fromY = -1;\r
5696       break;\r
5697 \r
5698     case EP_Demote:\r
5699       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5700       fromX = fromY = -1;\r
5701       break;\r
5702 \r
5703     case DP_Pawn:\r
5704       DropMenuEvent(WhitePawn, fromX, fromY);\r
5705       fromX = fromY = -1;\r
5706       break;\r
5707 \r
5708     case DP_Knight:\r
5709       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5710       fromX = fromY = -1;\r
5711       break;\r
5712 \r
5713     case DP_Bishop:\r
5714       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5715       fromX = fromY = -1;\r
5716       break;\r
5717 \r
5718     case DP_Rook:\r
5719       DropMenuEvent(WhiteRook, fromX, fromY);\r
5720       fromX = fromY = -1;\r
5721       break;\r
5722 \r
5723     case DP_Queen:\r
5724       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5725       fromX = fromY = -1;\r
5726       break;\r
5727 \r
5728     case IDM_English:\r
5729       barbaric = 0; appData.language = "";\r
5730       TranslateMenus(0);\r
5731       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5732       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5733       lastChecked = wmId;\r
5734       break;\r
5735 \r
5736     default:\r
5737       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5738           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5739       else\r
5740       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5741           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5742           TranslateMenus(0);\r
5743           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5744           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5745           lastChecked = wmId;\r
5746           break;\r
5747       }\r
5748       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5749     }\r
5750     break;\r
5751 \r
5752   case WM_TIMER:\r
5753     switch (wParam) {\r
5754     case CLOCK_TIMER_ID:\r
5755       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5756       clockTimerEvent = 0;\r
5757       DecrementClocks(); /* call into back end */\r
5758       break;\r
5759     case LOAD_GAME_TIMER_ID:\r
5760       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5761       loadGameTimerEvent = 0;\r
5762       AutoPlayGameLoop(); /* call into back end */\r
5763       break;\r
5764     case ANALYSIS_TIMER_ID:\r
5765       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5766                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5767         AnalysisPeriodicEvent(0);\r
5768       } else {\r
5769         KillTimer(hwnd, analysisTimerEvent);\r
5770         analysisTimerEvent = 0;\r
5771       }\r
5772       break;\r
5773     case DELAYED_TIMER_ID:\r
5774       KillTimer(hwnd, delayedTimerEvent);\r
5775       delayedTimerEvent = 0;\r
5776       delayedTimerCallback();\r
5777       break;\r
5778     }\r
5779     break;\r
5780 \r
5781   case WM_USER_Input:\r
5782     InputEvent(hwnd, message, wParam, lParam);\r
5783     break;\r
5784 \r
5785   /* [AS] Also move "attached" child windows */\r
5786   case WM_WINDOWPOSCHANGING:\r
5787 \r
5788     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5789         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5790 \r
5791         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5792             /* Window is moving */\r
5793             RECT rcMain;\r
5794 \r
5795 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5796             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5797             rcMain.right  = wpMain.x + wpMain.width;\r
5798             rcMain.top    = wpMain.y;\r
5799             rcMain.bottom = wpMain.y + wpMain.height;\r
5800             \r
5801             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5802             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5803             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5804             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5805             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5806             wpMain.x = lpwp->x;\r
5807             wpMain.y = lpwp->y;\r
5808 \r
5809         }\r
5810     }\r
5811     break;\r
5812 \r
5813   /* [AS] Snapping */\r
5814   case WM_ENTERSIZEMOVE:\r
5815     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5816     if (hwnd == hwndMain) {\r
5817       doingSizing = TRUE;\r
5818       lastSizing = 0;\r
5819     }\r
5820     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5821     break;\r
5822 \r
5823   case WM_SIZING:\r
5824     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5825     if (hwnd == hwndMain) {\r
5826       lastSizing = wParam;\r
5827     }\r
5828     break;\r
5829 \r
5830   case WM_MOVING:\r
5831     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5832       return OnMoving( &sd, hwnd, wParam, lParam );\r
5833 \r
5834   case WM_EXITSIZEMOVE:\r
5835     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5836     if (hwnd == hwndMain) {\r
5837       RECT client;\r
5838       doingSizing = FALSE;\r
5839       InvalidateRect(hwnd, &boardRect, FALSE);\r
5840       GetClientRect(hwnd, &client);\r
5841       ResizeBoard(client.right, client.bottom, lastSizing);\r
5842       lastSizing = 0;\r
5843       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5844     }\r
5845     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5846     break;\r
5847 \r
5848   case WM_DESTROY: /* message: window being destroyed */\r
5849     PostQuitMessage(0);\r
5850     break;\r
5851 \r
5852   case WM_CLOSE:\r
5853     if (hwnd == hwndMain) {\r
5854       ExitEvent(0);\r
5855     }\r
5856     break;\r
5857 \r
5858   default:      /* Passes it on if unprocessed */\r
5859     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5860   }\r
5861 \r
5862 \r
5863   return 0;\r
5864 }\r
5865 \r
5866 /*---------------------------------------------------------------------------*\\r
5867  *\r
5868  * Misc utility routines\r
5869  *\r
5870 \*---------------------------------------------------------------------------*/\r
5871 \r
5872 /*\r
5873  * Decent random number generator, at least not as bad as Windows\r
5874  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5875  */\r
5876 unsigned int randstate;\r
5877 \r
5878 int\r
5879 myrandom(void)\r
5880 {\r
5881   randstate = randstate * 1664525 + 1013904223;\r
5882   return (int) randstate & 0x7fffffff;\r
5883 }\r
5884 \r
5885 void\r
5886 mysrandom(unsigned int seed)\r
5887 {\r
5888   randstate = seed;\r
5889 }\r
5890 \r
5891 \r
5892 /* \r
5893  * returns TRUE if user selects a different color, FALSE otherwise \r
5894  */\r
5895 \r
5896 BOOL\r
5897 ChangeColor(HWND hwnd, COLORREF *which)\r
5898 {\r
5899   static BOOL firstTime = TRUE;\r
5900   static DWORD customColors[16];\r
5901   CHOOSECOLOR cc;\r
5902   COLORREF newcolor;\r
5903   int i;\r
5904   ColorClass ccl;\r
5905 \r
5906   if (firstTime) {\r
5907     /* Make initial colors in use available as custom colors */\r
5908     /* Should we put the compiled-in defaults here instead? */\r
5909     i = 0;\r
5910     customColors[i++] = lightSquareColor & 0xffffff;\r
5911     customColors[i++] = darkSquareColor & 0xffffff;\r
5912     customColors[i++] = whitePieceColor & 0xffffff;\r
5913     customColors[i++] = blackPieceColor & 0xffffff;\r
5914     customColors[i++] = highlightSquareColor & 0xffffff;\r
5915     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5916 \r
5917     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5918       customColors[i++] = textAttribs[ccl].color;\r
5919     }\r
5920     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5921     firstTime = FALSE;\r
5922   }\r
5923 \r
5924   cc.lStructSize = sizeof(cc);\r
5925   cc.hwndOwner = hwnd;\r
5926   cc.hInstance = NULL;\r
5927   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5928   cc.lpCustColors = (LPDWORD) customColors;\r
5929   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5930 \r
5931   if (!ChooseColor(&cc)) return FALSE;\r
5932 \r
5933   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5934   if (newcolor == *which) return FALSE;\r
5935   *which = newcolor;\r
5936   return TRUE;\r
5937 \r
5938   /*\r
5939   InitDrawingColors();\r
5940   InvalidateRect(hwnd, &boardRect, FALSE);\r
5941   */\r
5942 }\r
5943 \r
5944 BOOLEAN\r
5945 MyLoadSound(MySound *ms)\r
5946 {\r
5947   BOOL ok = FALSE;\r
5948   struct stat st;\r
5949   FILE *f;\r
5950 \r
5951   if (ms->data && ms->flag) free(ms->data);\r
5952   ms->data = NULL;\r
5953 \r
5954   switch (ms->name[0]) {\r
5955   case NULLCHAR:\r
5956     /* Silence */\r
5957     ok = TRUE;\r
5958     break;\r
5959   case '$':\r
5960     /* System sound from Control Panel.  Don't preload here. */\r
5961     ok = TRUE;\r
5962     break;\r
5963   case '!':\r
5964     if (ms->name[1] == NULLCHAR) {\r
5965       /* "!" alone = silence */\r
5966       ok = TRUE;\r
5967     } else {\r
5968       /* Builtin wave resource.  Error if not found. */\r
5969       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5970       if (h == NULL) break;\r
5971       ms->data = (void *)LoadResource(hInst, h);\r
5972       ms->flag = 0; // not maloced, so cannot be freed!\r
5973       if (h == NULL) break;\r
5974       ok = TRUE;\r
5975     }\r
5976     break;\r
5977   default:\r
5978     /* .wav file.  Error if not found. */\r
5979     f = fopen(ms->name, "rb");\r
5980     if (f == NULL) break;\r
5981     if (fstat(fileno(f), &st) < 0) break;\r
5982     ms->data = malloc(st.st_size);\r
5983     ms->flag = 1;\r
5984     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5985     fclose(f);\r
5986     ok = TRUE;\r
5987     break;\r
5988   }\r
5989   if (!ok) {\r
5990     char buf[MSG_SIZ];\r
5991       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5992     DisplayError(buf, GetLastError());\r
5993   }\r
5994   return ok;\r
5995 }\r
5996 \r
5997 BOOLEAN\r
5998 MyPlaySound(MySound *ms)\r
5999 {\r
6000   BOOLEAN ok = FALSE;\r
6001 \r
6002   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6003   switch (ms->name[0]) {\r
6004   case NULLCHAR:\r
6005         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6006     /* Silence */\r
6007     ok = TRUE;\r
6008     break;\r
6009   case '$':\r
6010     /* System sound from Control Panel (deprecated feature).\r
6011        "$" alone or an unset sound name gets default beep (still in use). */\r
6012     if (ms->name[1]) {\r
6013       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6014     }\r
6015     if (!ok) ok = MessageBeep(MB_OK);\r
6016     break; \r
6017   case '!':\r
6018     /* Builtin wave resource, or "!" alone for silence */\r
6019     if (ms->name[1]) {\r
6020       if (ms->data == NULL) return FALSE;\r
6021       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6022     } else {\r
6023       ok = TRUE;\r
6024     }\r
6025     break;\r
6026   default:\r
6027     /* .wav file.  Error if not found. */\r
6028     if (ms->data == NULL) return FALSE;\r
6029     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6030     break;\r
6031   }\r
6032   /* Don't print an error: this can happen innocently if the sound driver\r
6033      is busy; for instance, if another instance of WinBoard is playing\r
6034      a sound at about the same time. */\r
6035   return ok;\r
6036 }\r
6037 \r
6038 \r
6039 LRESULT CALLBACK\r
6040 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6041 {\r
6042   BOOL ok;\r
6043   OPENFILENAME *ofn;\r
6044   static UINT *number; /* gross that this is static */\r
6045 \r
6046   switch (message) {\r
6047   case WM_INITDIALOG: /* message: initialize dialog box */\r
6048     /* Center the dialog over the application window */\r
6049     ofn = (OPENFILENAME *) lParam;\r
6050     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6051       number = (UINT *) ofn->lCustData;\r
6052       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6053     } else {\r
6054       number = NULL;\r
6055     }\r
6056     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6057     Translate(hDlg, 1536);\r
6058     return FALSE;  /* Allow for further processing */\r
6059 \r
6060   case WM_COMMAND:\r
6061     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6062       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6063     }\r
6064     return FALSE;  /* Allow for further processing */\r
6065   }\r
6066   return FALSE;\r
6067 }\r
6068 \r
6069 UINT APIENTRY\r
6070 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6071 {\r
6072   static UINT *number;\r
6073   OPENFILENAME *ofname;\r
6074   OFNOTIFY *ofnot;\r
6075   switch (uiMsg) {\r
6076   case WM_INITDIALOG:\r
6077     Translate(hdlg, DLG_IndexNumber);\r
6078     ofname = (OPENFILENAME *)lParam;\r
6079     number = (UINT *)(ofname->lCustData);\r
6080     break;\r
6081   case WM_NOTIFY:\r
6082     ofnot = (OFNOTIFY *)lParam;\r
6083     if (ofnot->hdr.code == CDN_FILEOK) {\r
6084       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6085     }\r
6086     break;\r
6087   }\r
6088   return 0;\r
6089 }\r
6090 \r
6091 \r
6092 FILE *\r
6093 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6094                char *nameFilt, char *dlgTitle, UINT *number,\r
6095                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6096 {\r
6097   OPENFILENAME openFileName;\r
6098   char buf1[MSG_SIZ];\r
6099   FILE *f;\r
6100 \r
6101   if (fileName == NULL) fileName = buf1;\r
6102   if (defName == NULL) {\r
6103     safeStrCpy(fileName, "*.", 3 );\r
6104     strcat(fileName, defExt);\r
6105   } else {\r
6106     safeStrCpy(fileName, defName, MSG_SIZ );\r
6107   }\r
6108     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
6109   if (number) *number = 0;\r
6110 \r
6111   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6112   openFileName.hwndOwner         = hwnd;\r
6113   openFileName.hInstance         = (HANDLE) hInst;\r
6114   openFileName.lpstrFilter       = nameFilt;\r
6115   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6116   openFileName.nMaxCustFilter    = 0L;\r
6117   openFileName.nFilterIndex      = 1L;\r
6118   openFileName.lpstrFile         = fileName;\r
6119   openFileName.nMaxFile          = MSG_SIZ;\r
6120   openFileName.lpstrFileTitle    = fileTitle;\r
6121   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6122   openFileName.lpstrInitialDir   = NULL;\r
6123   openFileName.lpstrTitle        = dlgTitle;\r
6124   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6125     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6126     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6127     | (oldDialog ? 0 : OFN_EXPLORER);\r
6128   openFileName.nFileOffset       = 0;\r
6129   openFileName.nFileExtension    = 0;\r
6130   openFileName.lpstrDefExt       = defExt;\r
6131   openFileName.lCustData         = (LONG) number;\r
6132   openFileName.lpfnHook          = oldDialog ?\r
6133     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6134   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6135 \r
6136   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6137                         GetOpenFileName(&openFileName)) {\r
6138     /* open the file */\r
6139     f = fopen(openFileName.lpstrFile, write);\r
6140     if (f == NULL) {\r
6141       MessageBox(hwnd, _("File open failed"), NULL,\r
6142                  MB_OK|MB_ICONEXCLAMATION);\r
6143       return NULL;\r
6144     }\r
6145   } else {\r
6146     int err = CommDlgExtendedError();\r
6147     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6148     return FALSE;\r
6149   }\r
6150   return f;\r
6151 }\r
6152 \r
6153 \r
6154 \r
6155 VOID APIENTRY\r
6156 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6157 {\r
6158   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6159 \r
6160   /*\r
6161    * Get the first pop-up menu in the menu template. This is the\r
6162    * menu that TrackPopupMenu displays.\r
6163    */\r
6164   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6165   TranslateOneMenu(10, hmenuTrackPopup);\r
6166 \r
6167   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6168 \r
6169   /*\r
6170    * TrackPopup uses screen coordinates, so convert the\r
6171    * coordinates of the mouse click to screen coordinates.\r
6172    */\r
6173   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6174 \r
6175   /* Draw and track the floating pop-up menu. */\r
6176   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6177                  pt.x, pt.y, 0, hwnd, NULL);\r
6178 \r
6179   /* Destroy the menu.*/\r
6180   DestroyMenu(hmenu);\r
6181 }\r
6182    \r
6183 typedef struct {\r
6184   HWND hDlg, hText;\r
6185   int sizeX, sizeY, newSizeX, newSizeY;\r
6186   HDWP hdwp;\r
6187 } ResizeEditPlusButtonsClosure;\r
6188 \r
6189 BOOL CALLBACK\r
6190 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6191 {\r
6192   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6193   RECT rect;\r
6194   POINT pt;\r
6195 \r
6196   if (hChild == cl->hText) return TRUE;\r
6197   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6198   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6199   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6200   ScreenToClient(cl->hDlg, &pt);\r
6201   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6202     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6203   return TRUE;\r
6204 }\r
6205 \r
6206 /* Resize a dialog that has a (rich) edit field filling most of\r
6207    the top, with a row of buttons below */\r
6208 VOID\r
6209 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6210 {\r
6211   RECT rectText;\r
6212   int newTextHeight, newTextWidth;\r
6213   ResizeEditPlusButtonsClosure cl;\r
6214   \r
6215   /*if (IsIconic(hDlg)) return;*/\r
6216   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6217   \r
6218   cl.hdwp = BeginDeferWindowPos(8);\r
6219 \r
6220   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6221   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6222   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6223   if (newTextHeight < 0) {\r
6224     newSizeY += -newTextHeight;\r
6225     newTextHeight = 0;\r
6226   }\r
6227   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6228     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6229 \r
6230   cl.hDlg = hDlg;\r
6231   cl.hText = hText;\r
6232   cl.sizeX = sizeX;\r
6233   cl.sizeY = sizeY;\r
6234   cl.newSizeX = newSizeX;\r
6235   cl.newSizeY = newSizeY;\r
6236   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6237 \r
6238   EndDeferWindowPos(cl.hdwp);\r
6239 }\r
6240 \r
6241 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6242 {\r
6243     RECT    rChild, rParent;\r
6244     int     wChild, hChild, wParent, hParent;\r
6245     int     wScreen, hScreen, xNew, yNew;\r
6246     HDC     hdc;\r
6247 \r
6248     /* Get the Height and Width of the child window */\r
6249     GetWindowRect (hwndChild, &rChild);\r
6250     wChild = rChild.right - rChild.left;\r
6251     hChild = rChild.bottom - rChild.top;\r
6252 \r
6253     /* Get the Height and Width of the parent window */\r
6254     GetWindowRect (hwndParent, &rParent);\r
6255     wParent = rParent.right - rParent.left;\r
6256     hParent = rParent.bottom - rParent.top;\r
6257 \r
6258     /* Get the display limits */\r
6259     hdc = GetDC (hwndChild);\r
6260     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6261     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6262     ReleaseDC(hwndChild, hdc);\r
6263 \r
6264     /* Calculate new X position, then adjust for screen */\r
6265     xNew = rParent.left + ((wParent - wChild) /2);\r
6266     if (xNew < 0) {\r
6267         xNew = 0;\r
6268     } else if ((xNew+wChild) > wScreen) {\r
6269         xNew = wScreen - wChild;\r
6270     }\r
6271 \r
6272     /* Calculate new Y position, then adjust for screen */\r
6273     if( mode == 0 ) {\r
6274         yNew = rParent.top  + ((hParent - hChild) /2);\r
6275     }\r
6276     else {\r
6277         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6278     }\r
6279 \r
6280     if (yNew < 0) {\r
6281         yNew = 0;\r
6282     } else if ((yNew+hChild) > hScreen) {\r
6283         yNew = hScreen - hChild;\r
6284     }\r
6285 \r
6286     /* Set it, and return */\r
6287     return SetWindowPos (hwndChild, NULL,\r
6288                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6289 }\r
6290 \r
6291 /* Center one window over another */\r
6292 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6293 {\r
6294     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6295 }\r
6296 \r
6297 /*---------------------------------------------------------------------------*\\r
6298  *\r
6299  * Startup Dialog functions\r
6300  *\r
6301 \*---------------------------------------------------------------------------*/\r
6302 void\r
6303 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6304 {\r
6305   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6306 \r
6307   while (*cd != NULL) {\r
6308     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6309     cd++;\r
6310   }\r
6311 }\r
6312 \r
6313 void\r
6314 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6315 {\r
6316   char buf1[MAX_ARG_LEN];\r
6317   int len;\r
6318 \r
6319   if (str[0] == '@') {\r
6320     FILE* f = fopen(str + 1, "r");\r
6321     if (f == NULL) {\r
6322       DisplayFatalError(str + 1, errno, 2);\r
6323       return;\r
6324     }\r
6325     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6326     fclose(f);\r
6327     buf1[len] = NULLCHAR;\r
6328     str = buf1;\r
6329   }\r
6330 \r
6331 \r
6332 \r
6333   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6334 \r
6335   for (;;) {\r
6336     char buf[MSG_SIZ];\r
6337     char *end = strchr(str, '\n');\r
6338     if (end == NULL) return;\r
6339     memcpy(buf, str, end - str);\r
6340     buf[end - str] = NULLCHAR;\r
6341     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6342     str = end + 1;\r
6343   }\r
6344 }\r
6345 \r
6346 void\r
6347 SetStartupDialogEnables(HWND hDlg)\r
6348 {\r
6349   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6350     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6351     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6352   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6353     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6354   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6355     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6356   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6357     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6358   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6359     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6360     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6361     IsDlgButtonChecked(hDlg, OPT_View));\r
6362 }\r
6363 \r
6364 char *\r
6365 QuoteForFilename(char *filename)\r
6366 {\r
6367   int dquote, space;\r
6368   dquote = strchr(filename, '"') != NULL;\r
6369   space = strchr(filename, ' ') != NULL;\r
6370   if (dquote || space) {\r
6371     if (dquote) {\r
6372       return "'";\r
6373     } else {\r
6374       return "\"";\r
6375     }\r
6376   } else {\r
6377     return "";\r
6378   }\r
6379 }\r
6380 \r
6381 VOID\r
6382 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6383 {\r
6384   char buf[MSG_SIZ];\r
6385   char *q;\r
6386 \r
6387   InitComboStringsFromOption(hwndCombo, nthnames);\r
6388   q = QuoteForFilename(nthcp);\r
6389     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6390   if (*nthdir != NULLCHAR) {\r
6391     q = QuoteForFilename(nthdir);\r
6392       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6393   }\r
6394   if (*nthcp == NULLCHAR) {\r
6395     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6396   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6397     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6398     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6399   }\r
6400 }\r
6401 \r
6402 LRESULT CALLBACK\r
6403 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6404 {\r
6405   char buf[MSG_SIZ];\r
6406   HANDLE hwndCombo;\r
6407   char *p;\r
6408 \r
6409   switch (message) {\r
6410   case WM_INITDIALOG:\r
6411     /* Center the dialog */\r
6412     CenterWindow (hDlg, GetDesktopWindow());\r
6413     Translate(hDlg, DLG_Startup);\r
6414     /* Initialize the dialog items */\r
6415     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6416                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6417                   firstChessProgramNames);\r
6418     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6419                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6420                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6421     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6422     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6423       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6424     if (*appData.icsHelper != NULLCHAR) {\r
6425       char *q = QuoteForFilename(appData.icsHelper);\r
6426       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6427     }\r
6428     if (*appData.icsHost == NULLCHAR) {\r
6429       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6430       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6431     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6432       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6433       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6434     }\r
6435 \r
6436     if (appData.icsActive) {\r
6437       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6438     }\r
6439     else if (appData.noChessProgram) {\r
6440       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6441     }\r
6442     else {\r
6443       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6444     }\r
6445 \r
6446     SetStartupDialogEnables(hDlg);\r
6447     return TRUE;\r
6448 \r
6449   case WM_COMMAND:\r
6450     switch (LOWORD(wParam)) {\r
6451     case IDOK:\r
6452       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6453         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6454         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6455         p = buf;\r
6456         currentEngine[0] = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6457         ParseArgs(StringGet, &p);\r
6458         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6459         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6460         p = buf;\r
6461         currentEngine[1] = strdup(p+5); // [HGM] also remember engine line of 2nd for saving its settings\r
6462         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6463         ParseArgs(StringGet, &p);\r
6464         SwapEngines(singleList); // ... and then make it 'second'\r
6465 \r
6466         appData.noChessProgram = FALSE;\r
6467         appData.icsActive = FALSE;\r
6468       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6469         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6470         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6471         p = buf;\r
6472         ParseArgs(StringGet, &p);\r
6473         if (appData.zippyPlay) {\r
6474           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6475           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6476           p = buf;\r
6477           ParseArgs(StringGet, &p);\r
6478         }\r
6479       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6480         appData.noChessProgram = TRUE;\r
6481         appData.icsActive = FALSE;\r
6482       } else {\r
6483         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6484                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6485         return TRUE;\r
6486       }\r
6487       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6488         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6489         p = buf;\r
6490         ParseArgs(StringGet, &p);\r
6491       }\r
6492       EndDialog(hDlg, TRUE);\r
6493       return TRUE;\r
6494 \r
6495     case IDCANCEL:\r
6496       ExitEvent(0);\r
6497       return TRUE;\r
6498 \r
6499     case IDM_HELPCONTENTS:\r
6500       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6501         MessageBox (GetFocus(),\r
6502                     _("Unable to activate help"),\r
6503                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6504       }\r
6505       break;\r
6506 \r
6507     default:\r
6508       SetStartupDialogEnables(hDlg);\r
6509       break;\r
6510     }\r
6511     break;\r
6512   }\r
6513   return FALSE;\r
6514 }\r
6515 \r
6516 /*---------------------------------------------------------------------------*\\r
6517  *\r
6518  * About box dialog functions\r
6519  *\r
6520 \*---------------------------------------------------------------------------*/\r
6521 \r
6522 /* Process messages for "About" dialog box */\r
6523 LRESULT CALLBACK\r
6524 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6525 {\r
6526   switch (message) {\r
6527   case WM_INITDIALOG: /* message: initialize dialog box */\r
6528     /* Center the dialog over the application window */\r
6529     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6530     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6531     Translate(hDlg, ABOUTBOX);\r
6532     JAWS_COPYRIGHT\r
6533     return (TRUE);\r
6534 \r
6535   case WM_COMMAND: /* message: received a command */\r
6536     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6537         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6538       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6539       return (TRUE);\r
6540     }\r
6541     break;\r
6542   }\r
6543   return (FALSE);\r
6544 }\r
6545 \r
6546 /*---------------------------------------------------------------------------*\\r
6547  *\r
6548  * Comment Dialog functions\r
6549  *\r
6550 \*---------------------------------------------------------------------------*/\r
6551 \r
6552 LRESULT CALLBACK\r
6553 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6554 {\r
6555   static HANDLE hwndText = NULL;\r
6556   int len, newSizeX, newSizeY;\r
6557   static int sizeX, sizeY;\r
6558   char *str;\r
6559   RECT rect;\r
6560   MINMAXINFO *mmi;\r
6561 \r
6562   switch (message) {\r
6563   case WM_INITDIALOG: /* message: initialize dialog box */\r
6564     /* Initialize the dialog items */\r
6565     Translate(hDlg, DLG_EditComment);\r
6566     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6567     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6568     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6569     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6570     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6571     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6572     SetWindowText(hDlg, commentTitle);\r
6573     if (editComment) {\r
6574       SetFocus(hwndText);\r
6575     } else {\r
6576       SetFocus(GetDlgItem(hDlg, IDOK));\r
6577     }\r
6578     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6579                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6580                 MAKELPARAM(FALSE, 0));\r
6581     /* Size and position the dialog */\r
6582     if (!commentDialog) {\r
6583       commentDialog = hDlg;\r
6584       GetClientRect(hDlg, &rect);\r
6585       sizeX = rect.right;\r
6586       sizeY = rect.bottom;\r
6587       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6588           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6589         WINDOWPLACEMENT wp;\r
6590         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6591         wp.length = sizeof(WINDOWPLACEMENT);\r
6592         wp.flags = 0;\r
6593         wp.showCmd = SW_SHOW;\r
6594         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6595         wp.rcNormalPosition.left = wpComment.x;\r
6596         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6597         wp.rcNormalPosition.top = wpComment.y;\r
6598         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6599         SetWindowPlacement(hDlg, &wp);\r
6600 \r
6601         GetClientRect(hDlg, &rect);\r
6602         newSizeX = rect.right;\r
6603         newSizeY = rect.bottom;\r
6604         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6605                               newSizeX, newSizeY);\r
6606         sizeX = newSizeX;\r
6607         sizeY = newSizeY;\r
6608       }\r
6609     }\r
6610     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6611     return FALSE;\r
6612 \r
6613   case WM_COMMAND: /* message: received a command */\r
6614     switch (LOWORD(wParam)) {\r
6615     case IDOK:\r
6616       if (editComment) {\r
6617         char *p, *q;\r
6618         /* Read changed options from the dialog box */\r
6619         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6620         len = GetWindowTextLength(hwndText);\r
6621         str = (char *) malloc(len + 1);\r
6622         GetWindowText(hwndText, str, len + 1);\r
6623         p = q = str;\r
6624         while (*q) {\r
6625           if (*q == '\r')\r
6626             q++;\r
6627           else\r
6628             *p++ = *q++;\r
6629         }\r
6630         *p = NULLCHAR;\r
6631         ReplaceComment(commentIndex, str);\r
6632         free(str);\r
6633       }\r
6634       CommentPopDown();\r
6635       return TRUE;\r
6636 \r
6637     case IDCANCEL:\r
6638     case OPT_CancelComment:\r
6639       CommentPopDown();\r
6640       return TRUE;\r
6641 \r
6642     case OPT_ClearComment:\r
6643       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6644       break;\r
6645 \r
6646     case OPT_EditComment:\r
6647       EditCommentEvent();\r
6648       return TRUE;\r
6649 \r
6650     default:\r
6651       break;\r
6652     }\r
6653     break;\r
6654 \r
6655   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6656         if( wParam == OPT_CommentText ) {\r
6657             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6658 \r
6659             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6660                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6661                 POINTL pt;\r
6662                 LRESULT index;\r
6663 \r
6664                 pt.x = LOWORD( lpMF->lParam );\r
6665                 pt.y = HIWORD( lpMF->lParam );\r
6666 \r
6667                 if(lpMF->msg == WM_CHAR) {\r
6668                         CHARRANGE sel;\r
6669                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6670                         index = sel.cpMin;\r
6671                 } else\r
6672                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6673 \r
6674                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6675                 len = GetWindowTextLength(hwndText);\r
6676                 str = (char *) malloc(len + 1);\r
6677                 GetWindowText(hwndText, str, len + 1);\r
6678                 ReplaceComment(commentIndex, str);\r
6679                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6680                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6681                 free(str);\r
6682 \r
6683                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6684                 lpMF->msg = WM_USER;\r
6685 \r
6686                 return TRUE;\r
6687             }\r
6688         }\r
6689         break;\r
6690 \r
6691   case WM_SIZE:\r
6692     newSizeX = LOWORD(lParam);\r
6693     newSizeY = HIWORD(lParam);\r
6694     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6695     sizeX = newSizeX;\r
6696     sizeY = newSizeY;\r
6697     break;\r
6698 \r
6699   case WM_GETMINMAXINFO:\r
6700     /* Prevent resizing window too small */\r
6701     mmi = (MINMAXINFO *) lParam;\r
6702     mmi->ptMinTrackSize.x = 100;\r
6703     mmi->ptMinTrackSize.y = 100;\r
6704     break;\r
6705   }\r
6706   return FALSE;\r
6707 }\r
6708 \r
6709 VOID\r
6710 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6711 {\r
6712   FARPROC lpProc;\r
6713   char *p, *q;\r
6714 \r
6715   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6716 \r
6717   if (str == NULL) str = "";\r
6718   p = (char *) malloc(2 * strlen(str) + 2);\r
6719   q = p;\r
6720   while (*str) {\r
6721     if (*str == '\n') *q++ = '\r';\r
6722     *q++ = *str++;\r
6723   }\r
6724   *q = NULLCHAR;\r
6725   if (commentText != NULL) free(commentText);\r
6726 \r
6727   commentIndex = index;\r
6728   commentTitle = title;\r
6729   commentText = p;\r
6730   editComment = edit;\r
6731 \r
6732   if (commentDialog) {\r
6733     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6734     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6735   } else {\r
6736     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6737     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6738                  hwndMain, (DLGPROC)lpProc);\r
6739     FreeProcInstance(lpProc);\r
6740   }\r
6741   commentUp = TRUE;\r
6742 }\r
6743 \r
6744 \r
6745 /*---------------------------------------------------------------------------*\\r
6746  *\r
6747  * Type-in move dialog functions\r
6748  * \r
6749 \*---------------------------------------------------------------------------*/\r
6750 \r
6751 LRESULT CALLBACK\r
6752 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6753 {\r
6754   char move[MSG_SIZ];\r
6755   HWND hInput;\r
6756 \r
6757   switch (message) {\r
6758   case WM_INITDIALOG:\r
6759     move[0] = (char) lParam;\r
6760     move[1] = NULLCHAR;\r
6761     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6762     Translate(hDlg, DLG_TypeInMove);\r
6763     hInput = GetDlgItem(hDlg, OPT_Move);\r
6764     SetWindowText(hInput, move);\r
6765     SetFocus(hInput);\r
6766     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6767     return FALSE;\r
6768 \r
6769   case WM_COMMAND:\r
6770     switch (LOWORD(wParam)) {\r
6771     case IDOK:\r
6772 \r
6773       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6774       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6775       TypeInDoneEvent(move);\r
6776       EndDialog(hDlg, TRUE);\r
6777       return TRUE;\r
6778     case IDCANCEL:\r
6779       EndDialog(hDlg, FALSE);\r
6780       return TRUE;\r
6781     default:\r
6782       break;\r
6783     }\r
6784     break;\r
6785   }\r
6786   return FALSE;\r
6787 }\r
6788 \r
6789 VOID\r
6790 PopUpMoveDialog(char firstchar)\r
6791 {\r
6792     FARPROC lpProc;\r
6793 \r
6794       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6795       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6796         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6797       FreeProcInstance(lpProc);\r
6798 }\r
6799 \r
6800 /*---------------------------------------------------------------------------*\\r
6801  *\r
6802  * Type-in name dialog functions\r
6803  * \r
6804 \*---------------------------------------------------------------------------*/\r
6805 \r
6806 LRESULT CALLBACK\r
6807 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6808 {\r
6809   char move[MSG_SIZ];\r
6810   HWND hInput;\r
6811 \r
6812   switch (message) {\r
6813   case WM_INITDIALOG:\r
6814     move[0] = (char) lParam;\r
6815     move[1] = NULLCHAR;\r
6816     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6817     Translate(hDlg, DLG_TypeInName);\r
6818     hInput = GetDlgItem(hDlg, OPT_Name);\r
6819     SetWindowText(hInput, move);\r
6820     SetFocus(hInput);\r
6821     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6822     return FALSE;\r
6823 \r
6824   case WM_COMMAND:\r
6825     switch (LOWORD(wParam)) {\r
6826     case IDOK:\r
6827       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6828       appData.userName = strdup(move);\r
6829       SetUserLogo(); DisplayLogos();\r
6830       SetGameInfo();\r
6831       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6832         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6833         DisplayTitle(move);\r
6834       }\r
6835 \r
6836 \r
6837       EndDialog(hDlg, TRUE);\r
6838       return TRUE;\r
6839     case IDCANCEL:\r
6840       EndDialog(hDlg, FALSE);\r
6841       return TRUE;\r
6842     default:\r
6843       break;\r
6844     }\r
6845     break;\r
6846   }\r
6847   return FALSE;\r
6848 }\r
6849 \r
6850 VOID\r
6851 PopUpNameDialog(char firstchar)\r
6852 {\r
6853     FARPROC lpProc;\r
6854     \r
6855       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6856       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6857         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6858       FreeProcInstance(lpProc);\r
6859 }\r
6860 \r
6861 /*---------------------------------------------------------------------------*\\r
6862  *\r
6863  *  Error dialogs\r
6864  * \r
6865 \*---------------------------------------------------------------------------*/\r
6866 \r
6867 /* Nonmodal error box */\r
6868 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6869                              WPARAM wParam, LPARAM lParam);\r
6870 \r
6871 VOID\r
6872 ErrorPopUp(char *title, char *content)\r
6873 {\r
6874   FARPROC lpProc;\r
6875   char *p, *q;\r
6876   BOOLEAN modal = hwndMain == NULL;\r
6877 \r
6878   p = content;\r
6879   q = errorMessage;\r
6880   while (*p) {\r
6881     if (*p == '\n') {\r
6882       if (modal) {\r
6883         *q++ = ' ';\r
6884         p++;\r
6885       } else {\r
6886         *q++ = '\r';\r
6887         *q++ = *p++;\r
6888       }\r
6889     } else {\r
6890       *q++ = *p++;\r
6891     }\r
6892   }\r
6893   *q = NULLCHAR;\r
6894   strncpy(errorTitle, title, sizeof(errorTitle));\r
6895   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6896   \r
6897   if (modal) {\r
6898     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6899   } else {\r
6900     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6901     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6902                  hwndMain, (DLGPROC)lpProc);\r
6903     FreeProcInstance(lpProc);\r
6904   }\r
6905 }\r
6906 \r
6907 VOID\r
6908 ErrorPopDown()\r
6909 {\r
6910   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6911   if (errorDialog == NULL) return;\r
6912   DestroyWindow(errorDialog);\r
6913   errorDialog = NULL;\r
6914   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6915 }\r
6916 \r
6917 LRESULT CALLBACK\r
6918 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6919 {\r
6920   RECT rChild;\r
6921 \r
6922   switch (message) {\r
6923   case WM_INITDIALOG:\r
6924     GetWindowRect(hDlg, &rChild);\r
6925 \r
6926     /*\r
6927     SetWindowPos(hDlg, NULL, rChild.left,\r
6928       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6929       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6930     */\r
6931 \r
6932     /* \r
6933         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6934         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6935         and it doesn't work when you resize the dialog.\r
6936         For now, just give it a default position.\r
6937     */\r
6938     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6939     Translate(hDlg, DLG_Error);\r
6940 \r
6941     errorDialog = hDlg;\r
6942     SetWindowText(hDlg, errorTitle);\r
6943     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6944     return FALSE;\r
6945 \r
6946   case WM_COMMAND:\r
6947     switch (LOWORD(wParam)) {\r
6948     case IDOK:\r
6949     case IDCANCEL:\r
6950       if (errorDialog == hDlg) errorDialog = NULL;\r
6951       DestroyWindow(hDlg);\r
6952       return TRUE;\r
6953 \r
6954     default:\r
6955       break;\r
6956     }\r
6957     break;\r
6958   }\r
6959   return FALSE;\r
6960 }\r
6961 \r
6962 #ifdef GOTHIC\r
6963 HWND gothicDialog = NULL;\r
6964 \r
6965 LRESULT CALLBACK\r
6966 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6967 {\r
6968   RECT rChild;\r
6969   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6970 \r
6971   switch (message) {\r
6972   case WM_INITDIALOG:\r
6973     GetWindowRect(hDlg, &rChild);\r
6974 \r
6975     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6976                                                              SWP_NOZORDER);\r
6977 \r
6978     /* \r
6979         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6980         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6981         and it doesn't work when you resize the dialog.\r
6982         For now, just give it a default position.\r
6983     */\r
6984     gothicDialog = hDlg;\r
6985     SetWindowText(hDlg, errorTitle);\r
6986     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6987     return FALSE;\r
6988 \r
6989   case WM_COMMAND:\r
6990     switch (LOWORD(wParam)) {\r
6991     case IDOK:\r
6992     case IDCANCEL:\r
6993       if (errorDialog == hDlg) errorDialog = NULL;\r
6994       DestroyWindow(hDlg);\r
6995       return TRUE;\r
6996 \r
6997     default:\r
6998       break;\r
6999     }\r
7000     break;\r
7001   }\r
7002   return FALSE;\r
7003 }\r
7004 \r
7005 VOID\r
7006 GothicPopUp(char *title, VariantClass variant)\r
7007 {\r
7008   FARPROC lpProc;\r
7009   static char *lastTitle;\r
7010 \r
7011   strncpy(errorTitle, title, sizeof(errorTitle));\r
7012   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7013 \r
7014   if(lastTitle != title && gothicDialog != NULL) {\r
7015     DestroyWindow(gothicDialog);\r
7016     gothicDialog = NULL;\r
7017   }\r
7018   if(variant != VariantNormal && gothicDialog == NULL) {\r
7019     title = lastTitle;\r
7020     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7021     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7022                  hwndMain, (DLGPROC)lpProc);\r
7023     FreeProcInstance(lpProc);\r
7024   }\r
7025 }\r
7026 #endif\r
7027 \r
7028 /*---------------------------------------------------------------------------*\\r
7029  *\r
7030  *  Ics Interaction console functions\r
7031  *\r
7032 \*---------------------------------------------------------------------------*/\r
7033 \r
7034 #define HISTORY_SIZE 64\r
7035 static char *history[HISTORY_SIZE];\r
7036 int histIn = 0, histP = 0;\r
7037 \r
7038 \r
7039 VOID\r
7040 SaveInHistory(char *cmd)\r
7041 {\r
7042   if (history[histIn] != NULL) {\r
7043     free(history[histIn]);\r
7044     history[histIn] = NULL;\r
7045   }\r
7046   if (*cmd == NULLCHAR) return;\r
7047   history[histIn] = StrSave(cmd);\r
7048   histIn = (histIn + 1) % HISTORY_SIZE;\r
7049   if (history[histIn] != NULL) {\r
7050     free(history[histIn]);\r
7051 \r
7052     history[histIn] = NULL;\r
7053   }\r
7054   histP = histIn;\r
7055 }\r
7056 \r
7057 char *\r
7058 PrevInHistory(char *cmd)\r
7059 {\r
7060   int newhp;\r
7061   if (histP == histIn) {\r
7062     if (history[histIn] != NULL) free(history[histIn]);\r
7063     history[histIn] = StrSave(cmd);\r
7064   }\r
7065   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7066   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7067   histP = newhp;\r
7068   return history[histP];\r
7069 }\r
7070 \r
7071 char *\r
7072 NextInHistory()\r
7073 {\r
7074   if (histP == histIn) return NULL;\r
7075   histP = (histP + 1) % HISTORY_SIZE;\r
7076   return history[histP];   \r
7077 }\r
7078 \r
7079 HMENU\r
7080 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7081 {\r
7082   HMENU hmenu, h;\r
7083   int i = 0;\r
7084   hmenu = LoadMenu(hInst, "TextMenu");\r
7085   h = GetSubMenu(hmenu, 0);\r
7086   while (e->item) {\r
7087     if (strcmp(e->item, "-") == 0) {\r
7088       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7089     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
7090       int flags = MF_STRING, j = 0;\r
7091       if (e->item[0] == '|') {\r
7092         flags |= MF_MENUBARBREAK;\r
7093         j++;\r
7094       }\r
7095       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
7096       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
7097     }\r
7098     e++;\r
7099     i++;\r
7100   } \r
7101   return hmenu;\r
7102 }\r
7103 \r
7104 WNDPROC consoleTextWindowProc;\r
7105 \r
7106 void\r
7107 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7108 {\r
7109   char buf[MSG_SIZ], name[MSG_SIZ];\r
7110   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7111   CHARRANGE sel;\r
7112 \r
7113   if (!getname) {\r
7114     SetWindowText(hInput, command);\r
7115     if (immediate) {\r
7116       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7117     } else {\r
7118       sel.cpMin = 999999;\r
7119       sel.cpMax = 999999;\r
7120       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7121       SetFocus(hInput);\r
7122     }\r
7123     return;\r
7124   }    \r
7125   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7126   if (sel.cpMin == sel.cpMax) {\r
7127     /* Expand to surrounding word */\r
7128     TEXTRANGE tr;\r
7129     do {\r
7130       tr.chrg.cpMax = sel.cpMin;\r
7131       tr.chrg.cpMin = --sel.cpMin;\r
7132       if (sel.cpMin < 0) break;\r
7133       tr.lpstrText = name;\r
7134       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7135     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7136     sel.cpMin++;\r
7137 \r
7138     do {\r
7139       tr.chrg.cpMin = sel.cpMax;\r
7140       tr.chrg.cpMax = ++sel.cpMax;\r
7141       tr.lpstrText = name;\r
7142       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7143     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7144     sel.cpMax--;\r
7145 \r
7146     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7147       MessageBeep(MB_ICONEXCLAMATION);\r
7148       return;\r
7149     }\r
7150     tr.chrg = sel;\r
7151     tr.lpstrText = name;\r
7152     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7153   } else {\r
7154     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7155       MessageBeep(MB_ICONEXCLAMATION);\r
7156       return;\r
7157     }\r
7158     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7159   }\r
7160   if (immediate) {\r
7161     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7162     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7163     SetWindowText(hInput, buf);\r
7164     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7165   } else {\r
7166     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7167       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7168     SetWindowText(hInput, buf);\r
7169     sel.cpMin = 999999;\r
7170     sel.cpMax = 999999;\r
7171     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7172     SetFocus(hInput);\r
7173   }\r
7174 }\r
7175 \r
7176 LRESULT CALLBACK \r
7177 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7178 {\r
7179   HWND hInput;\r
7180   CHARRANGE sel;\r
7181 \r
7182   switch (message) {\r
7183   case WM_KEYDOWN:\r
7184     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7185     if(wParam=='R') return 0;\r
7186     switch (wParam) {\r
7187     case VK_PRIOR:\r
7188       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7189       return 0;\r
7190     case VK_NEXT:\r
7191       sel.cpMin = 999999;\r
7192       sel.cpMax = 999999;\r
7193       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7194       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7195       return 0;\r
7196     }\r
7197     break;\r
7198   case WM_CHAR:\r
7199    if(wParam != '\022') {\r
7200     if (wParam == '\t') {\r
7201       if (GetKeyState(VK_SHIFT) < 0) {\r
7202         /* shifted */\r
7203         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7204         if (buttonDesc[0].hwnd) {\r
7205           SetFocus(buttonDesc[0].hwnd);\r
7206         } else {\r
7207           SetFocus(hwndMain);\r
7208         }\r
7209       } else {\r
7210         /* unshifted */\r
7211         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7212       }\r
7213     } else {\r
7214       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7215       JAWS_DELETE( SetFocus(hInput); )\r
7216       SendMessage(hInput, message, wParam, lParam);\r
7217     }\r
7218     return 0;\r
7219    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7220    lParam = -1;\r
7221   case WM_RBUTTONDOWN:\r
7222     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7223       /* Move selection here if it was empty */\r
7224       POINT pt;\r
7225       pt.x = LOWORD(lParam);\r
7226       pt.y = HIWORD(lParam);\r
7227       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7228       if (sel.cpMin == sel.cpMax) {\r
7229         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7230         sel.cpMax = sel.cpMin;\r
7231         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7232       }\r
7233       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7234 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7235       POINT pt;\r
7236       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7237       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7238       if (sel.cpMin == sel.cpMax) {\r
7239         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7240         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7241       }\r
7242       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7243         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7244       }\r
7245       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7246       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7247       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7248       MenuPopup(hwnd, pt, hmenu, -1);\r
7249 }\r
7250     }\r
7251     return 0;\r
7252   case WM_RBUTTONUP:\r
7253     if (GetKeyState(VK_SHIFT) & ~1) {\r
7254       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7255         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7256     }\r
7257     return 0;\r
7258   case WM_PASTE:\r
7259     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7260     SetFocus(hInput);\r
7261     return SendMessage(hInput, message, wParam, lParam);\r
7262   case WM_MBUTTONDOWN:\r
7263     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7264   case WM_COMMAND:\r
7265     switch (LOWORD(wParam)) {\r
7266     case IDM_QuickPaste:\r
7267       {\r
7268         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7269         if (sel.cpMin == sel.cpMax) {\r
7270           MessageBeep(MB_ICONEXCLAMATION);\r
7271           return 0;\r
7272         }\r
7273         SendMessage(hwnd, WM_COPY, 0, 0);\r
7274         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7275         SendMessage(hInput, WM_PASTE, 0, 0);\r
7276         SetFocus(hInput);\r
7277         return 0;\r
7278       }\r
7279     case IDM_Cut:\r
7280       SendMessage(hwnd, WM_CUT, 0, 0);\r
7281       return 0;\r
7282     case IDM_Paste:\r
7283       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7284       return 0;\r
7285     case IDM_Copy:\r
7286       SendMessage(hwnd, WM_COPY, 0, 0);\r
7287       return 0;\r
7288     default:\r
7289       {\r
7290         int i = LOWORD(wParam) - IDM_CommandX;\r
7291         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7292             icsTextMenuEntry[i].command != NULL) {\r
7293           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7294                    icsTextMenuEntry[i].getname,\r
7295                    icsTextMenuEntry[i].immediate);\r
7296           return 0;\r
7297         }\r
7298       }\r
7299       break;\r
7300     }\r
7301     break;\r
7302   }\r
7303   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7304 }\r
7305 \r
7306 WNDPROC consoleInputWindowProc;\r
7307 \r
7308 LRESULT CALLBACK\r
7309 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7310 {\r
7311   char buf[MSG_SIZ];\r
7312   char *p;\r
7313   static BOOL sendNextChar = FALSE;\r
7314   static BOOL quoteNextChar = FALSE;\r
7315   InputSource *is = consoleInputSource;\r
7316   CHARFORMAT cf;\r
7317   CHARRANGE sel;\r
7318 \r
7319   switch (message) {\r
7320   case WM_CHAR:\r
7321     if (!appData.localLineEditing || sendNextChar) {\r
7322       is->buf[0] = (CHAR) wParam;\r
7323       is->count = 1;\r
7324       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7325       sendNextChar = FALSE;\r
7326       return 0;\r
7327     }\r
7328     if (quoteNextChar) {\r
7329       buf[0] = (char) wParam;\r
7330       buf[1] = NULLCHAR;\r
7331       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7332       quoteNextChar = FALSE;\r
7333       return 0;\r
7334     }\r
7335     switch (wParam) {\r
7336     case '\r':   /* Enter key */\r
7337       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7338       if (consoleEcho) SaveInHistory(is->buf);\r
7339       is->buf[is->count++] = '\n';\r
7340       is->buf[is->count] = NULLCHAR;\r
7341       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7342       if (consoleEcho) {\r
7343         ConsoleOutput(is->buf, is->count, TRUE);\r
7344       } else if (appData.localLineEditing) {\r
7345         ConsoleOutput("\n", 1, TRUE);\r
7346       }\r
7347       /* fall thru */\r
7348     case '\033': /* Escape key */\r
7349       SetWindowText(hwnd, "");\r
7350       cf.cbSize = sizeof(CHARFORMAT);\r
7351       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7352       if (consoleEcho) {\r
7353         cf.crTextColor = textAttribs[ColorNormal].color;\r
7354       } else {\r
7355         cf.crTextColor = COLOR_ECHOOFF;\r
7356       }\r
7357       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7358       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7359       return 0;\r
7360     case '\t':   /* Tab key */\r
7361       if (GetKeyState(VK_SHIFT) < 0) {\r
7362         /* shifted */\r
7363         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7364       } else {\r
7365         /* unshifted */\r
7366         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7367         if (buttonDesc[0].hwnd) {\r
7368           SetFocus(buttonDesc[0].hwnd);\r
7369         } else {\r
7370           SetFocus(hwndMain);\r
7371         }\r
7372       }\r
7373       return 0;\r
7374     case '\023': /* Ctrl+S */\r
7375       sendNextChar = TRUE;\r
7376       return 0;\r
7377     case '\021': /* Ctrl+Q */\r
7378       quoteNextChar = TRUE;\r
7379       return 0;\r
7380     JAWS_REPLAY\r
7381     default:\r
7382       break;\r
7383     }\r
7384     break;\r
7385   case WM_KEYDOWN:\r
7386     switch (wParam) {\r
7387     case VK_UP:\r
7388       GetWindowText(hwnd, buf, MSG_SIZ);\r
7389       p = PrevInHistory(buf);\r
7390       if (p != NULL) {\r
7391         SetWindowText(hwnd, p);\r
7392         sel.cpMin = 999999;\r
7393         sel.cpMax = 999999;\r
7394         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7395         return 0;\r
7396       }\r
7397       break;\r
7398     case VK_DOWN:\r
7399       p = NextInHistory();\r
7400       if (p != NULL) {\r
7401         SetWindowText(hwnd, p);\r
7402         sel.cpMin = 999999;\r
7403         sel.cpMax = 999999;\r
7404         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7405         return 0;\r
7406       }\r
7407       break;\r
7408     case VK_HOME:\r
7409     case VK_END:\r
7410       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7411       /* fall thru */\r
7412     case VK_PRIOR:\r
7413     case VK_NEXT:\r
7414       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7415       return 0;\r
7416     }\r
7417     break;\r
7418   case WM_MBUTTONDOWN:\r
7419     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7420       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7421     break;\r
7422   case WM_RBUTTONUP:\r
7423     if (GetKeyState(VK_SHIFT) & ~1) {\r
7424       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7425         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7426     } else {\r
7427       POINT pt;\r
7428       HMENU hmenu;\r
7429       hmenu = LoadMenu(hInst, "InputMenu");\r
7430       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7431       if (sel.cpMin == sel.cpMax) {\r
7432         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7433         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7434       }\r
7435       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7436         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7437       }\r
7438       pt.x = LOWORD(lParam);\r
7439       pt.y = HIWORD(lParam);\r
7440       MenuPopup(hwnd, pt, hmenu, -1);\r
7441     }\r
7442     return 0;\r
7443   case WM_COMMAND:\r
7444     switch (LOWORD(wParam)) { \r
7445     case IDM_Undo:\r
7446       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7447       return 0;\r
7448     case IDM_SelectAll:\r
7449       sel.cpMin = 0;\r
7450       sel.cpMax = -1; /*999999?*/\r
7451       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7452       return 0;\r
7453     case IDM_Cut:\r
7454       SendMessage(hwnd, WM_CUT, 0, 0);\r
7455       return 0;\r
7456     case IDM_Paste:\r
7457       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7458       return 0;\r
7459     case IDM_Copy:\r
7460       SendMessage(hwnd, WM_COPY, 0, 0);\r
7461       return 0;\r
7462     }\r
7463     break;\r
7464   }\r
7465   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7466 }\r
7467 \r
7468 #define CO_MAX  100000\r
7469 #define CO_TRIM   1000\r
7470 \r
7471 LRESULT CALLBACK\r
7472 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7473 {\r
7474   static SnapData sd;\r
7475   HWND hText, hInput;\r
7476   RECT rect;\r
7477   static int sizeX, sizeY;\r
7478   int newSizeX, newSizeY;\r
7479   MINMAXINFO *mmi;\r
7480   WORD wMask;\r
7481 \r
7482   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7483   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7484 \r
7485   switch (message) {\r
7486   case WM_NOTIFY:\r
7487     if (((NMHDR*)lParam)->code == EN_LINK)\r
7488     {\r
7489       ENLINK *pLink = (ENLINK*)lParam;\r
7490       if (pLink->msg == WM_LBUTTONUP)\r
7491       {\r
7492         TEXTRANGE tr;\r
7493 \r
7494         tr.chrg = pLink->chrg;\r
7495         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7496         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7497         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7498         free(tr.lpstrText);\r
7499       }\r
7500     }\r
7501     break;\r
7502   case WM_INITDIALOG: /* message: initialize dialog box */\r
7503     hwndConsole = hDlg;\r
7504     SetFocus(hInput);\r
7505     consoleTextWindowProc = (WNDPROC)\r
7506       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7507     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7508     consoleInputWindowProc = (WNDPROC)\r
7509       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7510     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7511     Colorize(ColorNormal, TRUE);\r
7512     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7513     ChangedConsoleFont();\r
7514     GetClientRect(hDlg, &rect);\r
7515     sizeX = rect.right;\r
7516     sizeY = rect.bottom;\r
7517     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7518         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7519       WINDOWPLACEMENT wp;\r
7520       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7521       wp.length = sizeof(WINDOWPLACEMENT);\r
7522       wp.flags = 0;\r
7523       wp.showCmd = SW_SHOW;\r
7524       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7525       wp.rcNormalPosition.left = wpConsole.x;\r
7526       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7527       wp.rcNormalPosition.top = wpConsole.y;\r
7528       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7529       SetWindowPlacement(hDlg, &wp);\r
7530     }\r
7531 \r
7532    // [HGM] Chessknight's change 2004-07-13\r
7533    else { /* Determine Defaults */\r
7534        WINDOWPLACEMENT wp;\r
7535        wpConsole.x = wpMain.width + 1;\r
7536        wpConsole.y = wpMain.y;\r
7537        wpConsole.width = screenWidth -  wpMain.width;\r
7538        wpConsole.height = wpMain.height;\r
7539        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7540        wp.length = sizeof(WINDOWPLACEMENT);\r
7541        wp.flags = 0;\r
7542        wp.showCmd = SW_SHOW;\r
7543        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7544        wp.rcNormalPosition.left = wpConsole.x;\r
7545        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7546        wp.rcNormalPosition.top = wpConsole.y;\r
7547        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7548        SetWindowPlacement(hDlg, &wp);\r
7549     }\r
7550 \r
7551    // Allow hText to highlight URLs and send notifications on them\r
7552    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7553    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7554    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7555    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7556 \r
7557     return FALSE;\r
7558 \r
7559   case WM_SETFOCUS:\r
7560     SetFocus(hInput);\r
7561     return 0;\r
7562 \r
7563   case WM_CLOSE:\r
7564     ExitEvent(0);\r
7565     /* not reached */\r
7566     break;\r
7567 \r
7568   case WM_SIZE:\r
7569     if (IsIconic(hDlg)) break;\r
7570     newSizeX = LOWORD(lParam);\r
7571     newSizeY = HIWORD(lParam);\r
7572     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7573       RECT rectText, rectInput;\r
7574       POINT pt;\r
7575       int newTextHeight, newTextWidth;\r
7576       GetWindowRect(hText, &rectText);\r
7577       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7578       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7579       if (newTextHeight < 0) {\r
7580         newSizeY += -newTextHeight;\r
7581         newTextHeight = 0;\r
7582       }\r
7583       SetWindowPos(hText, NULL, 0, 0,\r
7584         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7585       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7586       pt.x = rectInput.left;\r
7587       pt.y = rectInput.top + newSizeY - sizeY;\r
7588       ScreenToClient(hDlg, &pt);\r
7589       SetWindowPos(hInput, NULL, \r
7590         pt.x, pt.y, /* needs client coords */   \r
7591         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7592         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7593     }\r
7594     sizeX = newSizeX;\r
7595     sizeY = newSizeY;\r
7596     break;\r
7597 \r
7598   case WM_GETMINMAXINFO:\r
7599     /* Prevent resizing window too small */\r
7600     mmi = (MINMAXINFO *) lParam;\r
7601     mmi->ptMinTrackSize.x = 100;\r
7602     mmi->ptMinTrackSize.y = 100;\r
7603     break;\r
7604 \r
7605   /* [AS] Snapping */\r
7606   case WM_ENTERSIZEMOVE:\r
7607     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7608 \r
7609   case WM_SIZING:\r
7610     return OnSizing( &sd, hDlg, wParam, lParam );\r
7611 \r
7612   case WM_MOVING:\r
7613     return OnMoving( &sd, hDlg, wParam, lParam );\r
7614 \r
7615   case WM_EXITSIZEMOVE:\r
7616         UpdateICSWidth(hText);\r
7617     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7618   }\r
7619 \r
7620   return DefWindowProc(hDlg, message, wParam, lParam);\r
7621 }\r
7622 \r
7623 \r
7624 VOID\r
7625 ConsoleCreate()\r
7626 {\r
7627   HWND hCons;\r
7628   if (hwndConsole) return;\r
7629   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7630   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7631 }\r
7632 \r
7633 \r
7634 VOID\r
7635 ConsoleOutput(char* data, int length, int forceVisible)\r
7636 {\r
7637   HWND hText;\r
7638   int trim, exlen;\r
7639   char *p, *q;\r
7640   char buf[CO_MAX+1];\r
7641   POINT pEnd;\r
7642   RECT rect;\r
7643   static int delayLF = 0;\r
7644   CHARRANGE savesel, sel;\r
7645 \r
7646   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7647   p = data;\r
7648   q = buf;\r
7649   if (delayLF) {\r
7650     *q++ = '\r';\r
7651     *q++ = '\n';\r
7652     delayLF = 0;\r
7653   }\r
7654   while (length--) {\r
7655     if (*p == '\n') {\r
7656       if (*++p) {\r
7657         *q++ = '\r';\r
7658         *q++ = '\n';\r
7659       } else {\r
7660         delayLF = 1;\r
7661       }\r
7662     } else if (*p == '\007') {\r
7663        MyPlaySound(&sounds[(int)SoundBell]);\r
7664        p++;\r
7665     } else {\r
7666       *q++ = *p++;\r
7667     }\r
7668   }\r
7669   *q = NULLCHAR;\r
7670   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7671   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7672   /* Save current selection */\r
7673   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7674   exlen = GetWindowTextLength(hText);\r
7675   /* Find out whether current end of text is visible */\r
7676   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7677   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7678   /* Trim existing text if it's too long */\r
7679   if (exlen + (q - buf) > CO_MAX) {\r
7680     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7681     sel.cpMin = 0;\r
7682     sel.cpMax = trim;\r
7683     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7684     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7685     exlen -= trim;\r
7686     savesel.cpMin -= trim;\r
7687     savesel.cpMax -= trim;\r
7688     if (exlen < 0) exlen = 0;\r
7689     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7690     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7691   }\r
7692   /* Append the new text */\r
7693   sel.cpMin = exlen;\r
7694   sel.cpMax = exlen;\r
7695   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7696   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7697   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7698   if (forceVisible || exlen == 0 ||\r
7699       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7700        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7701     /* Scroll to make new end of text visible if old end of text\r
7702        was visible or new text is an echo of user typein */\r
7703     sel.cpMin = 9999999;\r
7704     sel.cpMax = 9999999;\r
7705     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7706     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7707     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7708     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7709   }\r
7710   if (savesel.cpMax == exlen || forceVisible) {\r
7711     /* Move insert point to new end of text if it was at the old\r
7712        end of text or if the new text is an echo of user typein */\r
7713     sel.cpMin = 9999999;\r
7714     sel.cpMax = 9999999;\r
7715     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7716   } else {\r
7717     /* Restore previous selection */\r
7718     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7719   }\r
7720   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7721 }\r
7722 \r
7723 /*---------*/\r
7724 \r
7725 \r
7726 void\r
7727 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7728 {\r
7729   char buf[100];\r
7730   char *str;\r
7731   COLORREF oldFg, oldBg;\r
7732   HFONT oldFont;\r
7733   RECT rect;\r
7734 \r
7735   if(copyNumber > 1)\r
7736     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7737 \r
7738   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7739   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7740   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7741 \r
7742   rect.left = x;\r
7743   rect.right = x + squareSize;\r
7744   rect.top  = y;\r
7745   rect.bottom = y + squareSize;\r
7746   str = buf;\r
7747 \r
7748   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7749                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7750              y, ETO_CLIPPED|ETO_OPAQUE,\r
7751              &rect, str, strlen(str), NULL);\r
7752 \r
7753   (void) SetTextColor(hdc, oldFg);\r
7754   (void) SetBkColor(hdc, oldBg);\r
7755   (void) SelectObject(hdc, oldFont);\r
7756 }\r
7757 \r
7758 void\r
7759 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7760               RECT *rect, char *color, char *flagFell)\r
7761 {\r
7762   char buf[100];\r
7763   char *str;\r
7764   COLORREF oldFg, oldBg;\r
7765   HFONT oldFont;\r
7766 \r
7767   if (twoBoards && partnerUp) return;\r
7768   if (appData.clockMode) {\r
7769     if (tinyLayout == 2)\r
7770       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7771     else\r
7772       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7773     str = buf;\r
7774   } else {\r
7775     str = color;\r
7776   }\r
7777 \r
7778   if (highlight) {\r
7779     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7780     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7781   } else {\r
7782     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7783     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7784   }\r
7785 \r
7786   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7787 \r
7788   JAWS_SILENCE\r
7789 \r
7790   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7791              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7792              rect, str, strlen(str), NULL);\r
7793   if(logoHeight > 0 && appData.clockMode) {\r
7794       RECT r;\r
7795       str += strlen(color)+2;\r
7796       r.top = rect->top + logoHeight/2;\r
7797       r.left = rect->left;\r
7798       r.right = rect->right;\r
7799       r.bottom = rect->bottom;\r
7800       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7801                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7802                  &r, str, strlen(str), NULL);\r
7803   }\r
7804   (void) SetTextColor(hdc, oldFg);\r
7805   (void) SetBkColor(hdc, oldBg);\r
7806   (void) SelectObject(hdc, oldFont);\r
7807 }\r
7808 \r
7809 \r
7810 int\r
7811 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7812            OVERLAPPED *ovl)\r
7813 {\r
7814   int ok, err;\r
7815 \r
7816   /* [AS]  */\r
7817   if( count <= 0 ) {\r
7818     if (appData.debugMode) {\r
7819       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7820     }\r
7821 \r
7822     return ERROR_INVALID_USER_BUFFER;\r
7823   }\r
7824 \r
7825   ResetEvent(ovl->hEvent);\r
7826   ovl->Offset = ovl->OffsetHigh = 0;\r
7827   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7828   if (ok) {\r
7829     err = NO_ERROR;\r
7830   } else {\r
7831     err = GetLastError();\r
7832     if (err == ERROR_IO_PENDING) {\r
7833       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7834       if (ok)\r
7835         err = NO_ERROR;\r
7836       else\r
7837         err = GetLastError();\r
7838     }\r
7839   }\r
7840   return err;\r
7841 }\r
7842 \r
7843 int\r
7844 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7845             OVERLAPPED *ovl)\r
7846 {\r
7847   int ok, err;\r
7848 \r
7849   ResetEvent(ovl->hEvent);\r
7850   ovl->Offset = ovl->OffsetHigh = 0;\r
7851   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7852   if (ok) {\r
7853     err = NO_ERROR;\r
7854   } else {\r
7855     err = GetLastError();\r
7856     if (err == ERROR_IO_PENDING) {\r
7857       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7858       if (ok)\r
7859         err = NO_ERROR;\r
7860       else\r
7861         err = GetLastError();\r
7862     }\r
7863 \r
7864   }\r
7865   return err;\r
7866 }\r
7867 \r
7868 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7869 void CheckForInputBufferFull( InputSource * is )\r
7870 {\r
7871     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7872         /* Look for end of line */\r
7873         char * p = is->buf;\r
7874         \r
7875         while( p < is->next && *p != '\n' ) {\r
7876             p++;\r
7877         }\r
7878 \r
7879         if( p >= is->next ) {\r
7880             if (appData.debugMode) {\r
7881                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7882             }\r
7883 \r
7884             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7885             is->count = (DWORD) -1;\r
7886             is->next = is->buf;\r
7887         }\r
7888     }\r
7889 }\r
7890 \r
7891 DWORD\r
7892 InputThread(LPVOID arg)\r
7893 {\r
7894   InputSource *is;\r
7895   OVERLAPPED ovl;\r
7896 \r
7897   is = (InputSource *) arg;\r
7898   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7899   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7900   while (is->hThread != NULL) {\r
7901     is->error = DoReadFile(is->hFile, is->next,\r
7902                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7903                            &is->count, &ovl);\r
7904     if (is->error == NO_ERROR) {\r
7905       is->next += is->count;\r
7906     } else {\r
7907       if (is->error == ERROR_BROKEN_PIPE) {\r
7908         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7909         is->count = 0;\r
7910       } else {\r
7911         is->count = (DWORD) -1;\r
7912         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7913         break; \r
7914       }\r
7915     }\r
7916 \r
7917     CheckForInputBufferFull( is );\r
7918 \r
7919     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7920 \r
7921     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7922 \r
7923     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7924   }\r
7925 \r
7926   CloseHandle(ovl.hEvent);\r
7927   CloseHandle(is->hFile);\r
7928 \r
7929   if (appData.debugMode) {\r
7930     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7931   }\r
7932 \r
7933   return 0;\r
7934 }\r
7935 \r
7936 \r
7937 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7938 DWORD\r
7939 NonOvlInputThread(LPVOID arg)\r
7940 {\r
7941   InputSource *is;\r
7942   char *p, *q;\r
7943   int i;\r
7944   char prev;\r
7945 \r
7946   is = (InputSource *) arg;\r
7947   while (is->hThread != NULL) {\r
7948     is->error = ReadFile(is->hFile, is->next,\r
7949                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7950                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7951     if (is->error == NO_ERROR) {\r
7952       /* Change CRLF to LF */\r
7953       if (is->next > is->buf) {\r
7954         p = is->next - 1;\r
7955         i = is->count + 1;\r
7956       } else {\r
7957         p = is->next;\r
7958         i = is->count;\r
7959       }\r
7960       q = p;\r
7961       prev = NULLCHAR;\r
7962       while (i > 0) {\r
7963         if (prev == '\r' && *p == '\n') {\r
7964           *(q-1) = '\n';\r
7965           is->count--;\r
7966         } else { \r
7967           *q++ = *p;\r
7968         }\r
7969         prev = *p++;\r
7970         i--;\r
7971       }\r
7972       *q = NULLCHAR;\r
7973       is->next = q;\r
7974     } else {\r
7975       if (is->error == ERROR_BROKEN_PIPE) {\r
7976         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7977         is->count = 0; \r
7978       } else {\r
7979         is->count = (DWORD) -1;\r
7980       }\r
7981     }\r
7982 \r
7983     CheckForInputBufferFull( is );\r
7984 \r
7985     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7986 \r
7987     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7988 \r
7989     if (is->count < 0) break;  /* Quit on error */\r
7990   }\r
7991   CloseHandle(is->hFile);\r
7992   return 0;\r
7993 }\r
7994 \r
7995 DWORD\r
7996 SocketInputThread(LPVOID arg)\r
7997 {\r
7998   InputSource *is;\r
7999 \r
8000   is = (InputSource *) arg;\r
8001   while (is->hThread != NULL) {\r
8002     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8003     if ((int)is->count == SOCKET_ERROR) {\r
8004       is->count = (DWORD) -1;\r
8005       is->error = WSAGetLastError();\r
8006     } else {\r
8007       is->error = NO_ERROR;\r
8008       is->next += is->count;\r
8009       if (is->count == 0 && is->second == is) {\r
8010         /* End of file on stderr; quit with no message */\r
8011         break;\r
8012       }\r
8013     }\r
8014     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8015 \r
8016     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8017 \r
8018     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8019   }\r
8020   return 0;\r
8021 }\r
8022 \r
8023 VOID\r
8024 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8025 {\r
8026   InputSource *is;\r
8027 \r
8028   is = (InputSource *) lParam;\r
8029   if (is->lineByLine) {\r
8030     /* Feed in lines one by one */\r
8031     char *p = is->buf;\r
8032     char *q = p;\r
8033     while (q < is->next) {\r
8034       if (*q++ == '\n') {\r
8035         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8036         p = q;\r
8037       }\r
8038     }\r
8039     \r
8040     /* Move any partial line to the start of the buffer */\r
8041     q = is->buf;\r
8042     while (p < is->next) {\r
8043       *q++ = *p++;\r
8044     }\r
8045     is->next = q;\r
8046 \r
8047     if (is->error != NO_ERROR || is->count == 0) {\r
8048       /* Notify backend of the error.  Note: If there was a partial\r
8049          line at the end, it is not flushed through. */\r
8050       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8051     }\r
8052   } else {\r
8053     /* Feed in the whole chunk of input at once */\r
8054     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8055     is->next = is->buf;\r
8056   }\r
8057 }\r
8058 \r
8059 /*---------------------------------------------------------------------------*\\r
8060  *\r
8061  *  Menu enables. Used when setting various modes.\r
8062  *\r
8063 \*---------------------------------------------------------------------------*/\r
8064 \r
8065 typedef struct {\r
8066   int item;\r
8067   int flags;\r
8068 } Enables;\r
8069 \r
8070 VOID\r
8071 GreyRevert(Boolean grey)\r
8072 { // [HGM] vari: for retracting variations in local mode\r
8073   HMENU hmenu = GetMenu(hwndMain);\r
8074   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8075   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8076 }\r
8077 \r
8078 VOID\r
8079 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8080 {\r
8081   while (enab->item > 0) {\r
8082     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8083     enab++;\r
8084   }\r
8085 }\r
8086 \r
8087 Enables gnuEnables[] = {\r
8088   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8089   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8090   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8091   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8092   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8093   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8094   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8095   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8096   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8101 \r
8102 \r
8103   // Needed to switch from ncp to GNU mode on Engine Load\r
8104   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8105   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8106   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8107   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8108   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8109   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8110   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8111   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8112   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8113   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8114   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8115   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8116   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8117   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8118   { -1, -1 }\r
8119 };\r
8120 \r
8121 Enables icsEnables[] = {\r
8122   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8123   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8124   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8125   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8126   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8127   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8128   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8129   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8130   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8131   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8132   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8133   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8134   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8135   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8136   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8137   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8138   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8139   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8140   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8141   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8142   { -1, -1 }\r
8143 };\r
8144 \r
8145 #if ZIPPY\r
8146 Enables zippyEnables[] = {\r
8147   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8148   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8149   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8150   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8151   { -1, -1 }\r
8152 };\r
8153 #endif\r
8154 \r
8155 Enables ncpEnables[] = {\r
8156   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8157   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8158   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8159   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8160   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8161   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8162   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8163   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8164   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8165   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8166   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8167   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8168   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8169   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8170   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8171   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8172   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8173   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8174   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8175   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8176   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8177   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8178   { -1, -1 }\r
8179 };\r
8180 \r
8181 Enables trainingOnEnables[] = {\r
8182   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8183   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8184   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8185   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8186   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8187   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8188   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8189   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8190   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8191   { -1, -1 }\r
8192 };\r
8193 \r
8194 Enables trainingOffEnables[] = {\r
8195   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8196   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8197   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8198   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8199   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8200   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8201   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8202   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8203   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8204   { -1, -1 }\r
8205 };\r
8206 \r
8207 /* These modify either ncpEnables or gnuEnables */\r
8208 Enables cmailEnables[] = {\r
8209   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8210   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8211   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8212   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8213   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8214   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8215   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8216   { -1, -1 }\r
8217 };\r
8218 \r
8219 Enables machineThinkingEnables[] = {\r
8220   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8221   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8222   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8223   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8224   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8225   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8226   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8227   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8228   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8229   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8230   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8231   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8232   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8233 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8234   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8235   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8236   { -1, -1 }\r
8237 };\r
8238 \r
8239 Enables userThinkingEnables[] = {\r
8240   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8241   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8242   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8243   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8244   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8245   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8246   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8247   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8248   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8249   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8250   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8251   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8252   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8253 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8254   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8255   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8256   { -1, -1 }\r
8257 };\r
8258 \r
8259 /*---------------------------------------------------------------------------*\\r
8260  *\r
8261  *  Front-end interface functions exported by XBoard.\r
8262  *  Functions appear in same order as prototypes in frontend.h.\r
8263  * \r
8264 \*---------------------------------------------------------------------------*/\r
8265 VOID\r
8266 CheckMark(UINT item, int state)\r
8267 {\r
8268     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8269 }\r
8270 \r
8271 VOID\r
8272 ModeHighlight()\r
8273 {\r
8274   static UINT prevChecked = 0;\r
8275   static int prevPausing = 0;\r
8276   UINT nowChecked;\r
8277 \r
8278   if (pausing != prevPausing) {\r
8279     prevPausing = pausing;\r
8280     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8281                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8282     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8283   }\r
8284 \r
8285   switch (gameMode) {\r
8286   case BeginningOfGame:\r
8287     if (appData.icsActive)\r
8288       nowChecked = IDM_IcsClient;\r
8289     else if (appData.noChessProgram)\r
8290       nowChecked = IDM_EditGame;\r
8291     else\r
8292       nowChecked = IDM_MachineBlack;\r
8293     break;\r
8294   case MachinePlaysBlack:\r
8295     nowChecked = IDM_MachineBlack;\r
8296     break;\r
8297   case MachinePlaysWhite:\r
8298     nowChecked = IDM_MachineWhite;\r
8299     break;\r
8300   case TwoMachinesPlay:\r
8301     nowChecked = IDM_TwoMachines;\r
8302     break;\r
8303   case AnalyzeMode:\r
8304     nowChecked = IDM_AnalysisMode;\r
8305     break;\r
8306   case AnalyzeFile:\r
8307     nowChecked = IDM_AnalyzeFile;\r
8308     break;\r
8309   case EditGame:\r
8310     nowChecked = IDM_EditGame;\r
8311     break;\r
8312   case PlayFromGameFile:\r
8313     nowChecked = IDM_LoadGame;\r
8314     break;\r
8315   case EditPosition:\r
8316     nowChecked = IDM_EditPosition;\r
8317     break;\r
8318   case Training:\r
8319     nowChecked = IDM_Training;\r
8320     break;\r
8321   case IcsPlayingWhite:\r
8322   case IcsPlayingBlack:\r
8323   case IcsObserving:\r
8324   case IcsIdle:\r
8325     nowChecked = IDM_IcsClient;\r
8326     break;\r
8327   default:\r
8328   case EndOfGame:\r
8329     nowChecked = 0;\r
8330     break;\r
8331   }\r
8332   if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8333     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
8334   CheckMark(prevChecked, MF_UNCHECKED);\r
8335   CheckMark(nowChecked, MF_CHECKED);\r
8336   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8337 \r
8338   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8339     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8340                           MF_BYCOMMAND|MF_ENABLED);\r
8341   } else {\r
8342     (void) EnableMenuItem(GetMenu(hwndMain), \r
8343                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8344   }\r
8345 \r
8346   prevChecked = nowChecked;\r
8347 \r
8348   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8349   if (appData.icsActive) {\r
8350        if (appData.icsEngineAnalyze) {\r
8351                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8352        } else {\r
8353                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8354        }\r
8355   }\r
8356   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8357 }\r
8358 \r
8359 VOID\r
8360 SetICSMode()\r
8361 {\r
8362   HMENU hmenu = GetMenu(hwndMain);\r
8363   SetMenuEnables(hmenu, icsEnables);\r
8364   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8365     MF_BYCOMMAND|MF_ENABLED);\r
8366 #if ZIPPY\r
8367   if (appData.zippyPlay) {\r
8368     SetMenuEnables(hmenu, zippyEnables);\r
8369     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8370          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8371           MF_BYCOMMAND|MF_ENABLED);\r
8372   }\r
8373 #endif\r
8374 }\r
8375 \r
8376 VOID\r
8377 SetGNUMode()\r
8378 {\r
8379   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8380 }\r
8381 \r
8382 VOID\r
8383 SetNCPMode()\r
8384 {\r
8385   HMENU hmenu = GetMenu(hwndMain);\r
8386   SetMenuEnables(hmenu, ncpEnables);\r
8387     DrawMenuBar(hwndMain);\r
8388 }\r
8389 \r
8390 VOID\r
8391 SetCmailMode()\r
8392 {\r
8393   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8394 }\r
8395 \r
8396 VOID \r
8397 SetTrainingModeOn()\r
8398 {\r
8399   int i;\r
8400   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8401   for (i = 0; i < N_BUTTONS; i++) {\r
8402     if (buttonDesc[i].hwnd != NULL)\r
8403       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8404   }\r
8405   CommentPopDown();\r
8406 }\r
8407 \r
8408 VOID SetTrainingModeOff()\r
8409 {\r
8410   int i;\r
8411   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8412   for (i = 0; i < N_BUTTONS; i++) {\r
8413     if (buttonDesc[i].hwnd != NULL)\r
8414       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8415   }\r
8416 }\r
8417 \r
8418 \r
8419 VOID\r
8420 SetUserThinkingEnables()\r
8421 {\r
8422   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8423 }\r
8424 \r
8425 VOID\r
8426 SetMachineThinkingEnables()\r
8427 {\r
8428   HMENU hMenu = GetMenu(hwndMain);\r
8429   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8430 \r
8431   SetMenuEnables(hMenu, machineThinkingEnables);\r
8432 \r
8433   if (gameMode == MachinePlaysBlack) {\r
8434     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8435   } else if (gameMode == MachinePlaysWhite) {\r
8436     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8437   } else if (gameMode == TwoMachinesPlay) {\r
8438     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8439   }\r
8440 }\r
8441 \r
8442 \r
8443 VOID\r
8444 DisplayTitle(char *str)\r
8445 {\r
8446   char title[MSG_SIZ], *host;\r
8447   if (str[0] != NULLCHAR) {\r
8448     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8449   } else if (appData.icsActive) {\r
8450     if (appData.icsCommPort[0] != NULLCHAR)\r
8451       host = "ICS";\r
8452     else \r
8453       host = appData.icsHost;\r
8454       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8455   } else if (appData.noChessProgram) {\r
8456     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8457   } else {\r
8458     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8459     strcat(title, ": ");\r
8460     strcat(title, first.tidy);\r
8461   }\r
8462   SetWindowText(hwndMain, title);\r
8463 }\r
8464 \r
8465 \r
8466 VOID\r
8467 DisplayMessage(char *str1, char *str2)\r
8468 {\r
8469   HDC hdc;\r
8470   HFONT oldFont;\r
8471   int remain = MESSAGE_TEXT_MAX - 1;\r
8472   int len;\r
8473 \r
8474   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8475   messageText[0] = NULLCHAR;\r
8476   if (*str1) {\r
8477     len = strlen(str1);\r
8478     if (len > remain) len = remain;\r
8479     strncpy(messageText, str1, len);\r
8480     messageText[len] = NULLCHAR;\r
8481     remain -= len;\r
8482   }\r
8483   if (*str2 && remain >= 2) {\r
8484     if (*str1) {\r
8485       strcat(messageText, "  ");\r
8486       remain -= 2;\r
8487     }\r
8488     len = strlen(str2);\r
8489     if (len > remain) len = remain;\r
8490     strncat(messageText, str2, len);\r
8491   }\r
8492   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8493   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8494 \r
8495   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8496 \r
8497   SAYMACHINEMOVE();\r
8498 \r
8499   hdc = GetDC(hwndMain);\r
8500   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8501   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8502              &messageRect, messageText, strlen(messageText), NULL);\r
8503   (void) SelectObject(hdc, oldFont);\r
8504   (void) ReleaseDC(hwndMain, hdc);\r
8505 }\r
8506 \r
8507 VOID\r
8508 DisplayError(char *str, int error)\r
8509 {\r
8510   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8511   int len;\r
8512 \r
8513   if (error == 0) {\r
8514     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8515   } else {\r
8516     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8517                         NULL, error, LANG_NEUTRAL,\r
8518                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8519     if (len > 0) {\r
8520       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8521     } else {\r
8522       ErrorMap *em = errmap;\r
8523       while (em->err != 0 && em->err != error) em++;\r
8524       if (em->err != 0) {\r
8525         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8526       } else {\r
8527         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8528       }\r
8529     }\r
8530   }\r
8531   \r
8532   ErrorPopUp(_("Error"), buf);\r
8533 }\r
8534 \r
8535 \r
8536 VOID\r
8537 DisplayMoveError(char *str)\r
8538 {\r
8539   fromX = fromY = -1;\r
8540   ClearHighlights();\r
8541   DrawPosition(FALSE, NULL);\r
8542   if (appData.popupMoveErrors) {\r
8543     ErrorPopUp(_("Error"), str);\r
8544   } else {\r
8545     DisplayMessage(str, "");\r
8546     moveErrorMessageUp = TRUE;\r
8547   }\r
8548 }\r
8549 \r
8550 VOID\r
8551 DisplayFatalError(char *str, int error, int exitStatus)\r
8552 {\r
8553   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8554   int len;\r
8555   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8556 \r
8557   if (error != 0) {\r
8558     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8559                         NULL, error, LANG_NEUTRAL,\r
8560                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8561     if (len > 0) {\r
8562       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8563     } else {\r
8564       ErrorMap *em = errmap;\r
8565       while (em->err != 0 && em->err != error) em++;\r
8566       if (em->err != 0) {\r
8567         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8568       } else {\r
8569         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8570       }\r
8571     }\r
8572     str = buf;\r
8573   }\r
8574   if (appData.debugMode) {\r
8575     fprintf(debugFP, "%s: %s\n", label, str);\r
8576   }\r
8577   if (appData.popupExitMessage) {\r
8578     if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!\r
8579     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8580                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8581   }\r
8582   ExitEvent(exitStatus);\r
8583 }\r
8584 \r
8585 \r
8586 VOID\r
8587 DisplayInformation(char *str)\r
8588 {\r
8589   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8590 }\r
8591 \r
8592 char *\r
8593 Shorten (char *s)\r
8594 {\r
8595   return s;\r
8596 }\r
8597 \r
8598 VOID\r
8599 DisplayNote(char *str)\r
8600 {\r
8601   ErrorPopUp(_("Note"), str);\r
8602 }\r
8603 \r
8604 \r
8605 typedef struct {\r
8606   char *title, *question, *replyPrefix;\r
8607   ProcRef pr;\r
8608 } QuestionParams;\r
8609 \r
8610 LRESULT CALLBACK\r
8611 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8612 {\r
8613   static QuestionParams *qp;\r
8614   char reply[MSG_SIZ];\r
8615   int len, err;\r
8616 \r
8617   switch (message) {\r
8618   case WM_INITDIALOG:\r
8619     qp = (QuestionParams *) lParam;\r
8620     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8621     Translate(hDlg, DLG_Question);\r
8622     SetWindowText(hDlg, qp->title);\r
8623     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8624     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8625     return FALSE;\r
8626 \r
8627   case WM_COMMAND:\r
8628     switch (LOWORD(wParam)) {\r
8629     case IDOK:\r
8630       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8631       if (*reply) strcat(reply, " ");\r
8632       len = strlen(reply);\r
8633       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8634       strcat(reply, "\n");\r
8635       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8636       EndDialog(hDlg, TRUE);\r
8637       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8638       return TRUE;\r
8639     case IDCANCEL:\r
8640       EndDialog(hDlg, FALSE);\r
8641       return TRUE;\r
8642     default:\r
8643       break;\r
8644     }\r
8645     break;\r
8646   }\r
8647   return FALSE;\r
8648 }\r
8649 \r
8650 VOID\r
8651 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8652 {\r
8653     QuestionParams qp;\r
8654     FARPROC lpProc;\r
8655     \r
8656     qp.title = title;\r
8657     qp.question = question;\r
8658     qp.replyPrefix = replyPrefix;\r
8659     qp.pr = pr;\r
8660     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8661     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8662       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8663     FreeProcInstance(lpProc);\r
8664 }\r
8665 \r
8666 /* [AS] Pick FRC position */\r
8667 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8668 {\r
8669     static int * lpIndexFRC;\r
8670     BOOL index_is_ok;\r
8671     char buf[16];\r
8672 \r
8673     switch( message )\r
8674     {\r
8675     case WM_INITDIALOG:\r
8676         lpIndexFRC = (int *) lParam;\r
8677 \r
8678         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8679         Translate(hDlg, DLG_NewGameFRC);\r
8680 \r
8681         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8682         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8683         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8684         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8685 \r
8686         break;\r
8687 \r
8688     case WM_COMMAND:\r
8689         switch( LOWORD(wParam) ) {\r
8690         case IDOK:\r
8691             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8692             EndDialog( hDlg, 0 );\r
8693             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8694             return TRUE;\r
8695         case IDCANCEL:\r
8696             EndDialog( hDlg, 1 );   \r
8697             return TRUE;\r
8698         case IDC_NFG_Edit:\r
8699             if( HIWORD(wParam) == EN_CHANGE ) {\r
8700                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8701 \r
8702                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8703             }\r
8704             return TRUE;\r
8705         case IDC_NFG_Random:\r
8706           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8707             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8708             return TRUE;\r
8709         }\r
8710 \r
8711         break;\r
8712     }\r
8713 \r
8714     return FALSE;\r
8715 }\r
8716 \r
8717 int NewGameFRC()\r
8718 {\r
8719     int result;\r
8720     int index = appData.defaultFrcPosition;\r
8721     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8722 \r
8723     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8724 \r
8725     if( result == 0 ) {\r
8726         appData.defaultFrcPosition = index;\r
8727     }\r
8728 \r
8729     return result;\r
8730 }\r
8731 \r
8732 /* [AS] Game list options. Refactored by HGM */\r
8733 \r
8734 HWND gameListOptionsDialog;\r
8735 \r
8736 // low-level front-end: clear text edit / list widget\r
8737 void\r
8738 \r
8739 GLT_ClearList()\r
8740 {\r
8741     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8742 }\r
8743 \r
8744 // low-level front-end: clear text edit / list widget\r
8745 void\r
8746 GLT_DeSelectList()\r
8747 {\r
8748     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8749 }\r
8750 \r
8751 // low-level front-end: append line to text edit / list widget\r
8752 void\r
8753 GLT_AddToList( char *name )\r
8754 {\r
8755     if( name != 0 ) {\r
8756             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8757     }\r
8758 }\r
8759 \r
8760 // low-level front-end: get line from text edit / list widget\r
8761 Boolean\r
8762 GLT_GetFromList( int index, char *name )\r
8763 {\r
8764     if( name != 0 ) {\r
8765             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8766                 return TRUE;\r
8767     }\r
8768     return FALSE;\r
8769 }\r
8770 \r
8771 void GLT_MoveSelection( HWND hDlg, int delta )\r
8772 {\r
8773     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8774     int idx2 = idx1 + delta;\r
8775     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8776 \r
8777     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8778         char buf[128];\r
8779 \r
8780         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8781         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8782         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8783         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8784     }\r
8785 }\r
8786 \r
8787 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8788 {\r
8789     switch( message )\r
8790     {\r
8791     case WM_INITDIALOG:\r
8792         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8793         \r
8794         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8795         Translate(hDlg, DLG_GameListOptions);\r
8796 \r
8797         /* Initialize list */\r
8798         GLT_TagsToList( lpUserGLT );\r
8799 \r
8800         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8801 \r
8802         break;\r
8803 \r
8804     case WM_COMMAND:\r
8805         switch( LOWORD(wParam) ) {\r
8806         case IDOK:\r
8807             GLT_ParseList();\r
8808             EndDialog( hDlg, 0 );\r
8809             return TRUE;\r
8810         case IDCANCEL:\r
8811             EndDialog( hDlg, 1 );\r
8812             return TRUE;\r
8813 \r
8814         case IDC_GLT_Default:\r
8815             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8816             return TRUE;\r
8817 \r
8818         case IDC_GLT_Restore:\r
8819             GLT_TagsToList( appData.gameListTags );\r
8820             return TRUE;\r
8821 \r
8822         case IDC_GLT_Up:\r
8823             GLT_MoveSelection( hDlg, -1 );\r
8824             return TRUE;\r
8825 \r
8826         case IDC_GLT_Down:\r
8827             GLT_MoveSelection( hDlg, +1 );\r
8828             return TRUE;\r
8829         }\r
8830 \r
8831         break;\r
8832     }\r
8833 \r
8834     return FALSE;\r
8835 }\r
8836 \r
8837 int GameListOptions()\r
8838 {\r
8839     int result;\r
8840     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8841 \r
8842       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8843 \r
8844     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8845 \r
8846     if( result == 0 ) {\r
8847         char *oldTags = appData.gameListTags;\r
8848         /* [AS] Memory leak here! */\r
8849         appData.gameListTags = strdup( lpUserGLT ); \r
8850         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8851             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8852     }\r
8853 \r
8854     return result;\r
8855 }\r
8856 \r
8857 VOID\r
8858 DisplayIcsInteractionTitle(char *str)\r
8859 {\r
8860   char consoleTitle[MSG_SIZ];\r
8861 \r
8862     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8863     SetWindowText(hwndConsole, consoleTitle);\r
8864 \r
8865     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8866       char buf[MSG_SIZ], *p = buf, *q;\r
8867         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8868       do {\r
8869         q = strchr(p, ';');\r
8870         if(q) *q++ = 0;\r
8871         if(*p) ChatPopUp(p);\r
8872       } while(p=q);\r
8873     }\r
8874 \r
8875     SetActiveWindow(hwndMain);\r
8876 }\r
8877 \r
8878 void\r
8879 DrawPositionX(int fullRedraw, Board board)\r
8880 {\r
8881   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8882 }\r
8883 \r
8884 void NotifyFrontendLogin()\r
8885 {\r
8886         if (hwndConsole)\r
8887                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8888 }\r
8889 \r
8890 VOID\r
8891 ResetFrontEnd()\r
8892 {\r
8893   fromX = fromY = -1;\r
8894   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8895     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8896     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8897     dragInfo.lastpos = dragInfo.pos;\r
8898     dragInfo.start.x = dragInfo.start.y = -1;\r
8899     dragInfo.from = dragInfo.start;\r
8900     ReleaseCapture();\r
8901     DrawPosition(TRUE, NULL);\r
8902   }\r
8903   TagsPopDown();\r
8904 }\r
8905 \r
8906 \r
8907 VOID\r
8908 CommentPopUp(char *title, char *str)\r
8909 {\r
8910   HWND hwnd = GetActiveWindow();\r
8911   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8912   SAY(str);\r
8913   SetActiveWindow(hwnd);\r
8914 }\r
8915 \r
8916 VOID\r
8917 CommentPopDown(void)\r
8918 {\r
8919   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8920   if (commentDialog) {\r
8921     ShowWindow(commentDialog, SW_HIDE);\r
8922   }\r
8923   commentUp = FALSE;\r
8924 }\r
8925 \r
8926 VOID\r
8927 EditCommentPopUp(int index, char *title, char *str)\r
8928 {\r
8929   EitherCommentPopUp(index, title, str, TRUE);\r
8930 }\r
8931 \r
8932 \r
8933 int\r
8934 Roar()\r
8935 {\r
8936   MyPlaySound(&sounds[(int)SoundRoar]);\r
8937   return 1;\r
8938 }\r
8939 \r
8940 VOID\r
8941 RingBell()\r
8942 {\r
8943   MyPlaySound(&sounds[(int)SoundMove]);\r
8944 }\r
8945 \r
8946 VOID PlayIcsWinSound()\r
8947 {\r
8948   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8949 }\r
8950 \r
8951 VOID PlayIcsLossSound()\r
8952 {\r
8953   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8954 }\r
8955 \r
8956 VOID PlayIcsDrawSound()\r
8957 {\r
8958   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8959 }\r
8960 \r
8961 VOID PlayIcsUnfinishedSound()\r
8962 {\r
8963   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8964 }\r
8965 \r
8966 VOID\r
8967 PlayAlarmSound()\r
8968 {\r
8969   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8970 }\r
8971 \r
8972 VOID\r
8973 PlayTellSound()\r
8974 {\r
8975   MyPlaySound(&textAttribs[ColorTell].sound);\r
8976 }\r
8977 \r
8978 \r
8979 VOID\r
8980 EchoOn()\r
8981 {\r
8982   HWND hInput;\r
8983   consoleEcho = TRUE;\r
8984   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8985   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8986   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8987 }\r
8988 \r
8989 \r
8990 VOID\r
8991 EchoOff()\r
8992 {\r
8993   CHARFORMAT cf;\r
8994   HWND hInput;\r
8995   consoleEcho = FALSE;\r
8996   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8997   /* This works OK: set text and background both to the same color */\r
8998   cf = consoleCF;\r
8999   cf.crTextColor = COLOR_ECHOOFF;\r
9000   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9001   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9002 }\r
9003 \r
9004 /* No Raw()...? */\r
9005 \r
9006 void Colorize(ColorClass cc, int continuation)\r
9007 {\r
9008   currentColorClass = cc;\r
9009   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9010   consoleCF.crTextColor = textAttribs[cc].color;\r
9011   consoleCF.dwEffects = textAttribs[cc].effects;\r
9012   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9013 }\r
9014 \r
9015 char *\r
9016 UserName()\r
9017 {\r
9018   static char buf[MSG_SIZ];\r
9019   DWORD bufsiz = MSG_SIZ;\r
9020 \r
9021   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9022         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9023   }\r
9024   if (!GetUserName(buf, &bufsiz)) {\r
9025     /*DisplayError("Error getting user name", GetLastError());*/\r
9026     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
9027   }\r
9028   return buf;\r
9029 }\r
9030 \r
9031 char *\r
9032 HostName()\r
9033 {\r
9034   static char buf[MSG_SIZ];\r
9035   DWORD bufsiz = MSG_SIZ;\r
9036 \r
9037   if (!GetComputerName(buf, &bufsiz)) {\r
9038     /*DisplayError("Error getting host name", GetLastError());*/\r
9039     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
9040   }\r
9041   return buf;\r
9042 }\r
9043 \r
9044 \r
9045 int\r
9046 ClockTimerRunning()\r
9047 {\r
9048   return clockTimerEvent != 0;\r
9049 }\r
9050 \r
9051 int\r
9052 StopClockTimer()\r
9053 {\r
9054   if (clockTimerEvent == 0) return FALSE;\r
9055   KillTimer(hwndMain, clockTimerEvent);\r
9056   clockTimerEvent = 0;\r
9057   return TRUE;\r
9058 }\r
9059 \r
9060 void\r
9061 StartClockTimer(long millisec)\r
9062 {\r
9063   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9064                              (UINT) millisec, NULL);\r
9065 }\r
9066 \r
9067 void\r
9068 DisplayWhiteClock(long timeRemaining, int highlight)\r
9069 {\r
9070   HDC hdc;\r
9071   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9072 \r
9073   if(appData.noGUI) return;\r
9074   hdc = GetDC(hwndMain);\r
9075   if (!IsIconic(hwndMain)) {\r
9076     DisplayAClock(hdc, timeRemaining, highlight, \r
9077                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
9078   }\r
9079   if (highlight && iconCurrent == iconBlack) {\r
9080     iconCurrent = iconWhite;\r
9081     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9082     if (IsIconic(hwndMain)) {\r
9083       DrawIcon(hdc, 2, 2, iconCurrent);\r
9084     }\r
9085   }\r
9086   (void) ReleaseDC(hwndMain, hdc);\r
9087   if (hwndConsole)\r
9088     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9089 }\r
9090 \r
9091 void\r
9092 DisplayBlackClock(long timeRemaining, int highlight)\r
9093 {\r
9094   HDC hdc;\r
9095   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9096 \r
9097 \r
9098   if(appData.noGUI) return;\r
9099   hdc = GetDC(hwndMain);\r
9100   if (!IsIconic(hwndMain)) {\r
9101     DisplayAClock(hdc, timeRemaining, highlight, \r
9102                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
9103   }\r
9104   if (highlight && iconCurrent == iconWhite) {\r
9105     iconCurrent = iconBlack;\r
9106     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9107     if (IsIconic(hwndMain)) {\r
9108       DrawIcon(hdc, 2, 2, iconCurrent);\r
9109     }\r
9110   }\r
9111   (void) ReleaseDC(hwndMain, hdc);\r
9112   if (hwndConsole)\r
9113     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9114 }\r
9115 \r
9116 \r
9117 int\r
9118 LoadGameTimerRunning()\r
9119 {\r
9120   return loadGameTimerEvent != 0;\r
9121 }\r
9122 \r
9123 int\r
9124 StopLoadGameTimer()\r
9125 {\r
9126   if (loadGameTimerEvent == 0) return FALSE;\r
9127   KillTimer(hwndMain, loadGameTimerEvent);\r
9128   loadGameTimerEvent = 0;\r
9129   return TRUE;\r
9130 }\r
9131 \r
9132 void\r
9133 StartLoadGameTimer(long millisec)\r
9134 {\r
9135   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9136                                 (UINT) millisec, NULL);\r
9137 }\r
9138 \r
9139 void\r
9140 AutoSaveGame()\r
9141 {\r
9142   char *defName;\r
9143   FILE *f;\r
9144   char fileTitle[MSG_SIZ];\r
9145 \r
9146   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9147   f = OpenFileDialog(hwndMain, "a", defName,\r
9148                      appData.oldSaveStyle ? "gam" : "pgn",\r
9149                      GAME_FILT, \r
9150                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9151   if (f != NULL) {\r
9152     SaveGame(f, 0, "");\r
9153     fclose(f);\r
9154   }\r
9155 }\r
9156 \r
9157 \r
9158 void\r
9159 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9160 {\r
9161   if (delayedTimerEvent != 0) {\r
9162     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9163       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9164     }\r
9165     KillTimer(hwndMain, delayedTimerEvent);\r
9166     delayedTimerEvent = 0;\r
9167     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9168     delayedTimerCallback();\r
9169   }\r
9170   delayedTimerCallback = cb;\r
9171   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9172                                 (UINT) millisec, NULL);\r
9173 }\r
9174 \r
9175 DelayedEventCallback\r
9176 GetDelayedEvent()\r
9177 {\r
9178   if (delayedTimerEvent) {\r
9179     return delayedTimerCallback;\r
9180   } else {\r
9181     return NULL;\r
9182   }\r
9183 }\r
9184 \r
9185 void\r
9186 CancelDelayedEvent()\r
9187 {\r
9188   if (delayedTimerEvent) {\r
9189     KillTimer(hwndMain, delayedTimerEvent);\r
9190     delayedTimerEvent = 0;\r
9191   }\r
9192 }\r
9193 \r
9194 DWORD GetWin32Priority(int nice)\r
9195 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9196 /*\r
9197 REALTIME_PRIORITY_CLASS     0x00000100\r
9198 HIGH_PRIORITY_CLASS         0x00000080\r
9199 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9200 NORMAL_PRIORITY_CLASS       0x00000020\r
9201 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9202 IDLE_PRIORITY_CLASS         0x00000040\r
9203 */\r
9204         if (nice < -15) return 0x00000080;\r
9205         if (nice < 0)   return 0x00008000;\r
9206 \r
9207         if (nice == 0)  return 0x00000020;\r
9208         if (nice < 15)  return 0x00004000;\r
9209         return 0x00000040;\r
9210 }\r
9211 \r
9212 void RunCommand(char *cmdLine)\r
9213 {\r
9214   /* Now create the child process. */\r
9215   STARTUPINFO siStartInfo;\r
9216   PROCESS_INFORMATION piProcInfo;\r
9217 \r
9218   siStartInfo.cb = sizeof(STARTUPINFO);\r
9219   siStartInfo.lpReserved = NULL;\r
9220   siStartInfo.lpDesktop = NULL;\r
9221   siStartInfo.lpTitle = NULL;\r
9222   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9223   siStartInfo.cbReserved2 = 0;\r
9224   siStartInfo.lpReserved2 = NULL;\r
9225   siStartInfo.hStdInput = NULL;\r
9226   siStartInfo.hStdOutput = NULL;\r
9227   siStartInfo.hStdError = NULL;\r
9228 \r
9229   CreateProcess(NULL,\r
9230                 cmdLine,           /* command line */\r
9231                 NULL,      /* process security attributes */\r
9232                 NULL,      /* primary thread security attrs */\r
9233                 TRUE,      /* handles are inherited */\r
9234                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9235                 NULL,      /* use parent's environment */\r
9236                 NULL,\r
9237                 &siStartInfo, /* STARTUPINFO pointer */\r
9238                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9239 \r
9240   CloseHandle(piProcInfo.hThread);\r
9241 }\r
9242 \r
9243 /* Start a child process running the given program.\r
9244    The process's standard output can be read from "from", and its\r
9245    standard input can be written to "to".\r
9246    Exit with fatal error if anything goes wrong.\r
9247    Returns an opaque pointer that can be used to destroy the process\r
9248    later.\r
9249 */\r
9250 int\r
9251 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9252 {\r
9253 #define BUFSIZE 4096\r
9254 \r
9255   HANDLE hChildStdinRd, hChildStdinWr,\r
9256     hChildStdoutRd, hChildStdoutWr;\r
9257   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9258   SECURITY_ATTRIBUTES saAttr;\r
9259   BOOL fSuccess;\r
9260   PROCESS_INFORMATION piProcInfo;\r
9261   STARTUPINFO siStartInfo;\r
9262   ChildProc *cp;\r
9263   char buf[MSG_SIZ];\r
9264   DWORD err;\r
9265 \r
9266   if (appData.debugMode) {\r
9267     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9268   }\r
9269 \r
9270   *pr = NoProc;\r
9271 \r
9272   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9273   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9274   saAttr.bInheritHandle = TRUE;\r
9275   saAttr.lpSecurityDescriptor = NULL;\r
9276 \r
9277   /*\r
9278    * The steps for redirecting child's STDOUT:\r
9279    *     1. Create anonymous pipe to be STDOUT for child.\r
9280    *     2. Create a noninheritable duplicate of read handle,\r
9281    *         and close the inheritable read handle.\r
9282    */\r
9283 \r
9284   /* Create a pipe for the child's STDOUT. */\r
9285   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9286     return GetLastError();\r
9287   }\r
9288 \r
9289   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9290   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9291                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9292                              FALSE,     /* not inherited */\r
9293                              DUPLICATE_SAME_ACCESS);\r
9294   if (! fSuccess) {\r
9295     return GetLastError();\r
9296   }\r
9297   CloseHandle(hChildStdoutRd);\r
9298 \r
9299   /*\r
9300    * The steps for redirecting child's STDIN:\r
9301    *     1. Create anonymous pipe to be STDIN for child.\r
9302    *     2. Create a noninheritable duplicate of write handle,\r
9303    *         and close the inheritable write handle.\r
9304    */\r
9305 \r
9306   /* Create a pipe for the child's STDIN. */\r
9307   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9308     return GetLastError();\r
9309   }\r
9310 \r
9311   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9312   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9313                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9314                              FALSE,     /* not inherited */\r
9315                              DUPLICATE_SAME_ACCESS);\r
9316   if (! fSuccess) {\r
9317     return GetLastError();\r
9318   }\r
9319   CloseHandle(hChildStdinWr);\r
9320 \r
9321   /* Arrange to (1) look in dir for the child .exe file, and\r
9322    * (2) have dir be the child's working directory.  Interpret\r
9323    * dir relative to the directory WinBoard loaded from. */\r
9324   GetCurrentDirectory(MSG_SIZ, buf);\r
9325   SetCurrentDirectory(installDir);\r
9326   SetCurrentDirectory(dir);\r
9327 \r
9328   /* Now create the child process. */\r
9329 \r
9330   siStartInfo.cb = sizeof(STARTUPINFO);\r
9331   siStartInfo.lpReserved = NULL;\r
9332   siStartInfo.lpDesktop = NULL;\r
9333   siStartInfo.lpTitle = NULL;\r
9334   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9335   siStartInfo.cbReserved2 = 0;\r
9336   siStartInfo.lpReserved2 = NULL;\r
9337   siStartInfo.hStdInput = hChildStdinRd;\r
9338   siStartInfo.hStdOutput = hChildStdoutWr;\r
9339   siStartInfo.hStdError = hChildStdoutWr;\r
9340 \r
9341   fSuccess = CreateProcess(NULL,\r
9342                            cmdLine,        /* command line */\r
9343                            NULL,           /* process security attributes */\r
9344                            NULL,           /* primary thread security attrs */\r
9345                            TRUE,           /* handles are inherited */\r
9346                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9347                            NULL,           /* use parent's environment */\r
9348                            NULL,\r
9349                            &siStartInfo, /* STARTUPINFO pointer */\r
9350                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9351 \r
9352   err = GetLastError();\r
9353   SetCurrentDirectory(buf); /* return to prev directory */\r
9354   if (! fSuccess) {\r
9355     return err;\r
9356   }\r
9357 \r
9358   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9359     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9360     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9361   }\r
9362 \r
9363   /* Close the handles we don't need in the parent */\r
9364   CloseHandle(piProcInfo.hThread);\r
9365   CloseHandle(hChildStdinRd);\r
9366   CloseHandle(hChildStdoutWr);\r
9367 \r
9368   /* Prepare return value */\r
9369   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9370   cp->kind = CPReal;\r
9371   cp->hProcess = piProcInfo.hProcess;\r
9372   cp->pid = piProcInfo.dwProcessId;\r
9373   cp->hFrom = hChildStdoutRdDup;\r
9374   cp->hTo = hChildStdinWrDup;\r
9375 \r
9376   *pr = (void *) cp;\r
9377 \r
9378   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9379      2000 where engines sometimes don't see the initial command(s)\r
9380      from WinBoard and hang.  I don't understand how that can happen,\r
9381      but the Sleep is harmless, so I've put it in.  Others have also\r
9382      reported what may be the same problem, so hopefully this will fix\r
9383      it for them too.  */\r
9384   Sleep(500);\r
9385 \r
9386   return NO_ERROR;\r
9387 }\r
9388 \r
9389 \r
9390 void\r
9391 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9392 {\r
9393   ChildProc *cp; int result;\r
9394 \r
9395   cp = (ChildProc *) pr;\r
9396   if (cp == NULL) return;\r
9397 \r
9398   switch (cp->kind) {\r
9399   case CPReal:\r
9400     /* TerminateProcess is considered harmful, so... */\r
9401     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9402     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9403     /* The following doesn't work because the chess program\r
9404        doesn't "have the same console" as WinBoard.  Maybe\r
9405        we could arrange for this even though neither WinBoard\r
9406        nor the chess program uses a console for stdio? */\r
9407     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9408 \r
9409     /* [AS] Special termination modes for misbehaving programs... */\r
9410     if( signal & 8 ) { \r
9411         result = TerminateProcess( cp->hProcess, 0 );\r
9412 \r
9413         if ( appData.debugMode) {\r
9414             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9415         }\r
9416     }\r
9417     else if( signal & 4 ) {\r
9418         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9419 \r
9420         if( dw != WAIT_OBJECT_0 ) {\r
9421             result = TerminateProcess( cp->hProcess, 0 );\r
9422 \r
9423             if ( appData.debugMode) {\r
9424                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9425             }\r
9426 \r
9427         }\r
9428     }\r
9429 \r
9430     CloseHandle(cp->hProcess);\r
9431     break;\r
9432 \r
9433   case CPComm:\r
9434     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9435     break;\r
9436 \r
9437   case CPSock:\r
9438     closesocket(cp->sock);\r
9439     WSACleanup();\r
9440     break;\r
9441 \r
9442   case CPRcmd:\r
9443     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9444     closesocket(cp->sock);\r
9445     closesocket(cp->sock2);\r
9446     WSACleanup();\r
9447     break;\r
9448   }\r
9449   free(cp);\r
9450 }\r
9451 \r
9452 void\r
9453 InterruptChildProcess(ProcRef pr)\r
9454 {\r
9455   ChildProc *cp;\r
9456 \r
9457   cp = (ChildProc *) pr;\r
9458   if (cp == NULL) return;\r
9459   switch (cp->kind) {\r
9460   case CPReal:\r
9461     /* The following doesn't work because the chess program\r
9462        doesn't "have the same console" as WinBoard.  Maybe\r
9463        we could arrange for this even though neither WinBoard\r
9464        nor the chess program uses a console for stdio */\r
9465     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9466     break;\r
9467 \r
9468   case CPComm:\r
9469   case CPSock:\r
9470     /* Can't interrupt */\r
9471     break;\r
9472 \r
9473   case CPRcmd:\r
9474     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9475     break;\r
9476   }\r
9477 }\r
9478 \r
9479 \r
9480 int\r
9481 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9482 {\r
9483   char cmdLine[MSG_SIZ];\r
9484 \r
9485   if (port[0] == NULLCHAR) {\r
9486     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9487   } else {\r
9488     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9489   }\r
9490   return StartChildProcess(cmdLine, "", pr);\r
9491 }\r
9492 \r
9493 \r
9494 /* Code to open TCP sockets */\r
9495 \r
9496 int\r
9497 OpenTCP(char *host, char *port, ProcRef *pr)\r
9498 {\r
9499   ChildProc *cp;\r
9500   int err;\r
9501   SOCKET s;\r
9502 \r
9503   struct sockaddr_in sa, mysa;\r
9504   struct hostent FAR *hp;\r
9505   unsigned short uport;\r
9506   WORD wVersionRequested;\r
9507   WSADATA wsaData;\r
9508 \r
9509   /* Initialize socket DLL */\r
9510   wVersionRequested = MAKEWORD(1, 1);\r
9511   err = WSAStartup(wVersionRequested, &wsaData);\r
9512   if (err != 0) return err;\r
9513 \r
9514   /* Make socket */\r
9515   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9516     err = WSAGetLastError();\r
9517     WSACleanup();\r
9518     return err;\r
9519   }\r
9520 \r
9521   /* Bind local address using (mostly) don't-care values.\r
9522    */\r
9523   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9524   mysa.sin_family = AF_INET;\r
9525   mysa.sin_addr.s_addr = INADDR_ANY;\r
9526   uport = (unsigned short) 0;\r
9527   mysa.sin_port = htons(uport);\r
9528   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9529       == SOCKET_ERROR) {\r
9530     err = WSAGetLastError();\r
9531     WSACleanup();\r
9532     return err;\r
9533   }\r
9534 \r
9535   /* Resolve remote host name */\r
9536   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9537   if (!(hp = gethostbyname(host))) {\r
9538     unsigned int b0, b1, b2, b3;\r
9539 \r
9540     err = WSAGetLastError();\r
9541 \r
9542     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9543       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9544       hp->h_addrtype = AF_INET;\r
9545       hp->h_length = 4;\r
9546       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9547       hp->h_addr_list[0] = (char *) malloc(4);\r
9548       hp->h_addr_list[0][0] = (char) b0;\r
9549       hp->h_addr_list[0][1] = (char) b1;\r
9550       hp->h_addr_list[0][2] = (char) b2;\r
9551       hp->h_addr_list[0][3] = (char) b3;\r
9552     } else {\r
9553       WSACleanup();\r
9554       return err;\r
9555     }\r
9556   }\r
9557   sa.sin_family = hp->h_addrtype;\r
9558   uport = (unsigned short) atoi(port);\r
9559   sa.sin_port = htons(uport);\r
9560   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9561 \r
9562   /* Make connection */\r
9563   if (connect(s, (struct sockaddr *) &sa,\r
9564               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9565     err = WSAGetLastError();\r
9566     WSACleanup();\r
9567     return err;\r
9568   }\r
9569 \r
9570   /* Prepare return value */\r
9571   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9572   cp->kind = CPSock;\r
9573   cp->sock = s;\r
9574   *pr = (ProcRef *) cp;\r
9575 \r
9576   return NO_ERROR;\r
9577 }\r
9578 \r
9579 int\r
9580 OpenCommPort(char *name, ProcRef *pr)\r
9581 {\r
9582   HANDLE h;\r
9583   COMMTIMEOUTS ct;\r
9584   ChildProc *cp;\r
9585   char fullname[MSG_SIZ];\r
9586 \r
9587   if (*name != '\\')\r
9588     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9589   else\r
9590     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9591 \r
9592   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9593                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9594   if (h == (HANDLE) -1) {\r
9595     return GetLastError();\r
9596   }\r
9597   hCommPort = h;\r
9598 \r
9599   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9600 \r
9601   /* Accumulate characters until a 100ms pause, then parse */\r
9602   ct.ReadIntervalTimeout = 100;\r
9603   ct.ReadTotalTimeoutMultiplier = 0;\r
9604   ct.ReadTotalTimeoutConstant = 0;\r
9605   ct.WriteTotalTimeoutMultiplier = 0;\r
9606   ct.WriteTotalTimeoutConstant = 0;\r
9607   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9608 \r
9609   /* Prepare return value */\r
9610   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9611   cp->kind = CPComm;\r
9612   cp->hFrom = h;\r
9613   cp->hTo = h;\r
9614   *pr = (ProcRef *) cp;\r
9615 \r
9616   return NO_ERROR;\r
9617 }\r
9618 \r
9619 int\r
9620 OpenLoopback(ProcRef *pr)\r
9621 {\r
9622   DisplayFatalError(_("Not implemented"), 0, 1);\r
9623   return NO_ERROR;\r
9624 }\r
9625 \r
9626 \r
9627 int\r
9628 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9629 {\r
9630   ChildProc *cp;\r
9631   int err;\r
9632   SOCKET s, s2, s3;\r
9633   struct sockaddr_in sa, mysa;\r
9634   struct hostent FAR *hp;\r
9635   unsigned short uport;\r
9636   WORD wVersionRequested;\r
9637   WSADATA wsaData;\r
9638   int fromPort;\r
9639   char stderrPortStr[MSG_SIZ];\r
9640 \r
9641   /* Initialize socket DLL */\r
9642   wVersionRequested = MAKEWORD(1, 1);\r
9643   err = WSAStartup(wVersionRequested, &wsaData);\r
9644   if (err != 0) return err;\r
9645 \r
9646   /* Resolve remote host name */\r
9647   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9648   if (!(hp = gethostbyname(host))) {\r
9649     unsigned int b0, b1, b2, b3;\r
9650 \r
9651     err = WSAGetLastError();\r
9652 \r
9653     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9654       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9655       hp->h_addrtype = AF_INET;\r
9656       hp->h_length = 4;\r
9657       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9658       hp->h_addr_list[0] = (char *) malloc(4);\r
9659       hp->h_addr_list[0][0] = (char) b0;\r
9660       hp->h_addr_list[0][1] = (char) b1;\r
9661       hp->h_addr_list[0][2] = (char) b2;\r
9662       hp->h_addr_list[0][3] = (char) b3;\r
9663     } else {\r
9664       WSACleanup();\r
9665       return err;\r
9666     }\r
9667   }\r
9668   sa.sin_family = hp->h_addrtype;\r
9669   uport = (unsigned short) 514;\r
9670   sa.sin_port = htons(uport);\r
9671   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9672 \r
9673   /* Bind local socket to unused "privileged" port address\r
9674    */\r
9675   s = INVALID_SOCKET;\r
9676   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9677   mysa.sin_family = AF_INET;\r
9678   mysa.sin_addr.s_addr = INADDR_ANY;\r
9679   for (fromPort = 1023;; fromPort--) {\r
9680     if (fromPort < 0) {\r
9681       WSACleanup();\r
9682       return WSAEADDRINUSE;\r
9683     }\r
9684     if (s == INVALID_SOCKET) {\r
9685 \r
9686       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9687         err = WSAGetLastError();\r
9688         WSACleanup();\r
9689         return err;\r
9690       }\r
9691     }\r
9692     uport = (unsigned short) fromPort;\r
9693     mysa.sin_port = htons(uport);\r
9694     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9695         == SOCKET_ERROR) {\r
9696       err = WSAGetLastError();\r
9697       if (err == WSAEADDRINUSE) continue;\r
9698       WSACleanup();\r
9699       return err;\r
9700     }\r
9701     if (connect(s, (struct sockaddr *) &sa,\r
9702       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9703       err = WSAGetLastError();\r
9704       if (err == WSAEADDRINUSE) {\r
9705         closesocket(s);\r
9706         s = -1;\r
9707         continue;\r
9708       }\r
9709       WSACleanup();\r
9710       return err;\r
9711     }\r
9712     break;\r
9713   }\r
9714 \r
9715   /* Bind stderr local socket to unused "privileged" port address\r
9716    */\r
9717   s2 = INVALID_SOCKET;\r
9718   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9719   mysa.sin_family = AF_INET;\r
9720   mysa.sin_addr.s_addr = INADDR_ANY;\r
9721   for (fromPort = 1023;; fromPort--) {\r
9722     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9723     if (fromPort < 0) {\r
9724       (void) closesocket(s);\r
9725       WSACleanup();\r
9726       return WSAEADDRINUSE;\r
9727     }\r
9728     if (s2 == INVALID_SOCKET) {\r
9729       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9730         err = WSAGetLastError();\r
9731         closesocket(s);\r
9732         WSACleanup();\r
9733         return err;\r
9734       }\r
9735     }\r
9736     uport = (unsigned short) fromPort;\r
9737     mysa.sin_port = htons(uport);\r
9738     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9739         == SOCKET_ERROR) {\r
9740       err = WSAGetLastError();\r
9741       if (err == WSAEADDRINUSE) continue;\r
9742       (void) closesocket(s);\r
9743       WSACleanup();\r
9744       return err;\r
9745     }\r
9746     if (listen(s2, 1) == SOCKET_ERROR) {\r
9747       err = WSAGetLastError();\r
9748       if (err == WSAEADDRINUSE) {\r
9749         closesocket(s2);\r
9750         s2 = INVALID_SOCKET;\r
9751         continue;\r
9752       }\r
9753       (void) closesocket(s);\r
9754       (void) closesocket(s2);\r
9755       WSACleanup();\r
9756       return err;\r
9757     }\r
9758     break;\r
9759   }\r
9760   prevStderrPort = fromPort; // remember port used\r
9761   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9762 \r
9763   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9764     err = WSAGetLastError();\r
9765     (void) closesocket(s);\r
9766     (void) closesocket(s2);\r
9767     WSACleanup();\r
9768     return err;\r
9769   }\r
9770 \r
9771   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9772     err = WSAGetLastError();\r
9773     (void) closesocket(s);\r
9774     (void) closesocket(s2);\r
9775     WSACleanup();\r
9776     return err;\r
9777   }\r
9778   if (*user == NULLCHAR) user = UserName();\r
9779   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9780     err = WSAGetLastError();\r
9781     (void) closesocket(s);\r
9782     (void) closesocket(s2);\r
9783     WSACleanup();\r
9784     return err;\r
9785   }\r
9786   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9787     err = WSAGetLastError();\r
9788     (void) closesocket(s);\r
9789     (void) closesocket(s2);\r
9790     WSACleanup();\r
9791     return err;\r
9792   }\r
9793 \r
9794   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9795     err = WSAGetLastError();\r
9796     (void) closesocket(s);\r
9797     (void) closesocket(s2);\r
9798     WSACleanup();\r
9799     return err;\r
9800   }\r
9801   (void) closesocket(s2);  /* Stop listening */\r
9802 \r
9803   /* Prepare return value */\r
9804   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9805   cp->kind = CPRcmd;\r
9806   cp->sock = s;\r
9807   cp->sock2 = s3;\r
9808   *pr = (ProcRef *) cp;\r
9809 \r
9810   return NO_ERROR;\r
9811 }\r
9812 \r
9813 \r
9814 InputSourceRef\r
9815 AddInputSource(ProcRef pr, int lineByLine,\r
9816                InputCallback func, VOIDSTAR closure)\r
9817 {\r
9818   InputSource *is, *is2 = NULL;\r
9819   ChildProc *cp = (ChildProc *) pr;\r
9820 \r
9821   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9822   is->lineByLine = lineByLine;\r
9823   is->func = func;\r
9824   is->closure = closure;\r
9825   is->second = NULL;\r
9826   is->next = is->buf;\r
9827   if (pr == NoProc) {\r
9828     is->kind = CPReal;\r
9829     consoleInputSource = is;\r
9830   } else {\r
9831     is->kind = cp->kind;\r
9832     /* \r
9833         [AS] Try to avoid a race condition if the thread is given control too early:\r
9834         we create all threads suspended so that the is->hThread variable can be\r
9835         safely assigned, then let the threads start with ResumeThread.\r
9836     */\r
9837     switch (cp->kind) {\r
9838     case CPReal:\r
9839       is->hFile = cp->hFrom;\r
9840       cp->hFrom = NULL; /* now owned by InputThread */\r
9841       is->hThread =\r
9842         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9843                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9844       break;\r
9845 \r
9846     case CPComm:\r
9847       is->hFile = cp->hFrom;\r
9848       cp->hFrom = NULL; /* now owned by InputThread */\r
9849       is->hThread =\r
9850         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9851                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9852       break;\r
9853 \r
9854     case CPSock:\r
9855       is->sock = cp->sock;\r
9856       is->hThread =\r
9857         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9858                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9859       break;\r
9860 \r
9861     case CPRcmd:\r
9862       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9863       *is2 = *is;\r
9864       is->sock = cp->sock;\r
9865       is->second = is2;\r
9866       is2->sock = cp->sock2;\r
9867       is2->second = is2;\r
9868       is->hThread =\r
9869         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9870                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9871       is2->hThread =\r
9872         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9873                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9874       break;\r
9875     }\r
9876 \r
9877     if( is->hThread != NULL ) {\r
9878         ResumeThread( is->hThread );\r
9879     }\r
9880 \r
9881     if( is2 != NULL && is2->hThread != NULL ) {\r
9882         ResumeThread( is2->hThread );\r
9883     }\r
9884   }\r
9885 \r
9886   return (InputSourceRef) is;\r
9887 }\r
9888 \r
9889 void\r
9890 RemoveInputSource(InputSourceRef isr)\r
9891 {\r
9892   InputSource *is;\r
9893 \r
9894   is = (InputSource *) isr;\r
9895   is->hThread = NULL;  /* tell thread to stop */\r
9896   CloseHandle(is->hThread);\r
9897   if (is->second != NULL) {\r
9898     is->second->hThread = NULL;\r
9899     CloseHandle(is->second->hThread);\r
9900   }\r
9901 }\r
9902 \r
9903 int no_wrap(char *message, int count)\r
9904 {\r
9905     ConsoleOutput(message, count, FALSE);\r
9906     return count;\r
9907 }\r
9908 \r
9909 int\r
9910 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9911 {\r
9912   DWORD dOutCount;\r
9913   int outCount = SOCKET_ERROR;\r
9914   ChildProc *cp = (ChildProc *) pr;\r
9915   static OVERLAPPED ovl;\r
9916 \r
9917   static int line = 0;\r
9918 \r
9919   if (pr == NoProc)\r
9920   {\r
9921     if (appData.noJoin || !appData.useInternalWrap)\r
9922       return no_wrap(message, count);\r
9923     else\r
9924     {\r
9925       int width = get_term_width();\r
9926       int len = wrap(NULL, message, count, width, &line);\r
9927       char *msg = malloc(len);\r
9928       int dbgchk;\r
9929 \r
9930       if (!msg)\r
9931         return no_wrap(message, count);\r
9932       else\r
9933       {\r
9934         dbgchk = wrap(msg, message, count, width, &line);\r
9935         if (dbgchk != len && appData.debugMode)\r
9936             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9937         ConsoleOutput(msg, len, FALSE);\r
9938         free(msg);\r
9939         return len;\r
9940       }\r
9941     }\r
9942   }\r
9943 \r
9944   if (ovl.hEvent == NULL) {\r
9945     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9946   }\r
9947   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9948 \r
9949   switch (cp->kind) {\r
9950   case CPSock:\r
9951   case CPRcmd:\r
9952     outCount = send(cp->sock, message, count, 0);\r
9953     if (outCount == SOCKET_ERROR) {\r
9954       *outError = WSAGetLastError();\r
9955     } else {\r
9956       *outError = NO_ERROR;\r
9957     }\r
9958     break;\r
9959 \r
9960   case CPReal:\r
9961     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9962                   &dOutCount, NULL)) {\r
9963       *outError = NO_ERROR;\r
9964       outCount = (int) dOutCount;\r
9965     } else {\r
9966       *outError = GetLastError();\r
9967     }\r
9968     break;\r
9969 \r
9970   case CPComm:\r
9971     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9972                             &dOutCount, &ovl);\r
9973     if (*outError == NO_ERROR) {\r
9974       outCount = (int) dOutCount;\r
9975     }\r
9976     break;\r
9977   }\r
9978   return outCount;\r
9979 }\r
9980 \r
9981 void\r
9982 DoSleep(int n)\r
9983 {\r
9984     if(n != 0) Sleep(n);\r
9985 }\r
9986 \r
9987 int\r
9988 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9989                        long msdelay)\r
9990 {\r
9991   /* Ignore delay, not implemented for WinBoard */\r
9992   return OutputToProcess(pr, message, count, outError);\r
9993 }\r
9994 \r
9995 \r
9996 void\r
9997 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9998                         char *buf, int count, int error)\r
9999 {\r
10000   DisplayFatalError(_("Not implemented"), 0, 1);\r
10001 }\r
10002 \r
10003 /* see wgamelist.c for Game List functions */\r
10004 /* see wedittags.c for Edit Tags functions */\r
10005 \r
10006 \r
10007 int\r
10008 ICSInitScript()\r
10009 {\r
10010   FILE *f;\r
10011   char buf[MSG_SIZ];\r
10012   char *dummy;\r
10013 \r
10014   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10015     f = fopen(buf, "r");\r
10016     if (f != NULL) {\r
10017       ProcessICSInitScript(f);\r
10018       fclose(f);\r
10019       return TRUE;\r
10020     }\r
10021   }\r
10022   return FALSE;\r
10023 }\r
10024 \r
10025 \r
10026 VOID\r
10027 StartAnalysisClock()\r
10028 {\r
10029   if (analysisTimerEvent) return;\r
10030   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10031                                         (UINT) 2000, NULL);\r
10032 }\r
10033 \r
10034 VOID\r
10035 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10036 {\r
10037   highlightInfo.sq[0].x = fromX;\r
10038   highlightInfo.sq[0].y = fromY;\r
10039   highlightInfo.sq[1].x = toX;\r
10040   highlightInfo.sq[1].y = toY;\r
10041 }\r
10042 \r
10043 VOID\r
10044 ClearHighlights()\r
10045 {\r
10046   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10047     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10048 }\r
10049 \r
10050 VOID\r
10051 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10052 {\r
10053   premoveHighlightInfo.sq[0].x = fromX;\r
10054   premoveHighlightInfo.sq[0].y = fromY;\r
10055   premoveHighlightInfo.sq[1].x = toX;\r
10056   premoveHighlightInfo.sq[1].y = toY;\r
10057 }\r
10058 \r
10059 VOID\r
10060 ClearPremoveHighlights()\r
10061 {\r
10062   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10063     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10064 }\r
10065 \r
10066 VOID\r
10067 ShutDownFrontEnd()\r
10068 {\r
10069   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10070   DeleteClipboardTempFiles();\r
10071 }\r
10072 \r
10073 void\r
10074 BoardToTop()\r
10075 {\r
10076     if (IsIconic(hwndMain))\r
10077       ShowWindow(hwndMain, SW_RESTORE);\r
10078 \r
10079     SetActiveWindow(hwndMain);\r
10080 }\r
10081 \r
10082 /*\r
10083  * Prototypes for animation support routines\r
10084  */\r
10085 static void ScreenSquare(int column, int row, POINT * pt);\r
10086 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10087      POINT frames[], int * nFrames);\r
10088 \r
10089 \r
10090 #define kFactor 4\r
10091 \r
10092 void\r
10093 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
10094 {       // [HGM] atomic: animate blast wave\r
10095         int i;\r
10096 \r
10097         explodeInfo.fromX = fromX;\r
10098         explodeInfo.fromY = fromY;\r
10099         explodeInfo.toX = toX;\r
10100         explodeInfo.toY = toY;\r
10101         for(i=1; i<4*kFactor; i++) {\r
10102             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
10103             DrawPosition(FALSE, board);\r
10104             Sleep(appData.animSpeed);\r
10105         }\r
10106         explodeInfo.radius = 0;\r
10107         DrawPosition(TRUE, board);\r
10108 }\r
10109 \r
10110 void\r
10111 AnimateMove(board, fromX, fromY, toX, toY)\r
10112      Board board;\r
10113      int fromX;\r
10114      int fromY;\r
10115      int toX;\r
10116      int toY;\r
10117 {\r
10118   ChessSquare piece, victim = EmptySquare, victim2 = EmptySquare;\r
10119   int x = toX, y = toY, x2 = kill2X;\r
10120   POINT start, finish, mid;\r
10121   POINT frames[kFactor * 2 + 1];\r
10122   int nFrames, n;\r
10123 \r
10124   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10125 \r
10126   if (!appData.animate) return;\r
10127   if (doingSizing) return;\r
10128   if (fromY < 0 || fromX < 0) return;\r
10129   piece = board[fromY][fromX];\r
10130   if (piece >= EmptySquare) return;\r
10131 \r
10132   if(x2 >= 0) toX = kill2X, toY = kill2Y,  victim = board[killY][killX], victim2 = board[kill2Y][kill2X]; else\r
10133   if(killX >= 0) toX = killX, toY = killY, victim = board[killY][killX]; // [HGM] lion: first to kill square\r
10134 \r
10135   animInfo.from.x = fromX;\r
10136   animInfo.from.y = fromY;\r
10137 \r
10138 again:\r
10139 \r
10140   ScreenSquare(fromX, fromY, &start);\r
10141   ScreenSquare(toX, toY, &finish);\r
10142 \r
10143   /* All moves except knight jumps move in straight line */\r
10144   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10145     mid.x = start.x + (finish.x - start.x) / 2;\r
10146     mid.y = start.y + (finish.y - start.y) / 2;\r
10147   } else {\r
10148     /* Knight: make straight movement then diagonal */\r
10149     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10150        mid.x = start.x + (finish.x - start.x) / 2;\r
10151        mid.y = start.y;\r
10152      } else {\r
10153        mid.x = start.x;\r
10154        mid.y = start.y + (finish.y - start.y) / 2;\r
10155      }\r
10156   }\r
10157   \r
10158   /* Don't use as many frames for very short moves */\r
10159   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10160     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10161   else\r
10162     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10163 \r
10164   animInfo.to.x = toX;\r
10165   animInfo.to.y = toY;\r
10166   animInfo.lastpos = start;\r
10167   animInfo.piece = piece;\r
10168   for (n = 0; n < nFrames; n++) {\r
10169     animInfo.pos = frames[n];\r
10170     DrawPosition(FALSE, board);\r
10171     animInfo.lastpos = animInfo.pos;\r
10172     Sleep(appData.animSpeed);\r
10173   }\r
10174   animInfo.pos = finish;\r
10175   DrawPosition(FALSE, board);\r
10176 \r
10177   if(toX == x2 && toY == kill2Y) {\r
10178     fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1;\r
10179     board[kill2Y][kill2X] = EmptySquare; goto again;\r
10180   } // second leg\r
10181   if(toX != x || toY != y) {\r
10182     fromX = toX; fromY = toY; toX = x; toY = y;\r
10183     board[killY][killX] = EmptySquare; goto again;\r
10184   } // second leg\r
10185 \r
10186 if(victim2 != EmptySquare) board[kill2Y][kill2X] = victim2;\r
10187 if(victim  != EmptySquare) board[killY][killX] = victim;\r
10188 \r
10189   animInfo.piece = EmptySquare;\r
10190   Explode(board, fromX, fromY, toX, toY);\r
10191 }\r
10192 \r
10193 /*      Convert board position to corner of screen rect and color       */\r
10194 \r
10195 static void\r
10196 ScreenSquare(column, row, pt)\r
10197      int column; int row; POINT * pt;\r
10198 {\r
10199   if (flipView) {\r
10200     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10201     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10202   } else {\r
10203     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10204     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10205   }\r
10206 }\r
10207 \r
10208 /*      Generate a series of frame coords from start->mid->finish.\r
10209         The movement rate doubles until the half way point is\r
10210         reached, then halves back down to the final destination,\r
10211         which gives a nice slow in/out effect. The algorithmn\r
10212         may seem to generate too many intermediates for short\r
10213         moves, but remember that the purpose is to attract the\r
10214         viewers attention to the piece about to be moved and\r
10215         then to where it ends up. Too few frames would be less\r
10216         noticeable.                                             */\r
10217 \r
10218 static void\r
10219 Tween(start, mid, finish, factor, frames, nFrames)\r
10220      POINT * start; POINT * mid;\r
10221      POINT * finish; int factor;\r
10222      POINT frames[]; int * nFrames;\r
10223 {\r
10224   int n, fraction = 1, count = 0;\r
10225 \r
10226   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10227   for (n = 0; n < factor; n++)\r
10228     fraction *= 2;\r
10229   for (n = 0; n < factor; n++) {\r
10230     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10231     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10232     count ++;\r
10233     fraction = fraction / 2;\r
10234   }\r
10235   \r
10236   /* Midpoint */\r
10237   frames[count] = *mid;\r
10238   count ++;\r
10239   \r
10240   /* Slow out, stepping 1/2, then 1/4, ... */\r
10241   fraction = 2;\r
10242   for (n = 0; n < factor; n++) {\r
10243     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10244     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10245     count ++;\r
10246     fraction = fraction * 2;\r
10247   }\r
10248   *nFrames = count;\r
10249 }\r
10250 \r
10251 void\r
10252 SettingsPopUp(ChessProgramState *cps)\r
10253 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10254       EngineOptionsPopup(savedHwnd, cps);\r
10255 }\r
10256 \r
10257 int flock(int fid, int code)\r
10258 {\r
10259     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10260     OVERLAPPED ov;\r
10261     ov.hEvent = NULL;\r
10262     ov.Offset = 0;\r
10263     ov.OffsetHigh = 0;\r
10264     switch(code) {\r
10265       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10266 \r
10267       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10268       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10269       default: return -1;\r
10270     }\r
10271     return 0;\r
10272 }\r
10273 \r
10274 char *\r
10275 Col2Text (int n)\r
10276 {\r
10277     static int i=0;\r
10278     static char col[8][20];\r
10279     COLORREF color = *(COLORREF *) colorVariable[n];\r
10280     i = i+1 & 7;\r
10281     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10282     return col[i];\r
10283 }\r
10284 \r
10285 void\r
10286 ActivateTheme (int new)\r
10287 {   // Redo initialization of features depending on options that can occur in themes\r
10288    InitTextures();\r
10289    if(new) InitDrawingColors();\r
10290    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10291    InitDrawingSizes(boardSize, 0);\r
10292    InvalidateRect(hwndMain, NULL, TRUE);\r
10293 }\r