8d35e2c36c0b8be3823639bc97a29e95cc8d96cb
[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 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 #define SLASH '/'\r
96 #define DATADIR "~~"\r
97 \r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
99 \r
100   int myrandom(void);\r
101   void mysrandom(unsigned int seed);\r
102 \r
103 extern int whiteFlag, blackFlag;\r
104 Boolean flipClock = FALSE;\r
105 extern HANDLE chatHandle[];\r
106 extern enum ICS_TYPE ics_type;\r
107 \r
108 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
109 int  MyGetFullPathName P((char *name, char *fullname));\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
111 VOID NewVariantPopup(HWND hwnd);\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
113                    /*char*/int promoChar));\r
114 void DisplayMove P((int moveNumber));\r
115 void ChatPopUp P((char *s));\r
116 typedef struct {\r
117   ChessSquare piece;  \r
118   POINT pos;      /* window coordinates of current pos */\r
119   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
120   POINT from;     /* board coordinates of the piece's orig pos */\r
121   POINT to;       /* board coordinates of the piece's new pos */\r
122 } AnimInfo;\r
123 \r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
125 \r
126 typedef struct {\r
127   POINT start;    /* window coordinates of start pos */\r
128   POINT pos;      /* window coordinates of current pos */\r
129   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
130   POINT from;     /* board coordinates of the piece's orig pos */\r
131   ChessSquare piece;\r
132 } DragInfo;\r
133 \r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
135 \r
136 typedef struct {\r
137   POINT sq[2];    /* board coordinates of from, to squares */\r
138 } HighlightInfo;\r
139 \r
140 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
144 \r
145 typedef struct { // [HGM] atomic\r
146   int fromX, fromY, toX, toY, radius;\r
147 } ExplodeInfo;\r
148 \r
149 static ExplodeInfo explodeInfo;\r
150 \r
151 /* Window class names */\r
152 char szAppName[] = "WinBoard";\r
153 char szConsoleName[] = "WBConsole";\r
154 \r
155 /* Title bar text */\r
156 char szTitle[] = "WinBoard";\r
157 char szConsoleTitle[] = "I C S Interaction";\r
158 \r
159 char *programName;\r
160 char *settingsFileName;\r
161 Boolean saveSettingsOnExit;\r
162 char installDir[MSG_SIZ];\r
163 int errorExitStatus;\r
164 \r
165 BoardSize boardSize;\r
166 Boolean chessProgram;\r
167 //static int boardX, boardY;\r
168 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
169 int squareSize, lineGap, minorSize;\r
170 static int winW, winH;\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
172 static int logoHeight = 0;\r
173 static char messageText[MESSAGE_TEXT_MAX];\r
174 static int clockTimerEvent = 0;\r
175 static int loadGameTimerEvent = 0;\r
176 static int analysisTimerEvent = 0;\r
177 static DelayedEventCallback delayedTimerCallback;\r
178 static int delayedTimerEvent = 0;\r
179 static int buttonCount = 2;\r
180 char *icsTextMenuString;\r
181 char *icsNames;\r
182 char *firstChessProgramNames;\r
183 char *secondChessProgramNames;\r
184 \r
185 #define PALETTESIZE 256\r
186 \r
187 HINSTANCE hInst;          /* current instance */\r
188 Boolean alwaysOnTop = FALSE;\r
189 RECT boardRect;\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
191   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
193 HPALETTE hPal;\r
194 ColorClass currentColorClass;\r
195 \r
196 static HWND savedHwnd;\r
197 HWND hCommPort = NULL;    /* currently open comm port */\r
198 static HWND hwndPause;    /* pause button */\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,\r
201   blackSquareBrush, /* [HGM] for band between board and holdings */\r
202   explodeBrush,     /* [HGM] atomic */\r
203   markerBrush[8],   /* [HGM] markers */\r
204   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
207 static HPEN gridPen = NULL;\r
208 static HPEN highlightPen = NULL;\r
209 static HPEN premovePen = NULL;\r
210 static NPLOGPALETTE pLogPal;\r
211 static BOOL paletteChanged = FALSE;\r
212 static HICON iconWhite, iconBlack, iconCurrent;\r
213 static int doingSizing = FALSE;\r
214 static int lastSizing = 0;\r
215 static int prevStderrPort;\r
216 static HBITMAP userLogo;\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 \r
229 #if defined(_winmajor)\r
230 #define oldDialog (_winmajor < 4)\r
231 #else\r
232 #define oldDialog 0\r
233 #endif\r
234 #endif\r
235 \r
236 #define INTERNATIONAL\r
237 \r
238 #ifdef INTERNATIONAL\r
239 #  define _(s) T_(s)\r
240 #  define N_(s) s\r
241 #else\r
242 #  define _(s) s\r
243 #  define N_(s) s\r
244 #  define T_(s) s\r
245 #  define Translate(x, y)\r
246 #  define LoadLanguageFile(s)\r
247 #endif\r
248 \r
249 #ifdef INTERNATIONAL\r
250 \r
251 Boolean barbaric; // flag indicating if translation is needed\r
252 \r
253 // list of item numbers used in each dialog (used to alter language at run time)\r
254 \r
255 #define ABOUTBOX -1  /* not sure why these are needed */\r
256 #define ABOUTBOX2 -1\r
257 \r
258 int dialogItems[][42] = {\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
261   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
263   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
264   OPT_Ranget, IDOK, IDCANCEL }, \r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
266   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
269   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
272   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
273 { ABOUTBOX2, IDC_ChessBoard }, \r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
275   OPT_GameListClose, IDC_GameListDoFilter }, \r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
277 { DLG_Error, IDOK }, \r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
279   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
282   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
283   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
284 { DLG_IndexNumber, IDC_Index }, \r
285 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
286 { DLG_TypeInName, IDOK, IDCANCEL }, \r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
288   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
290   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
291   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
292   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
293   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
294   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
295   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
297   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
298   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
299   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
300   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
301   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
302   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
303   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
304   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
306   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
307   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
308   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
309   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
310   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
311   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
312   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
314   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
315   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
316   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
317   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
318   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
319   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
320   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
321   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
323   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
324   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
325   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
326   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
329   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
330 { DLG_MoveHistory }, \r
331 { DLG_EvalGraph }, \r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
335   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
336   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
337   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
339   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
340   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
341 { 0 }\r
342 };\r
343 \r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
345 static int lastChecked;\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
347 extern int tinyLayout;\r
348 extern char * menuBarText[][10];\r
349 \r
350 void\r
351 LoadLanguageFile(char *name)\r
352 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
353     FILE *f;\r
354     int i=0, j=0, n=0, k;\r
355     char buf[MSG_SIZ];\r
356 \r
357     if(!name || name[0] == NULLCHAR) return;\r
358       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
359     appData.language = oldLanguage;\r
360     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
361     if((f = fopen(buf, "r")) == NULL) return;\r
362     while((k = fgetc(f)) != EOF) {\r
363         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
364         languageBuf[i] = k;\r
365         if(k == '\n') {\r
366             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
367                 char *p;\r
368                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
369                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
370                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
371                         english[j] = languageBuf + n + 1; *p = 0;\r
372                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
374                     }\r
375                 }\r
376             }\r
377             n = i + 1;\r
378         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
379             switch(k) {\r
380               case 'n': k = '\n'; break;\r
381               case 'r': k = '\r'; break;\r
382               case 't': k = '\t'; break;\r
383             }\r
384             languageBuf[--i] = k;\r
385         }\r
386         i++;\r
387     }\r
388     fclose(f);\r
389     barbaric = (j != 0);\r
390     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
391 }\r
392 \r
393 char *\r
394 T_(char *s)\r
395 {   // return the translation of the given string\r
396     // efficiency can be improved a lot...\r
397     int i=0;\r
398     static char buf[MSG_SIZ];\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
400     if(!barbaric) return s;\r
401     if(!s) return ""; // sanity\r
402     while(english[i]) {\r
403         if(!strcmp(s, english[i])) return foreign[i];\r
404         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
405             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
406             return buf;\r
407         }\r
408         i++;\r
409     }\r
410     return s;\r
411 }\r
412 \r
413 void\r
414 Translate(HWND hDlg, int dialogID)\r
415 {   // translate all text items in the given dialog\r
416     int i=0, j, k;\r
417     char buf[MSG_SIZ], *s;\r
418     if(!barbaric) return;\r
419     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
420     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
421     GetWindowText( hDlg, buf, MSG_SIZ );\r
422     s = T_(buf);\r
423     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
424     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
425         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
426         if(strlen(buf) == 0) continue;\r
427         s = T_(buf);\r
428         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
429     }\r
430 }\r
431 \r
432 HMENU\r
433 TranslateOneMenu(int i, HMENU subMenu)\r
434 {\r
435     int j;\r
436     static MENUITEMINFO info;\r
437 \r
438     info.cbSize = sizeof(MENUITEMINFO);\r
439     info.fMask = MIIM_STATE | MIIM_TYPE;\r
440           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
441             char buf[MSG_SIZ];\r
442             info.dwTypeData = buf;\r
443             info.cch = sizeof(buf);\r
444             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
445             if(i < 10) {\r
446                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
447                 else menuText[i][j] = strdup(buf); // remember original on first change\r
448             }\r
449             if(buf[0] == NULLCHAR) continue;\r
450             info.dwTypeData = T_(buf);\r
451             info.cch = strlen(buf)+1;\r
452             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
453           }\r
454     return subMenu;\r
455 }\r
456 \r
457 void\r
458 TranslateMenus(int addLanguage)\r
459 {\r
460     int i;\r
461     WIN32_FIND_DATA fileData;\r
462     HANDLE hFind;\r
463 #define IDM_English 1970\r
464     if(1) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
467           HMENU subMenu = GetSubMenu(mainMenu, i);\r
468           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
469                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
470           TranslateOneMenu(i, subMenu);\r
471         }\r
472         DrawMenuBar(hwndMain);\r
473     }\r
474 \r
475     if(!addLanguage) return;\r
476     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
477         HMENU mainMenu = GetMenu(hwndMain);\r
478         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
479         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
480         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
481         i = 0; lastChecked = IDM_English;\r
482         do {\r
483             char *p, *q = fileData.cFileName;\r
484             int checkFlag = MF_UNCHECKED;\r
485             languageFile[i] = strdup(q);\r
486             if(barbaric && !strcmp(oldLanguage, q)) {\r
487                 checkFlag = MF_CHECKED;\r
488                 lastChecked = IDM_English + i + 1;\r
489                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
490             }\r
491             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
492             p = strstr(fileData.cFileName, ".lng");\r
493             if(p) *p = 0;\r
494             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
495         } while(FindNextFile(hFind, &fileData));\r
496         FindClose(hFind);\r
497     }\r
498 }\r
499 \r
500 #endif\r
501 \r
502 #define IDM_RecentEngines 3000\r
503 \r
504 void\r
505 RecentEngineMenu (char *s)\r
506 {\r
507     if(appData.icsActive) return;\r
508     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
509         HMENU mainMenu = GetMenu(hwndMain);\r
510         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
511         int i=IDM_RecentEngines;\r
512         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
513         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
514         while(*s) {\r
515           char *p = strchr(s, '\n');\r
516           if(p == NULL) return; // malformed!\r
517           *p = NULLCHAR;\r
518           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
519           *p = '\n';\r
520           s = p+1;\r
521         }\r
522     }\r
523 }\r
524 \r
525 \r
526 typedef struct {\r
527   char *name;\r
528   int squareSize;\r
529   int lineGap;\r
530   int smallLayout;\r
531   int tinyLayout;\r
532   int cliWidth, cliHeight;\r
533 } SizeInfo;\r
534 \r
535 SizeInfo sizeInfo[] = \r
536 {\r
537   { "tiny",     21, 0, 1, 2, 0, 0 },\r
538   { "teeny",    25, 1, 1, 2, 0, 0 },\r
539   { "dinky",    29, 1, 1, 2, 0, 0 },\r
540   { "petite",   33, 1, 1, 2, 0, 0 },\r
541   { "slim",     37, 2, 1, 1, 0, 0 },\r
542   { "small",    40, 2, 1, 1, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
574   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
575   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
576   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
577   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
578   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[3][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },\r
607   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
608 };\r
609 \r
610 \r
611 MySound sounds[(int)NSoundClasses];\r
612 MyTextAttribs textAttribs[(int)NColorClasses];\r
613 \r
614 MyColorizeAttribs colorizeAttribs[] = {\r
615   { (COLORREF)0, 0, N_("Shout Text") },\r
616   { (COLORREF)0, 0, N_("SShout/CShout") },\r
617   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
618   { (COLORREF)0, 0, N_("Channel Text") },\r
619   { (COLORREF)0, 0, N_("Kibitz Text") },\r
620   { (COLORREF)0, 0, N_("Tell Text") },\r
621   { (COLORREF)0, 0, N_("Challenge Text") },\r
622   { (COLORREF)0, 0, N_("Request Text") },\r
623   { (COLORREF)0, 0, N_("Seek Text") },\r
624   { (COLORREF)0, 0, N_("Normal Text") },\r
625   { (COLORREF)0, 0, N_("None") }\r
626 };\r
627 \r
628 \r
629 \r
630 static char *commentTitle;\r
631 static char *commentText;\r
632 static int commentIndex;\r
633 static Boolean editComment = FALSE;\r
634 \r
635 \r
636 char errorTitle[MSG_SIZ];\r
637 char errorMessage[2*MSG_SIZ];\r
638 HWND errorDialog = NULL;\r
639 BOOLEAN moveErrorMessageUp = FALSE;\r
640 BOOLEAN consoleEcho = TRUE;\r
641 CHARFORMAT consoleCF;\r
642 COLORREF consoleBackgroundColor;\r
643 \r
644 char *programVersion;\r
645 \r
646 #define CPReal 1\r
647 #define CPComm 2\r
648 #define CPSock 3\r
649 #define CPRcmd 4\r
650 typedef int CPKind;\r
651 \r
652 typedef struct {\r
653   CPKind kind;\r
654   HANDLE hProcess;\r
655   DWORD pid;\r
656   HANDLE hTo;\r
657   HANDLE hFrom;\r
658   SOCKET sock;\r
659   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
660 } ChildProc;\r
661 \r
662 #define INPUT_SOURCE_BUF_SIZE 4096\r
663 \r
664 typedef struct _InputSource {\r
665   CPKind kind;\r
666   HANDLE hFile;\r
667   SOCKET sock;\r
668   int lineByLine;\r
669   HANDLE hThread;\r
670   DWORD id;\r
671   char buf[INPUT_SOURCE_BUF_SIZE];\r
672   char *next;\r
673   DWORD count;\r
674   int error;\r
675   InputCallback func;\r
676   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
677   VOIDSTAR closure;\r
678 } InputSource;\r
679 \r
680 InputSource *consoleInputSource;\r
681 \r
682 DCB dcb;\r
683 \r
684 /* forward */\r
685 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
686 VOID ConsoleCreate();\r
687 LRESULT CALLBACK\r
688   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
689 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
690 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
691 VOID ParseCommSettings(char *arg, DCB *dcb);\r
692 LRESULT CALLBACK\r
693   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
694 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
695 void ParseIcsTextMenu(char *icsTextMenuString);\r
696 VOID PopUpNameDialog(char firstchar);\r
697 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
698 \r
699 /* [AS] */\r
700 int NewGameFRC();\r
701 int GameListOptions();\r
702 \r
703 int dummy; // [HGM] for obsolete args\r
704 \r
705 HWND hwndMain = NULL;        /* root window*/\r
706 HWND hwndConsole = NULL;\r
707 HWND commentDialog = NULL;\r
708 HWND moveHistoryDialog = NULL;\r
709 HWND evalGraphDialog = NULL;\r
710 HWND engineOutputDialog = NULL;\r
711 HWND gameListDialog = NULL;\r
712 HWND editTagsDialog = NULL;\r
713 \r
714 int commentUp = FALSE;\r
715 \r
716 WindowPlacement wpMain;\r
717 WindowPlacement wpConsole;\r
718 WindowPlacement wpComment;\r
719 WindowPlacement wpMoveHistory;\r
720 WindowPlacement wpEvalGraph;\r
721 WindowPlacement wpEngineOutput;\r
722 WindowPlacement wpGameList;\r
723 WindowPlacement wpTags;\r
724 \r
725 VOID EngineOptionsPopup(); // [HGM] settings\r
726 \r
727 VOID GothicPopUp(char *title, VariantClass variant);\r
728 /*\r
729  * Setting "frozen" should disable all user input other than deleting\r
730  * the window.  We do this while engines are initializing themselves.\r
731  */\r
732 static int frozen = 0;\r
733 static int oldMenuItemState[MENU_BAR_ITEMS];\r
734 void FreezeUI()\r
735 {\r
736   HMENU hmenu;\r
737   int i;\r
738 \r
739   if (frozen) return;\r
740   frozen = 1;\r
741   hmenu = GetMenu(hwndMain);\r
742   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
743     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
744   }\r
745   DrawMenuBar(hwndMain);\r
746 }\r
747 \r
748 /* Undo a FreezeUI */\r
749 void ThawUI()\r
750 {\r
751   HMENU hmenu;\r
752   int i;\r
753 \r
754   if (!frozen) return;\r
755   frozen = 0;\r
756   hmenu = GetMenu(hwndMain);\r
757   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
758     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
759   }\r
760   DrawMenuBar(hwndMain);\r
761 }\r
762 \r
763 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
764 \r
765 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
766 #ifdef JAWS\r
767 #include "jaws.c"\r
768 #else\r
769 #define JAWS_INIT\r
770 #define JAWS_ARGS\r
771 #define JAWS_ALT_INTERCEPT\r
772 #define JAWS_KBUP_NAVIGATION\r
773 #define JAWS_KBDOWN_NAVIGATION\r
774 #define JAWS_MENU_ITEMS\r
775 #define JAWS_SILENCE\r
776 #define JAWS_REPLAY\r
777 #define JAWS_ACCEL\r
778 #define JAWS_COPYRIGHT\r
779 #define JAWS_DELETE(X) X\r
780 #define SAYMACHINEMOVE()\r
781 #define SAY(X)\r
782 #endif\r
783 \r
784 /*---------------------------------------------------------------------------*\\r
785  *\r
786  * WinMain\r
787  *\r
788 \*---------------------------------------------------------------------------*/\r
789 \r
790 static void HandleMessage P((MSG *message));\r
791 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
792 \r
793 int APIENTRY\r
794 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
795         LPSTR lpCmdLine, int nCmdShow)\r
796 {\r
797   MSG msg;\r
798 //  INITCOMMONCONTROLSEX ex;\r
799 \r
800   debugFP = stderr;\r
801 \r
802   LoadLibrary("RICHED32.DLL");\r
803   consoleCF.cbSize = sizeof(CHARFORMAT);\r
804 \r
805   if (!InitApplication(hInstance)) {\r
806     return (FALSE);\r
807   }\r
808   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
809     return (FALSE);\r
810   }\r
811 \r
812   JAWS_INIT\r
813   TranslateMenus(1);\r
814 \r
815 //  InitCommonControlsEx(&ex);\r
816   InitCommonControls();\r
817 \r
818   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
819   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
820   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
821 \r
822   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
823 \r
824   while (GetMessage(&msg, /* message structure */\r
825                     NULL, /* handle of window receiving the message */\r
826                     0,    /* lowest message to examine */\r
827                     0))   /* highest message to examine */\r
828     {\r
829         HandleMessage(&msg);\r
830     }\r
831 \r
832 \r
833   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
834 }\r
835 \r
836 static void\r
837 HandleMessage (MSG *message)\r
838 {\r
839     MSG msg = *message;\r
840 \r
841       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
842         // [HGM] navigate: switch between all windows with tab\r
843         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
844         int i, currentElement = 0;\r
845 \r
846         // first determine what element of the chain we come from (if any)\r
847         if(appData.icsActive) {\r
848             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
849             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
850         }\r
851         if(engineOutputDialog && EngineOutputIsUp()) {\r
852             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
853             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
854         }\r
855         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
856             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
857         }\r
858         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
859         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
860         if(msg.hwnd == e1)                 currentElement = 2; else\r
861         if(msg.hwnd == e2)                 currentElement = 3; else\r
862         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
863         if(msg.hwnd == mh)                currentElement = 4; else\r
864         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
865         if(msg.hwnd == hText)  currentElement = 5; else\r
866         if(msg.hwnd == hInput) currentElement = 6; else\r
867         for (i = 0; i < N_BUTTONS; i++) {\r
868             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
869         }\r
870 \r
871         // determine where to go to\r
872         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
873           do {\r
874             currentElement = (currentElement + direction) % 7;\r
875             switch(currentElement) {\r
876                 case 0:\r
877                   h = hwndMain; break; // passing this case always makes the loop exit\r
878                 case 1:\r
879                   h = buttonDesc[0].hwnd; break; // could be NULL\r
880                 case 2:\r
881                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
882                   h = e1; break;\r
883                 case 3:\r
884                   if(!EngineOutputIsUp()) continue;\r
885                   h = e2; break;\r
886                 case 4:\r
887                   if(!MoveHistoryIsUp()) continue;\r
888                   h = mh; break;\r
889 //              case 6: // input to eval graph does not seem to get here!\r
890 //                if(!EvalGraphIsUp()) continue;\r
891 //                h = evalGraphDialog; break;\r
892                 case 5:\r
893                   if(!appData.icsActive) continue;\r
894                   SAY("display");\r
895                   h = hText; break;\r
896                 case 6:\r
897                   if(!appData.icsActive) continue;\r
898                   SAY("input");\r
899                   h = hInput; break;\r
900             }\r
901           } while(h == 0);\r
902 \r
903           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
904           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
905           SetFocus(h);\r
906 \r
907           return; // this message now has been processed\r
908         }\r
909       }\r
910 \r
911       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
912           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
913           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
914           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
915           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
916           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
917           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
918           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
919           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
920           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
921         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
922         for(i=0; i<MAX_CHAT; i++) \r
923             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
924                 done = 1; break;\r
925         }\r
926         if(done) return; // [HGM] chat: end patch\r
927         TranslateMessage(&msg); /* Translates virtual key codes */\r
928         DispatchMessage(&msg);  /* Dispatches message to window */\r
929       }\r
930 }\r
931 \r
932 void\r
933 DoEvents ()\r
934 { /* Dispatch pending messages */\r
935   MSG msg;\r
936   while (PeekMessage(&msg, /* message structure */\r
937                      NULL, /* handle of window receiving the message */\r
938                      0,    /* lowest message to examine */\r
939                      0,    /* highest message to examine */\r
940                      PM_REMOVE))\r
941     {\r
942         HandleMessage(&msg);\r
943     }\r
944 }\r
945 \r
946 /*---------------------------------------------------------------------------*\\r
947  *\r
948  * Initialization functions\r
949  *\r
950 \*---------------------------------------------------------------------------*/\r
951 \r
952 void\r
953 SetUserLogo()\r
954 {   // update user logo if necessary\r
955     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
956 \r
957     if(appData.autoLogo) {\r
958           curName = UserName();\r
959           if(strcmp(curName, oldUserName)) {\r
960                 GetCurrentDirectory(MSG_SIZ, dir);\r
961                 SetCurrentDirectory(installDir);\r
962                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
963                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
964                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
965                 if(userLogo == NULL)\r
966                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
967                 SetCurrentDirectory(dir); /* return to prev directory */\r
968           }\r
969     }\r
970 }\r
971 \r
972 BOOL\r
973 InitApplication(HINSTANCE hInstance)\r
974 {\r
975   WNDCLASS wc;\r
976 \r
977   /* Fill in window class structure with parameters that describe the */\r
978   /* main window. */\r
979 \r
980   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
981   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
982   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
983   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
984   wc.hInstance     = hInstance;         /* Owner of this class */\r
985   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
986   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
987   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
988   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
989   wc.lpszClassName = szAppName;                 /* Name to register as */\r
990 \r
991   /* Register the window class and return success/failure code. */\r
992   if (!RegisterClass(&wc)) return FALSE;\r
993 \r
994   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
995   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
996   wc.cbClsExtra    = 0;\r
997   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
998   wc.hInstance     = hInstance;\r
999   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
1000   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1001   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1002   wc.lpszMenuName  = NULL;\r
1003   wc.lpszClassName = szConsoleName;\r
1004 \r
1005   if (!RegisterClass(&wc)) return FALSE;\r
1006   return TRUE;\r
1007 }\r
1008 \r
1009 \r
1010 /* Set by InitInstance, used by EnsureOnScreen */\r
1011 int screenHeight, screenWidth;\r
1012 RECT screenGeometry;\r
1013 \r
1014 void\r
1015 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1016 {\r
1017 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1018   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1019   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1020   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1021   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1022   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1023 }\r
1024 \r
1025 VOID\r
1026 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1027 {\r
1028   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1029   GetCurrentDirectory(MSG_SIZ, dir);\r
1030   SetCurrentDirectory(installDir);\r
1031   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1032       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1033 \r
1034       if (cps->programLogo == NULL && appData.debugMode) {\r
1035           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1036       }\r
1037   } else if(appData.autoLogo) {\r
1038       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1039         char *opponent = "";\r
1040         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1041         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1042         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1043         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1044             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1045             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1046         }\r
1047       } else\r
1048       if(appData.directory[n] && appData.directory[n][0]) {\r
1049         SetCurrentDirectory(appData.directory[n]);\r
1050         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1051       }\r
1052   }\r
1053   SetCurrentDirectory(dir); /* return to prev directory */\r
1054 }\r
1055 \r
1056 VOID\r
1057 InitTextures()\r
1058 {\r
1059   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1060   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1061   \r
1062   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1063       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1064       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1065       liteBackTextureMode = appData.liteBackTextureMode;\r
1066 \r
1067       if (liteBackTexture == NULL && appData.debugMode) {\r
1068           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1069       }\r
1070   }\r
1071   \r
1072   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1073       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1074       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1075       darkBackTextureMode = appData.darkBackTextureMode;\r
1076 \r
1077       if (darkBackTexture == NULL && appData.debugMode) {\r
1078           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1079       }\r
1080   }\r
1081 }\r
1082 \r
1083 #ifndef SM_CXVIRTUALSCREEN\r
1084 #define SM_CXVIRTUALSCREEN 78\r
1085 #endif\r
1086 #ifndef SM_CYVIRTUALSCREEN\r
1087 #define SM_CYVIRTUALSCREEN 79\r
1088 #endif\r
1089 #ifndef SM_XVIRTUALSCREEN \r
1090 #define SM_XVIRTUALSCREEN 76\r
1091 #endif\r
1092 #ifndef SM_YVIRTUALSCREEN \r
1093 #define SM_YVIRTUALSCREEN 77\r
1094 #endif\r
1095 \r
1096 VOID\r
1097 InitGeometry()\r
1098 {\r
1099   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1100   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1101   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1102   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1103   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1104   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1105   screenGeometry.right = screenGeometry.left + screenWidth;\r
1106   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1107 }\r
1108 \r
1109 ChessProgramState broadcast;\r
1110 \r
1111 BOOL\r
1112 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1113 {\r
1114   HWND hwnd; /* Main window handle. */\r
1115   int ibs;\r
1116   WINDOWPLACEMENT wp;\r
1117   char *filepart;\r
1118 \r
1119   hInst = hInstance;    /* Store instance handle in our global variable */\r
1120   programName = szAppName;\r
1121 \r
1122   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1123     *filepart = NULLCHAR;\r
1124     SetCurrentDirectory(installDir);\r
1125   } else {\r
1126     GetCurrentDirectory(MSG_SIZ, installDir);\r
1127   }\r
1128   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1129   InitGeometry();\r
1130   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1131   /* xboard, and older WinBoards, controlled the move sound with the\r
1132      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1133      always turn the option on (so that the backend will call us),\r
1134      then let the user turn the sound off by setting it to silence if\r
1135      desired.  To accommodate old winboard.ini files saved by old\r
1136      versions of WinBoard, we also turn off the sound if the option\r
1137      was initially set to false. [HGM] taken out of InitAppData */\r
1138   if (!appData.ringBellAfterMoves) {\r
1139     sounds[(int)SoundMove].name = strdup("");\r
1140     appData.ringBellAfterMoves = TRUE;\r
1141   }\r
1142   if (appData.debugMode) {\r
1143     char *c = appData.nameOfDebugFile;\r
1144     if(strstr(c, "///") == c) {\r
1145       broadcast.which = "broadcaster";\r
1146       broadcast.pr   = NoProc;\r
1147       broadcast.isr  = NULL;\r
1148       broadcast.program = c + 3;\r
1149       broadcast.dir  = ".";\r
1150       broadcast.host = "localhost";\r
1151       StartChessProgram(&broadcast);\r
1152       debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");\r
1153     } else\r
1154     debugFP = fopen(c, "w");\r
1155     setbuf(debugFP, NULL);\r
1156   }\r
1157 \r
1158   LoadLanguageFile(appData.language);\r
1159 \r
1160   InitBackEnd1();\r
1161 \r
1162 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1163 //  InitEngineUCI( installDir, &second );\r
1164 \r
1165   /* Create a main window for this application instance. */\r
1166   hwnd = CreateWindow(szAppName, szTitle,\r
1167                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1168                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1169                       NULL, NULL, hInstance, NULL);\r
1170   hwndMain = hwnd;\r
1171 \r
1172   /* If window could not be created, return "failure" */\r
1173   if (!hwnd) {\r
1174     return (FALSE);\r
1175   }\r
1176 \r
1177   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1178   LoadLogo(&first, 0, FALSE);\r
1179   LoadLogo(&second, 1, appData.icsActive);\r
1180 \r
1181   SetUserLogo();\r
1182 \r
1183   iconWhite = LoadIcon(hInstance, "icon_white");\r
1184   iconBlack = LoadIcon(hInstance, "icon_black");\r
1185   iconCurrent = iconWhite;\r
1186   InitDrawingColors();\r
1187 \r
1188   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1189   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1190     /* Compute window size for each board size, and use the largest\r
1191        size that fits on this screen as the default. */\r
1192     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1193     if (boardSize == (BoardSize)-1 &&\r
1194         winH <= screenHeight\r
1195            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1196         && winW <= screenWidth) {\r
1197       boardSize = (BoardSize)ibs;\r
1198     }\r
1199   }\r
1200 \r
1201   InitDrawingSizes(boardSize, 0);\r
1202   RecentEngineMenu(appData.recentEngineList);\r
1203   InitMenuChecks();\r
1204   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1205 \r
1206   /* [AS] Load textures if specified */\r
1207   InitTextures();\r
1208 \r
1209   mysrandom( (unsigned) time(NULL) );\r
1210 \r
1211   /* [AS] Restore layout */\r
1212   if( wpMoveHistory.visible ) {\r
1213       MoveHistoryPopUp();\r
1214   }\r
1215 \r
1216   if( wpEvalGraph.visible ) {\r
1217       EvalGraphPopUp();\r
1218   }\r
1219 \r
1220   if( wpEngineOutput.visible ) {\r
1221       EngineOutputPopUp();\r
1222   }\r
1223 \r
1224   /* Make the window visible; update its client area; and return "success" */\r
1225   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1226   wp.length = sizeof(WINDOWPLACEMENT);\r
1227   wp.flags = 0;\r
1228   wp.showCmd = nCmdShow;\r
1229   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1230   wp.rcNormalPosition.left = wpMain.x;\r
1231   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1232   wp.rcNormalPosition.top = wpMain.y;\r
1233   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1234   SetWindowPlacement(hwndMain, &wp);\r
1235 \r
1236   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1237 \r
1238   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1239                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1240 \r
1241   if (hwndConsole) {\r
1242 #if AOT_CONSOLE\r
1243     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1244                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1245 #endif\r
1246     ShowWindow(hwndConsole, nCmdShow);\r
1247     SetActiveWindow(hwndConsole);\r
1248   }\r
1249   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1250   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1251 \r
1252   return TRUE;\r
1253 \r
1254 }\r
1255 \r
1256 VOID\r
1257 InitMenuChecks()\r
1258 {\r
1259   HMENU hmenu = GetMenu(hwndMain);\r
1260 \r
1261   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1262                         MF_BYCOMMAND|((appData.icsActive &&\r
1263                                        *appData.icsCommPort != NULLCHAR) ?\r
1264                                       MF_ENABLED : MF_GRAYED));\r
1265   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1266                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1267                                      MF_CHECKED : MF_UNCHECKED));\r
1268   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1269 }\r
1270 \r
1271 //---------------------------------------------------------------------------------------------------------\r
1272 \r
1273 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1274 #define XBOARD FALSE\r
1275 \r
1276 #define OPTCHAR "/"\r
1277 #define SEPCHAR "="\r
1278 #define TOPLEVEL 0\r
1279 \r
1280 #include "args.h"\r
1281 \r
1282 // front-end part of option handling\r
1283 \r
1284 VOID\r
1285 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1286 {\r
1287   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1288   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1289   DeleteDC(hdc);\r
1290   lf->lfWidth = 0;\r
1291   lf->lfEscapement = 0;\r
1292   lf->lfOrientation = 0;\r
1293   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1294   lf->lfItalic = mfp->italic;\r
1295   lf->lfUnderline = mfp->underline;\r
1296   lf->lfStrikeOut = mfp->strikeout;\r
1297   lf->lfCharSet = mfp->charset;\r
1298   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1299 \r
1300 \r
1301 \r
1302   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1303   lf->lfQuality = DEFAULT_QUALITY;\r
1304   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1305     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1306 }\r
1307 \r
1308 void\r
1309 CreateFontInMF(MyFont *mf)\r
1310\r
1311   LFfromMFP(&mf->lf, &mf->mfp);\r
1312   if (mf->hf) DeleteObject(mf->hf);\r
1313   mf->hf = CreateFontIndirect(&mf->lf);\r
1314 }\r
1315 \r
1316 // [HGM] This platform-dependent table provides the location for storing the color info\r
1317 void *\r
1318 colorVariable[] = {\r
1319   &whitePieceColor, \r
1320   &blackPieceColor, \r
1321   &lightSquareColor,\r
1322   &darkSquareColor, \r
1323   &highlightSquareColor,\r
1324   &premoveHighlightColor,\r
1325   NULL,\r
1326   &consoleBackgroundColor,\r
1327   &appData.fontForeColorWhite,\r
1328   &appData.fontBackColorWhite,\r
1329   &appData.fontForeColorBlack,\r
1330   &appData.fontBackColorBlack,\r
1331   &appData.evalHistColorWhite,\r
1332   &appData.evalHistColorBlack,\r
1333   &appData.highlightArrowColor,\r
1334 };\r
1335 \r
1336 /* Command line font name parser.  NULL name means do nothing.\r
1337    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1338    For backward compatibility, syntax without the colon is also\r
1339    accepted, but font names with digits in them won't work in that case.\r
1340 */\r
1341 VOID\r
1342 ParseFontName(char *name, MyFontParams *mfp)\r
1343 {\r
1344   char *p, *q;\r
1345   if (name == NULL) return;\r
1346   p = name;\r
1347   q = strchr(p, ':');\r
1348   if (q) {\r
1349     if (q - p >= sizeof(mfp->faceName))\r
1350       ExitArgError(_("Font name too long:"), name, TRUE);\r
1351     memcpy(mfp->faceName, p, q - p);\r
1352     mfp->faceName[q - p] = NULLCHAR;\r
1353     p = q + 1;\r
1354   } else {\r
1355     q = mfp->faceName;\r
1356 \r
1357     while (*p && !isdigit(*p)) {\r
1358       *q++ = *p++;\r
1359       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1360         ExitArgError(_("Font name too long:"), name, TRUE);\r
1361     }\r
1362     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1363     *q = NULLCHAR;\r
1364   }\r
1365   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1366   mfp->pointSize = (float) atof(p);\r
1367   mfp->bold = (strchr(p, 'b') != NULL);\r
1368   mfp->italic = (strchr(p, 'i') != NULL);\r
1369   mfp->underline = (strchr(p, 'u') != NULL);\r
1370   mfp->strikeout = (strchr(p, 's') != NULL);\r
1371   mfp->charset = DEFAULT_CHARSET;\r
1372   q = strchr(p, 'c');\r
1373   if (q)\r
1374     mfp->charset = (BYTE) atoi(q+1);\r
1375 }\r
1376 \r
1377 void\r
1378 ParseFont(char *name, int number)\r
1379 { // wrapper to shield back-end from 'font'\r
1380   ParseFontName(name, &font[boardSize][number]->mfp);\r
1381 }\r
1382 \r
1383 void\r
1384 SetFontDefaults()\r
1385 { // in WB  we have a 2D array of fonts; this initializes their description\r
1386   int i, j;\r
1387   /* Point font array elements to structures and\r
1388      parse default font names */\r
1389   for (i=0; i<NUM_FONTS; i++) {\r
1390     for (j=0; j<NUM_SIZES; j++) {\r
1391       font[j][i] = &fontRec[j][i];\r
1392       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1393     }\r
1394   }\r
1395 }\r
1396 \r
1397 void\r
1398 CreateFonts()\r
1399 { // here we create the actual fonts from the selected descriptions\r
1400   int i, j;\r
1401   for (i=0; i<NUM_FONTS; i++) {\r
1402     for (j=0; j<NUM_SIZES; j++) {\r
1403       CreateFontInMF(font[j][i]);\r
1404     }\r
1405   }\r
1406 }\r
1407 /* Color name parser.\r
1408    X version accepts X color names, but this one\r
1409    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1410 COLORREF\r
1411 ParseColorName(char *name)\r
1412 {\r
1413   int red, green, blue, count;\r
1414   char buf[MSG_SIZ];\r
1415 \r
1416   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1417   if (count != 3) {\r
1418     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1419       &red, &green, &blue);\r
1420   }\r
1421   if (count != 3) {\r
1422     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1423     DisplayError(buf, 0);\r
1424     return RGB(0, 0, 0);\r
1425   }\r
1426   return PALETTERGB(red, green, blue);\r
1427 }\r
1428 \r
1429 void\r
1430 ParseColor(int n, char *name)\r
1431 { // for WinBoard the color is an int, which needs to be derived from the string\r
1432   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1433 }\r
1434 \r
1435 void\r
1436 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1437 {\r
1438   char *e = argValue;\r
1439   int eff = 0;\r
1440 \r
1441   while (*e) {\r
1442     if (*e == 'b')      eff |= CFE_BOLD;\r
1443     else if (*e == 'i') eff |= CFE_ITALIC;\r
1444     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1445     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1446     else if (*e == '#' || isdigit(*e)) break;\r
1447     e++;\r
1448   }\r
1449   *effects = eff;\r
1450   *color   = ParseColorName(e);\r
1451 }\r
1452 \r
1453 void\r
1454 ParseTextAttribs(ColorClass cc, char *s)\r
1455 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1456     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1457     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1458 }\r
1459 \r
1460 void\r
1461 ParseBoardSize(void *addr, char *name)\r
1462 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1463   BoardSize bs = SizeTiny;\r
1464   while (sizeInfo[bs].name != NULL) {\r
1465     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1466         *(BoardSize *)addr = bs;\r
1467         return;\r
1468     }\r
1469     bs++;\r
1470   }\r
1471   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1472 }\r
1473 \r
1474 void\r
1475 LoadAllSounds()\r
1476 { // [HGM] import name from appData first\r
1477   ColorClass cc;\r
1478   SoundClass sc;\r
1479   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1480     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1481     textAttribs[cc].sound.data = NULL;\r
1482     MyLoadSound(&textAttribs[cc].sound);\r
1483   }\r
1484   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1485     textAttribs[cc].sound.name = strdup("");\r
1486     textAttribs[cc].sound.data = NULL;\r
1487   }\r
1488   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1489     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1490     sounds[sc].data = NULL;\r
1491     MyLoadSound(&sounds[sc]);\r
1492   }\r
1493 }\r
1494 \r
1495 void\r
1496 SetCommPortDefaults()\r
1497 {\r
1498    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1499   dcb.DCBlength = sizeof(DCB);\r
1500   dcb.BaudRate = 9600;\r
1501   dcb.fBinary = TRUE;\r
1502   dcb.fParity = FALSE;\r
1503   dcb.fOutxCtsFlow = FALSE;\r
1504   dcb.fOutxDsrFlow = FALSE;\r
1505   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1506   dcb.fDsrSensitivity = FALSE;\r
1507   dcb.fTXContinueOnXoff = TRUE;\r
1508   dcb.fOutX = FALSE;\r
1509   dcb.fInX = FALSE;\r
1510   dcb.fNull = FALSE;\r
1511   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1512   dcb.fAbortOnError = FALSE;\r
1513   dcb.ByteSize = 7;\r
1514   dcb.Parity = SPACEPARITY;\r
1515   dcb.StopBits = ONESTOPBIT;\r
1516 }\r
1517 \r
1518 // [HGM] args: these three cases taken out to stay in front-end\r
1519 void\r
1520 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1521 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1522         // while the curent board size determines the element. This system should be ported to XBoard.\r
1523         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1524         int bs;\r
1525         for (bs=0; bs<NUM_SIZES; bs++) {\r
1526           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1527           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1528           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1529             ad->argName, mfp->faceName, mfp->pointSize,\r
1530             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1531             mfp->bold ? "b" : "",\r
1532             mfp->italic ? "i" : "",\r
1533             mfp->underline ? "u" : "",\r
1534             mfp->strikeout ? "s" : "",\r
1535             (int)mfp->charset);\r
1536         }\r
1537       }\r
1538 \r
1539 void\r
1540 ExportSounds()\r
1541 { // [HGM] copy the names from the internal WB variables to appData\r
1542   ColorClass cc;\r
1543   SoundClass sc;\r
1544   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1545     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1546   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1547     (&appData.soundMove)[sc] = sounds[sc].name;\r
1548 }\r
1549 \r
1550 void\r
1551 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1552 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1553         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1554         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1555           (ta->effects & CFE_BOLD) ? "b" : "",\r
1556           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1557           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1558           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1559           (ta->effects) ? " " : "",\r
1560           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1561       }\r
1562 \r
1563 void\r
1564 SaveColor(FILE *f, ArgDescriptor *ad)\r
1565 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1566         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1567         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1568           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1569 }\r
1570 \r
1571 void\r
1572 SaveBoardSize(FILE *f, char *name, void *addr)\r
1573 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1574   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1575 }\r
1576 \r
1577 void\r
1578 ParseCommPortSettings(char *s)\r
1579 { // wrapper to keep dcb from back-end\r
1580   ParseCommSettings(s, &dcb);\r
1581 }\r
1582 \r
1583 void\r
1584 GetWindowCoords()\r
1585 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1586   GetActualPlacement(hwndMain, &wpMain);\r
1587   GetActualPlacement(hwndConsole, &wpConsole);\r
1588   GetActualPlacement(commentDialog, &wpComment);\r
1589   GetActualPlacement(editTagsDialog, &wpTags);\r
1590   GetActualPlacement(gameListDialog, &wpGameList);\r
1591   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1592   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1593   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1594 }\r
1595 \r
1596 void\r
1597 PrintCommPortSettings(FILE *f, char *name)\r
1598 { // wrapper to shield back-end from DCB\r
1599       PrintCommSettings(f, name, &dcb);\r
1600 }\r
1601 \r
1602 int\r
1603 MySearchPath(char *installDir, char *name, char *fullname)\r
1604 {\r
1605   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1606   if(name[0]== '%') {\r
1607     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1608     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1609       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1610       *strchr(buf, '%') = 0;\r
1611       strcat(fullname, getenv(buf));\r
1612       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1613     }\r
1614     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1615     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1616     return (int) strlen(fullname);\r
1617   }\r
1618   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1619 }\r
1620 \r
1621 int\r
1622 MyGetFullPathName(char *name, char *fullname)\r
1623 {\r
1624   char *dummy;\r
1625   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1626 }\r
1627 \r
1628 int\r
1629 MainWindowUp()\r
1630 { // [HGM] args: allows testing if main window is realized from back-end\r
1631   return hwndMain != NULL;\r
1632 }\r
1633 \r
1634 void\r
1635 PopUpStartupDialog()\r
1636 {\r
1637     FARPROC lpProc;\r
1638     \r
1639     LoadLanguageFile(appData.language);\r
1640     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1641     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1642     FreeProcInstance(lpProc);\r
1643 }\r
1644 \r
1645 /*---------------------------------------------------------------------------*\\r
1646  *\r
1647  * GDI board drawing routines\r
1648  *\r
1649 \*---------------------------------------------------------------------------*/\r
1650 \r
1651 /* [AS] Draw square using background texture */\r
1652 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1653 {\r
1654     XFORM   x;\r
1655 \r
1656     if( mode == 0 ) {\r
1657         return; /* Should never happen! */\r
1658     }\r
1659 \r
1660     SetGraphicsMode( dst, GM_ADVANCED );\r
1661 \r
1662     switch( mode ) {\r
1663     case 1:\r
1664         /* Identity */\r
1665         break;\r
1666     case 2:\r
1667         /* X reflection */\r
1668         x.eM11 = -1.0;\r
1669         x.eM12 = 0;\r
1670         x.eM21 = 0;\r
1671         x.eM22 = 1.0;\r
1672         x.eDx = (FLOAT) dw + dx - 1;\r
1673         x.eDy = 0;\r
1674         dx = 0;\r
1675         SetWorldTransform( dst, &x );\r
1676         break;\r
1677     case 3:\r
1678         /* Y reflection */\r
1679         x.eM11 = 1.0;\r
1680         x.eM12 = 0;\r
1681         x.eM21 = 0;\r
1682         x.eM22 = -1.0;\r
1683         x.eDx = 0;\r
1684         x.eDy = (FLOAT) dh + dy - 1;\r
1685         dy = 0;\r
1686         SetWorldTransform( dst, &x );\r
1687         break;\r
1688     case 4:\r
1689         /* X/Y flip */\r
1690         x.eM11 = 0;\r
1691         x.eM12 = 1.0;\r
1692         x.eM21 = 1.0;\r
1693         x.eM22 = 0;\r
1694         x.eDx = (FLOAT) dx;\r
1695         x.eDy = (FLOAT) dy;\r
1696         dx = 0;\r
1697         dy = 0;\r
1698         SetWorldTransform( dst, &x );\r
1699         break;\r
1700     }\r
1701 \r
1702     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1703 \r
1704     x.eM11 = 1.0;\r
1705     x.eM12 = 0;\r
1706     x.eM21 = 0;\r
1707     x.eM22 = 1.0;\r
1708     x.eDx = 0;\r
1709     x.eDy = 0;\r
1710     SetWorldTransform( dst, &x );\r
1711 \r
1712     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1713 }\r
1714 \r
1715 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1716 enum {\r
1717     PM_WP = (int) WhitePawn, \r
1718     PM_WN = (int) WhiteKnight, \r
1719     PM_WB = (int) WhiteBishop, \r
1720     PM_WR = (int) WhiteRook, \r
1721     PM_WQ = (int) WhiteQueen, \r
1722     PM_WF = (int) WhiteFerz, \r
1723     PM_WW = (int) WhiteWazir, \r
1724     PM_WE = (int) WhiteAlfil, \r
1725     PM_WM = (int) WhiteMan, \r
1726     PM_WO = (int) WhiteCannon, \r
1727     PM_WU = (int) WhiteUnicorn, \r
1728     PM_WH = (int) WhiteNightrider, \r
1729     PM_WA = (int) WhiteAngel, \r
1730     PM_WC = (int) WhiteMarshall, \r
1731     PM_WAB = (int) WhiteCardinal, \r
1732     PM_WD = (int) WhiteDragon, \r
1733     PM_WL = (int) WhiteLance, \r
1734     PM_WS = (int) WhiteCobra, \r
1735     PM_WV = (int) WhiteFalcon, \r
1736     PM_WSG = (int) WhiteSilver, \r
1737     PM_WG = (int) WhiteGrasshopper, \r
1738     PM_WK = (int) WhiteKing,\r
1739     PM_BP = (int) BlackPawn, \r
1740     PM_BN = (int) BlackKnight, \r
1741     PM_BB = (int) BlackBishop, \r
1742     PM_BR = (int) BlackRook, \r
1743     PM_BQ = (int) BlackQueen, \r
1744     PM_BF = (int) BlackFerz, \r
1745     PM_BW = (int) BlackWazir, \r
1746     PM_BE = (int) BlackAlfil, \r
1747     PM_BM = (int) BlackMan,\r
1748     PM_BO = (int) BlackCannon, \r
1749     PM_BU = (int) BlackUnicorn, \r
1750     PM_BH = (int) BlackNightrider, \r
1751     PM_BA = (int) BlackAngel, \r
1752     PM_BC = (int) BlackMarshall, \r
1753     PM_BG = (int) BlackGrasshopper, \r
1754     PM_BAB = (int) BlackCardinal,\r
1755     PM_BD = (int) BlackDragon,\r
1756     PM_BL = (int) BlackLance,\r
1757     PM_BS = (int) BlackCobra,\r
1758     PM_BV = (int) BlackFalcon,\r
1759     PM_BSG = (int) BlackSilver,\r
1760     PM_BK = (int) BlackKing\r
1761 };\r
1762 \r
1763 static HFONT hPieceFont = NULL;\r
1764 static HBITMAP hPieceMask[(int) EmptySquare];\r
1765 static HBITMAP hPieceFace[(int) EmptySquare];\r
1766 static int fontBitmapSquareSize = 0;\r
1767 static char pieceToFontChar[(int) EmptySquare] =\r
1768                               { 'p', 'n', 'b', 'r', 'q', \r
1769                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1770                       'k', 'o', 'm', 'v', 't', 'w', \r
1771                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1772                                                               'l' };\r
1773 \r
1774 extern BOOL SetCharTable( char *table, const char * map );\r
1775 /* [HGM] moved to backend.c */\r
1776 \r
1777 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1778 {\r
1779     HBRUSH hbrush;\r
1780     BYTE r1 = GetRValue( color );\r
1781     BYTE g1 = GetGValue( color );\r
1782     BYTE b1 = GetBValue( color );\r
1783     BYTE r2 = r1 / 2;\r
1784     BYTE g2 = g1 / 2;\r
1785     BYTE b2 = b1 / 2;\r
1786     RECT rc;\r
1787 \r
1788     /* Create a uniform background first */\r
1789     hbrush = CreateSolidBrush( color );\r
1790     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1791     FillRect( hdc, &rc, hbrush );\r
1792     DeleteObject( hbrush );\r
1793     \r
1794     if( mode == 1 ) {\r
1795         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1796         int steps = squareSize / 2;\r
1797         int i;\r
1798 \r
1799         for( i=0; i<steps; i++ ) {\r
1800             BYTE r = r1 - (r1-r2) * i / steps;\r
1801             BYTE g = g1 - (g1-g2) * i / steps;\r
1802             BYTE b = b1 - (b1-b2) * i / steps;\r
1803 \r
1804             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1805             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1806             FillRect( hdc, &rc, hbrush );\r
1807             DeleteObject(hbrush);\r
1808         }\r
1809     }\r
1810     else if( mode == 2 ) {\r
1811         /* Diagonal gradient, good more or less for every piece */\r
1812         POINT triangle[3];\r
1813         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1814         HBRUSH hbrush_old;\r
1815         int steps = squareSize;\r
1816         int i;\r
1817 \r
1818         triangle[0].x = squareSize - steps;\r
1819         triangle[0].y = squareSize;\r
1820         triangle[1].x = squareSize;\r
1821         triangle[1].y = squareSize;\r
1822         triangle[2].x = squareSize;\r
1823         triangle[2].y = squareSize - steps;\r
1824 \r
1825         for( i=0; i<steps; i++ ) {\r
1826             BYTE r = r1 - (r1-r2) * i / steps;\r
1827             BYTE g = g1 - (g1-g2) * i / steps;\r
1828             BYTE b = b1 - (b1-b2) * i / steps;\r
1829 \r
1830             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1831             hbrush_old = SelectObject( hdc, hbrush );\r
1832             Polygon( hdc, triangle, 3 );\r
1833             SelectObject( hdc, hbrush_old );\r
1834             DeleteObject(hbrush);\r
1835             triangle[0].x++;\r
1836             triangle[2].y++;\r
1837         }\r
1838 \r
1839         SelectObject( hdc, hpen );\r
1840     }\r
1841 }\r
1842 \r
1843 /*\r
1844     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1845     seems to work ok. The main problem here is to find the "inside" of a chess\r
1846     piece: follow the steps as explained below.\r
1847 */\r
1848 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1849 {\r
1850     HBITMAP hbm;\r
1851     HBITMAP hbm_old;\r
1852     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1853     RECT rc;\r
1854     SIZE sz;\r
1855 \r
1856 \r
1857     POINT pt;\r
1858     int backColor = whitePieceColor; \r
1859     int foreColor = blackPieceColor;\r
1860     \r
1861     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1862         backColor = appData.fontBackColorWhite;\r
1863         foreColor = appData.fontForeColorWhite;\r
1864     }\r
1865     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1866         backColor = appData.fontBackColorBlack;\r
1867         foreColor = appData.fontForeColorBlack;\r
1868     }\r
1869 \r
1870     /* Mask */\r
1871     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1872 \r
1873     hbm_old = SelectObject( hdc, hbm );\r
1874 \r
1875     rc.left = 0;\r
1876     rc.top = 0;\r
1877     rc.right = squareSize;\r
1878     rc.bottom = squareSize;\r
1879 \r
1880     /* Step 1: background is now black */\r
1881     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1882 \r
1883     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1884 \r
1885     pt.x = (squareSize - sz.cx) / 2;\r
1886     pt.y = (squareSize - sz.cy) / 2;\r
1887 \r
1888     SetBkMode( hdc, TRANSPARENT );\r
1889     SetTextColor( hdc, chroma );\r
1890     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1891     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1892 \r
1893     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1894     /* Step 3: the area outside the piece is filled with white */\r
1895 //    FloodFill( hdc, 0, 0, chroma );\r
1896     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1897     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1898     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1899     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1900     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1901     /* \r
1902         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1903         but if the start point is not inside the piece we're lost!\r
1904         There should be a better way to do this... if we could create a region or path\r
1905         from the fill operation we would be fine for example.\r
1906     */\r
1907 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1908     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1909 \r
1910     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1911         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1912         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1913 \r
1914         SelectObject( dc2, bm2 );\r
1915         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1916         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1917         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1918         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1919         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1920 \r
1921         DeleteDC( dc2 );\r
1922         DeleteObject( bm2 );\r
1923     }\r
1924 \r
1925     SetTextColor( hdc, 0 );\r
1926     /* \r
1927         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1928         draw the piece again in black for safety.\r
1929     */\r
1930     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1931 \r
1932     SelectObject( hdc, hbm_old );\r
1933 \r
1934     if( hPieceMask[index] != NULL ) {\r
1935         DeleteObject( hPieceMask[index] );\r
1936     }\r
1937 \r
1938     hPieceMask[index] = hbm;\r
1939 \r
1940     /* Face */\r
1941     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1942 \r
1943     SelectObject( hdc, hbm );\r
1944 \r
1945     {\r
1946         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1947         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1948         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1949 \r
1950         SelectObject( dc1, hPieceMask[index] );\r
1951         SelectObject( dc2, bm2 );\r
1952         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1953         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1954         \r
1955         /* \r
1956             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1957             the piece background and deletes (makes transparent) the rest.\r
1958             Thanks to that mask, we are free to paint the background with the greates\r
1959             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1960             We use this, to make gradients and give the pieces a "roundish" look.\r
1961         */\r
1962         SetPieceBackground( hdc, backColor, 2 );\r
1963         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1964 \r
1965         DeleteDC( dc2 );\r
1966         DeleteDC( dc1 );\r
1967         DeleteObject( bm2 );\r
1968     }\r
1969 \r
1970     SetTextColor( hdc, foreColor );\r
1971     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1972 \r
1973     SelectObject( hdc, hbm_old );\r
1974 \r
1975     if( hPieceFace[index] != NULL ) {\r
1976         DeleteObject( hPieceFace[index] );\r
1977     }\r
1978 \r
1979     hPieceFace[index] = hbm;\r
1980 }\r
1981 \r
1982 static int TranslatePieceToFontPiece( int piece )\r
1983 {\r
1984     switch( piece ) {\r
1985     case BlackPawn:\r
1986         return PM_BP;\r
1987     case BlackKnight:\r
1988         return PM_BN;\r
1989     case BlackBishop:\r
1990         return PM_BB;\r
1991     case BlackRook:\r
1992         return PM_BR;\r
1993     case BlackQueen:\r
1994         return PM_BQ;\r
1995     case BlackKing:\r
1996         return PM_BK;\r
1997     case WhitePawn:\r
1998         return PM_WP;\r
1999     case WhiteKnight:\r
2000         return PM_WN;\r
2001     case WhiteBishop:\r
2002         return PM_WB;\r
2003     case WhiteRook:\r
2004         return PM_WR;\r
2005     case WhiteQueen:\r
2006         return PM_WQ;\r
2007     case WhiteKing:\r
2008         return PM_WK;\r
2009 \r
2010     case BlackAngel:\r
2011         return PM_BA;\r
2012     case BlackMarshall:\r
2013         return PM_BC;\r
2014     case BlackFerz:\r
2015         return PM_BF;\r
2016     case BlackNightrider:\r
2017         return PM_BH;\r
2018     case BlackAlfil:\r
2019         return PM_BE;\r
2020     case BlackWazir:\r
2021         return PM_BW;\r
2022     case BlackUnicorn:\r
2023         return PM_BU;\r
2024     case BlackCannon:\r
2025         return PM_BO;\r
2026     case BlackGrasshopper:\r
2027         return PM_BG;\r
2028     case BlackMan:\r
2029         return PM_BM;\r
2030     case BlackSilver:\r
2031         return PM_BSG;\r
2032     case BlackLance:\r
2033         return PM_BL;\r
2034     case BlackFalcon:\r
2035         return PM_BV;\r
2036     case BlackCobra:\r
2037         return PM_BS;\r
2038     case BlackCardinal:\r
2039         return PM_BAB;\r
2040     case BlackDragon:\r
2041         return PM_BD;\r
2042 \r
2043     case WhiteAngel:\r
2044         return PM_WA;\r
2045     case WhiteMarshall:\r
2046         return PM_WC;\r
2047     case WhiteFerz:\r
2048         return PM_WF;\r
2049     case WhiteNightrider:\r
2050         return PM_WH;\r
2051     case WhiteAlfil:\r
2052         return PM_WE;\r
2053     case WhiteWazir:\r
2054         return PM_WW;\r
2055     case WhiteUnicorn:\r
2056         return PM_WU;\r
2057     case WhiteCannon:\r
2058         return PM_WO;\r
2059     case WhiteGrasshopper:\r
2060         return PM_WG;\r
2061     case WhiteMan:\r
2062         return PM_WM;\r
2063     case WhiteSilver:\r
2064         return PM_WSG;\r
2065     case WhiteLance:\r
2066         return PM_WL;\r
2067     case WhiteFalcon:\r
2068         return PM_WV;\r
2069     case WhiteCobra:\r
2070         return PM_WS;\r
2071     case WhiteCardinal:\r
2072         return PM_WAB;\r
2073     case WhiteDragon:\r
2074         return PM_WD;\r
2075     }\r
2076 \r
2077     return 0;\r
2078 }\r
2079 \r
2080 void CreatePiecesFromFont()\r
2081 {\r
2082     LOGFONT lf;\r
2083     HDC hdc_window = NULL;\r
2084     HDC hdc = NULL;\r
2085     HFONT hfont_old;\r
2086     int fontHeight;\r
2087     int i;\r
2088 \r
2089     if( fontBitmapSquareSize < 0 ) {\r
2090         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2091         return;\r
2092     }\r
2093 \r
2094     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2095             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2096         fontBitmapSquareSize = -1;\r
2097         return;\r
2098     }\r
2099 \r
2100     if( fontBitmapSquareSize != squareSize ) {\r
2101         hdc_window = GetDC( hwndMain );\r
2102         hdc = CreateCompatibleDC( hdc_window );\r
2103 \r
2104         if( hPieceFont != NULL ) {\r
2105             DeleteObject( hPieceFont );\r
2106         }\r
2107         else {\r
2108             for( i=0; i<=(int)BlackKing; i++ ) {\r
2109                 hPieceMask[i] = NULL;\r
2110                 hPieceFace[i] = NULL;\r
2111             }\r
2112         }\r
2113 \r
2114         fontHeight = 75;\r
2115 \r
2116         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2117             fontHeight = appData.fontPieceSize;\r
2118         }\r
2119 \r
2120         fontHeight = (fontHeight * squareSize) / 100;\r
2121 \r
2122         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2123         lf.lfWidth = 0;\r
2124         lf.lfEscapement = 0;\r
2125         lf.lfOrientation = 0;\r
2126         lf.lfWeight = FW_NORMAL;\r
2127         lf.lfItalic = 0;\r
2128         lf.lfUnderline = 0;\r
2129         lf.lfStrikeOut = 0;\r
2130         lf.lfCharSet = DEFAULT_CHARSET;\r
2131         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2132         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2133         lf.lfQuality = PROOF_QUALITY;\r
2134         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2135         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2136         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2137 \r
2138         hPieceFont = CreateFontIndirect( &lf );\r
2139 \r
2140         if( hPieceFont == NULL ) {\r
2141             fontBitmapSquareSize = -2;\r
2142         }\r
2143         else {\r
2144             /* Setup font-to-piece character table */\r
2145             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2146                 /* No (or wrong) global settings, try to detect the font */\r
2147                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2148                     /* Alpha */\r
2149                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2150                 }\r
2151                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2152                     /* DiagramTT* family */\r
2153                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2154                 }\r
2155                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2156                     /* Fairy symbols */\r
2157                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2158                 }\r
2159                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2160                     /* Good Companion (Some characters get warped as literal :-( */\r
2161                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2162                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2163                     SetCharTable(pieceToFontChar, s);\r
2164                 }\r
2165                 else {\r
2166                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2167                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2168                 }\r
2169             }\r
2170 \r
2171             /* Create bitmaps */\r
2172             hfont_old = SelectObject( hdc, hPieceFont );\r
2173             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2174                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2175                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2176 \r
2177             SelectObject( hdc, hfont_old );\r
2178 \r
2179             fontBitmapSquareSize = squareSize;\r
2180         }\r
2181     }\r
2182 \r
2183     if( hdc != NULL ) {\r
2184         DeleteDC( hdc );\r
2185     }\r
2186 \r
2187     if( hdc_window != NULL ) {\r
2188         ReleaseDC( hwndMain, hdc_window );\r
2189     }\r
2190 }\r
2191 \r
2192 HBITMAP\r
2193 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2194 {\r
2195   char name[128], buf[MSG_SIZ];\r
2196 \r
2197     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2198   if(appData.pieceDirectory[0]) {\r
2199     HBITMAP res;\r
2200     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2201     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2202     if(res) return res;\r
2203   }\r
2204   if (gameInfo.event &&\r
2205       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2206       strcmp(name, "k80s") == 0) {\r
2207     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2208   }\r
2209   return LoadBitmap(hinst, name);\r
2210 }\r
2211 \r
2212 \r
2213 /* Insert a color into the program's logical palette\r
2214    structure.  This code assumes the given color is\r
2215    the result of the RGB or PALETTERGB macro, and it\r
2216    knows how those macros work (which is documented).\r
2217 */\r
2218 VOID\r
2219 InsertInPalette(COLORREF color)\r
2220 {\r
2221   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2222 \r
2223   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2224     DisplayFatalError(_("Too many colors"), 0, 1);\r
2225     pLogPal->palNumEntries--;\r
2226     return;\r
2227   }\r
2228 \r
2229   pe->peFlags = (char) 0;\r
2230   pe->peRed = (char) (0xFF & color);\r
2231   pe->peGreen = (char) (0xFF & (color >> 8));\r
2232   pe->peBlue = (char) (0xFF & (color >> 16));\r
2233   return;\r
2234 }\r
2235 \r
2236 \r
2237 VOID\r
2238 InitDrawingColors()\r
2239 {\r
2240   int i;\r
2241   if (pLogPal == NULL) {\r
2242     /* Allocate enough memory for a logical palette with\r
2243      * PALETTESIZE entries and set the size and version fields\r
2244      * of the logical palette structure.\r
2245      */\r
2246     pLogPal = (NPLOGPALETTE)\r
2247       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2248                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2249     pLogPal->palVersion    = 0x300;\r
2250   }\r
2251   pLogPal->palNumEntries = 0;\r
2252 \r
2253   InsertInPalette(lightSquareColor);\r
2254   InsertInPalette(darkSquareColor);\r
2255   InsertInPalette(whitePieceColor);\r
2256   InsertInPalette(blackPieceColor);\r
2257   InsertInPalette(highlightSquareColor);\r
2258   InsertInPalette(premoveHighlightColor);\r
2259 \r
2260   /*  create a logical color palette according the information\r
2261    *  in the LOGPALETTE structure.\r
2262    */\r
2263   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2264 \r
2265   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2266   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2267   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2268   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2269   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2270   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2271   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2272     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2273 \r
2274    /* [AS] Force rendering of the font-based pieces */\r
2275   if( fontBitmapSquareSize > 0 ) {\r
2276     fontBitmapSquareSize = 0;\r
2277   }\r
2278 }\r
2279 \r
2280 \r
2281 int\r
2282 BoardWidth(int boardSize, int n)\r
2283 { /* [HGM] argument n added to allow different width and height */\r
2284   int lineGap = sizeInfo[boardSize].lineGap;\r
2285 \r
2286   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2287       lineGap = appData.overrideLineGap;\r
2288   }\r
2289 \r
2290   return (n + 1) * lineGap +\r
2291           n * sizeInfo[boardSize].squareSize;\r
2292 }\r
2293 \r
2294 /* Respond to board resize by dragging edge */\r
2295 VOID\r
2296 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2297 {\r
2298   BoardSize newSize = NUM_SIZES - 1;\r
2299   static int recurse = 0;\r
2300   if (IsIconic(hwndMain)) return;\r
2301   if (recurse > 0) return;\r
2302   recurse++;\r
2303   while (newSize > 0) {\r
2304         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2305         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2306            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2307     newSize--;\r
2308   } \r
2309   boardSize = newSize;\r
2310   InitDrawingSizes(boardSize, flags);\r
2311   recurse--;\r
2312 }\r
2313 \r
2314 \r
2315 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2316 \r
2317 VOID\r
2318 InitDrawingSizes(BoardSize boardSize, int flags)\r
2319 {\r
2320   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2321   ChessSquare piece;\r
2322   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2323   HDC hdc;\r
2324   SIZE clockSize, messageSize;\r
2325   HFONT oldFont;\r
2326   char buf[MSG_SIZ];\r
2327   char *str;\r
2328   HMENU hmenu = GetMenu(hwndMain);\r
2329   RECT crect, wrect, oldRect;\r
2330   int offby;\r
2331   LOGBRUSH logbrush;\r
2332   VariantClass v = gameInfo.variant;\r
2333 \r
2334   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2335   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2336 \r
2337   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2338   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2339   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2340   oldBoardSize = boardSize;\r
2341 \r
2342   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2343   { // correct board size to one where built-in pieces exist\r
2344     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2345        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2346 \r
2347       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2348       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2349       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2350       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2351       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2352                                    boardSize = SizeMiddling;\r
2353     }\r
2354   }\r
2355   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2356 \r
2357   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2358   oldRect.top = wpMain.y;\r
2359   oldRect.right = wpMain.x + wpMain.width;\r
2360   oldRect.bottom = wpMain.y + wpMain.height;\r
2361 \r
2362   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2363   smallLayout = sizeInfo[boardSize].smallLayout;\r
2364   squareSize = sizeInfo[boardSize].squareSize;\r
2365   lineGap = sizeInfo[boardSize].lineGap;\r
2366   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2367   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2368 \r
2369   // [HGM] decide on tininess based on total board width rather than square size\r
2370   tinyLayout = squareSize * (BOARD_WIDTH);\r
2371   tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;\r
2372 \r
2373   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2374       lineGap = appData.overrideLineGap;\r
2375   }\r
2376 \r
2377   if (tinyLayout != oldTinyLayout) {\r
2378     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2379     if (tinyLayout == 2) {\r
2380       style &= ~WS_SYSMENU;\r
2381       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2382                  "&Minimize\tCtrl+F4");\r
2383     } else {\r
2384       style |= WS_SYSMENU;\r
2385       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2386     }\r
2387     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2388 \r
2389     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2390       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2391         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2392     }\r
2393     DrawMenuBar(hwndMain);\r
2394   }\r
2395 \r
2396   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2397   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2398 \r
2399   /* Get text area sizes */\r
2400   hdc = GetDC(hwndMain);\r
2401   if (appData.clockMode) {\r
2402     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2403   } else {\r
2404     snprintf(buf, MSG_SIZ, _("White"));\r
2405   }\r
2406   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2407   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2408   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2409   str = _("We only care about the height here");\r
2410   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2411   SelectObject(hdc, oldFont);\r
2412   ReleaseDC(hwndMain, hdc);\r
2413 \r
2414   /* Compute where everything goes */\r
2415   if((first.programLogo || second.programLogo) && tinyLayout != 2) {\r
2416         /* [HGM] logo: if either logo is on, reserve space for it */\r
2417         logoHeight =  2*clockSize.cy;\r
2418         leftLogoRect.left   = OUTER_MARGIN;\r
2419         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2420         leftLogoRect.top    = OUTER_MARGIN;\r
2421         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2422 \r
2423         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2424         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2425         rightLogoRect.top    = OUTER_MARGIN;\r
2426         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2427 \r
2428 \r
2429     whiteRect.left = leftLogoRect.right;\r
2430     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2431     whiteRect.top = OUTER_MARGIN;\r
2432     whiteRect.bottom = whiteRect.top + logoHeight;\r
2433 \r
2434     blackRect.right = rightLogoRect.left;\r
2435     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2436     blackRect.top = whiteRect.top;\r
2437     blackRect.bottom = whiteRect.bottom;\r
2438   } else {\r
2439     whiteRect.left = OUTER_MARGIN;\r
2440     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2441     whiteRect.top = OUTER_MARGIN;\r
2442     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2443 \r
2444     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2445     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2446     blackRect.top = whiteRect.top;\r
2447     blackRect.bottom = whiteRect.bottom;\r
2448 \r
2449     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2450   }\r
2451 \r
2452   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2453   if (appData.showButtonBar) {\r
2454     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2455       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2456   } else {\r
2457     messageRect.right = OUTER_MARGIN + boardWidth;\r
2458   }\r
2459   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2460   messageRect.bottom = messageRect.top + messageSize.cy;\r
2461 \r
2462   boardRect.left = OUTER_MARGIN;\r
2463   boardRect.right = boardRect.left + boardWidth;\r
2464   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2465   boardRect.bottom = boardRect.top + boardHeight;\r
2466 \r
2467   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2468   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2469   oldTinyLayout = tinyLayout;\r
2470   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2471   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2472     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2473   winW *= 1 + twoBoards;\r
2474   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2475   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2476   wpMain.height = winH; //       without disturbing window attachments\r
2477   GetWindowRect(hwndMain, &wrect);\r
2478   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2479                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2480 \r
2481   // [HGM] placement: let attached windows follow size change.\r
2482   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2483   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2484   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2485   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2486   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2487 \r
2488   /* compensate if menu bar wrapped */\r
2489   GetClientRect(hwndMain, &crect);\r
2490   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2491   wpMain.height += offby;\r
2492   switch (flags) {\r
2493   case WMSZ_TOPLEFT:\r
2494     SetWindowPos(hwndMain, NULL, \r
2495                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2496                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2497     break;\r
2498 \r
2499   case WMSZ_TOPRIGHT:\r
2500   case WMSZ_TOP:\r
2501     SetWindowPos(hwndMain, NULL, \r
2502                  wrect.left, wrect.bottom - wpMain.height, \r
2503                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2504     break;\r
2505 \r
2506   case WMSZ_BOTTOMLEFT:\r
2507   case WMSZ_LEFT:\r
2508     SetWindowPos(hwndMain, NULL, \r
2509                  wrect.right - wpMain.width, wrect.top, \r
2510                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2511     break;\r
2512 \r
2513   case WMSZ_BOTTOMRIGHT:\r
2514   case WMSZ_BOTTOM:\r
2515   case WMSZ_RIGHT:\r
2516   default:\r
2517     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2518                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2519     break;\r
2520   }\r
2521 \r
2522   hwndPause = NULL;\r
2523   for (i = 0; i < N_BUTTONS; i++) {\r
2524     if (buttonDesc[i].hwnd != NULL) {\r
2525       DestroyWindow(buttonDesc[i].hwnd);\r
2526       buttonDesc[i].hwnd = NULL;\r
2527     }\r
2528     if (appData.showButtonBar) {\r
2529       buttonDesc[i].hwnd =\r
2530         CreateWindow("BUTTON", buttonDesc[i].label,\r
2531                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2532                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2533                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2534                      (HMENU) buttonDesc[i].id,\r
2535                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2536       if (tinyLayout == 2) {\r
2537         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2538                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2539                     MAKELPARAM(FALSE, 0));\r
2540       }\r
2541       if (buttonDesc[i].id == IDM_Pause)\r
2542         hwndPause = buttonDesc[i].hwnd;\r
2543       buttonDesc[i].wndproc = (WNDPROC)\r
2544         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2545     }\r
2546   }\r
2547   if (gridPen != NULL) DeleteObject(gridPen);\r
2548   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2549   if (premovePen != NULL) DeleteObject(premovePen);\r
2550   if (lineGap != 0) {\r
2551     logbrush.lbStyle = BS_SOLID;\r
2552     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2553     gridPen =\r
2554       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2555                    lineGap, &logbrush, 0, NULL);\r
2556     logbrush.lbColor = highlightSquareColor;\r
2557     highlightPen =\r
2558       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2559                    lineGap, &logbrush, 0, NULL);\r
2560 \r
2561     logbrush.lbColor = premoveHighlightColor; \r
2562     premovePen =\r
2563       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2564                    lineGap, &logbrush, 0, NULL);\r
2565 \r
2566     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2567     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2568       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2569       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2570         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2571       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2572         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2573       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2574     }\r
2575     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2576       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2577       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2578         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2579         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2580       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2581         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2582       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2583     }\r
2584   }\r
2585 \r
2586   /* [HGM] Licensing requirement */\r
2587 #ifdef GOTHIC\r
2588   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2589 #endif\r
2590 #ifdef FALCON\r
2591   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2592 #endif\r
2593   GothicPopUp( "", VariantNormal);\r
2594 \r
2595 \r
2596 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2597 \r
2598   /* Load piece bitmaps for this board size */\r
2599   for (i=0; i<=2; i++) {\r
2600     for (piece = WhitePawn;\r
2601          (int) piece < (int) BlackPawn;\r
2602          piece = (ChessSquare) ((int) piece + 1)) {\r
2603       if (pieceBitmap[i][piece] != NULL)\r
2604         DeleteObject(pieceBitmap[i][piece]);\r
2605       pieceBitmap[i][piece] = NULL;\r
2606     }\r
2607   }\r
2608 \r
2609   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2610 \r
2611   // Orthodox Chess pieces\r
2612   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2613   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2614   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2615   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2616   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2617   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2618   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2619   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2620   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2621   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2622   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2623   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2624   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2625   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2626   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2627   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2628     // in Shogi, Hijack the unused Queen for Lance\r
2629     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2630     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2631     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2632   } else {\r
2633     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2634     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2635     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2636   }\r
2637 \r
2638   if(squareSize <= 72 && squareSize >= 33) { \r
2639     /* A & C are available in most sizes now */\r
2640     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2641       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2642       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2643       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2644       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2645       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2646       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2647       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2648       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2649       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2650       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2651       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2652       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2653     } else { // Smirf-like\r
2654       if(gameInfo.variant == VariantSChess) {\r
2655         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2656         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2657         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2658       } else {\r
2659         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2660         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2661         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2662       }\r
2663     }\r
2664     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2665       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2666       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2667       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2668     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2669       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2670       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2671       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2672     } else { // WinBoard standard\r
2673       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2674       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2675       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2676     }\r
2677   }\r
2678 \r
2679 \r
2680   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2681     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2682     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2683     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2684     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2685     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2686     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2687     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2688     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2689     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2690     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2691     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2692     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2693     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2694     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2695     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2696     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2697     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2698     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2699     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2700     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2701     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2702     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2703     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2704     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2705     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2706     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2707     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2708     pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2709     pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2710     pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2711     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2712     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2713     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2714     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2715     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2716     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2717 \r
2718     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
2719       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2720       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2721       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2722       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2723       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2724       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2725       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2726       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2727       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2728       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2729       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2730       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2731     } else {\r
2732       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2733       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2734       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2735       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2736       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2737       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2738       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2739       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2740       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2741       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2742       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2743       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2744     }\r
2745 \r
2746   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2747     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2748     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2749     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2750     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2751     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2752     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2753     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2754     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2755     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2756     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2757     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2758     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2759     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2760     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2761   }\r
2762 \r
2763 \r
2764   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2765   /* special Shogi support in this size */\r
2766   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2767       for (piece = WhitePawn;\r
2768            (int) piece < (int) BlackPawn;\r
2769            piece = (ChessSquare) ((int) piece + 1)) {\r
2770         if (pieceBitmap[i][piece] != NULL)\r
2771           DeleteObject(pieceBitmap[i][piece]);\r
2772       }\r
2773     }\r
2774   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2775   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2776   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2777   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2778   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2779   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2780   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2781   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2782   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2783   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2784   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2785   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2786   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2787   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2788   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2789   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2790   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2791   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2792   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2793   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2794   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2795   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2796   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2797   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2798   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2799   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2800   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2801   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2802   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2803   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2804   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2805   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2806   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2807   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2808   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2809   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2810   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2811   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2812   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2813   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2814   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2815   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2816   minorSize = 0;\r
2817   }\r
2818 \r
2819   if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention\r
2820     char buf[MSG_SIZ];\r
2821     if(pieceBitmap[0][i]) continue;\r
2822     snprintf(buf, MSG_SIZ, "piece%d_", i);\r
2823     pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");\r
2824     pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");\r
2825     pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");\r
2826   }\r
2827 }\r
2828 \r
2829 HBITMAP\r
2830 PieceBitmap(ChessSquare p, int kind)\r
2831 {\r
2832   if ((int) p >= (int) BlackPawn)\r
2833     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2834 \r
2835   return pieceBitmap[kind][(int) p];\r
2836 }\r
2837 \r
2838 /***************************************************************/\r
2839 \r
2840 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2841 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2842 /*\r
2843 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2844 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2845 */\r
2846 \r
2847 VOID\r
2848 SquareToPos(int row, int column, int * x, int * y)\r
2849 {\r
2850   if (flipView) {\r
2851     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2852     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2853   } else {\r
2854     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2855     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2856   }\r
2857 }\r
2858 \r
2859 VOID\r
2860 DrawCoordsOnDC(HDC hdc)\r
2861 {\r
2862   static char files[] = "0123456789012345678901221098765432109876543210";\r
2863   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2864   char str[2] = { NULLCHAR, NULLCHAR };\r
2865   int oldMode, oldAlign, x, y, start, i;\r
2866   HFONT oldFont;\r
2867   HBRUSH oldBrush;\r
2868 \r
2869   if (!appData.showCoords)\r
2870     return;\r
2871 \r
2872   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2873 \r
2874   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2875   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2876   oldAlign = GetTextAlign(hdc);\r
2877   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2878 \r
2879   y = boardRect.top + lineGap;\r
2880   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2881 \r
2882   if(border) {\r
2883     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2884     x += border - lineGap - 4; y += squareSize - 6;\r
2885   } else\r
2886   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2887   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2888     str[0] = files[start + i];\r
2889     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2890     y += squareSize + lineGap;\r
2891   }\r
2892 \r
2893   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2894 \r
2895   if(border) {\r
2896     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2897     x += -border + 4; y += border - squareSize + 6;\r
2898   } else\r
2899   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2900   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2901     str[0] = ranks[start + i];\r
2902     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2903     x += squareSize + lineGap;\r
2904   }    \r
2905 \r
2906   SelectObject(hdc, oldBrush);\r
2907   SetBkMode(hdc, oldMode);\r
2908   SetTextAlign(hdc, oldAlign);\r
2909   SelectObject(hdc, oldFont);\r
2910 }\r
2911 \r
2912 VOID\r
2913 DrawGridOnDC(HDC hdc)\r
2914 {\r
2915   HPEN oldPen;\r
2916  \r
2917   if (lineGap != 0) {\r
2918     oldPen = SelectObject(hdc, gridPen);\r
2919     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2920     SelectObject(hdc, oldPen);\r
2921   }\r
2922 }\r
2923 \r
2924 #define HIGHLIGHT_PEN 0\r
2925 #define PREMOVE_PEN   1\r
2926 \r
2927 VOID\r
2928 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2929 {\r
2930   int x1, y1;\r
2931   HPEN oldPen, hPen;\r
2932   if (lineGap == 0) return;\r
2933   if (flipView) {\r
2934     x1 = boardRect.left +\r
2935       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2936     y1 = boardRect.top +\r
2937       lineGap/2 + y * (squareSize + lineGap) + border;\r
2938   } else {\r
2939     x1 = boardRect.left +\r
2940       lineGap/2 + x * (squareSize + lineGap) + border;\r
2941     y1 = boardRect.top +\r
2942       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2943   }\r
2944   hPen = pen ? premovePen : highlightPen;\r
2945   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2946   MoveToEx(hdc, x1, y1, NULL);\r
2947   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2948   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2949   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2950   LineTo(hdc, x1, y1);\r
2951   SelectObject(hdc, oldPen);\r
2952 }\r
2953 \r
2954 VOID\r
2955 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2956 {\r
2957   int i;\r
2958   for (i=0; i<2; i++) {\r
2959     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2960       DrawHighlightOnDC(hdc, TRUE,\r
2961                         h->sq[i].x, h->sq[i].y,\r
2962                         pen);\r
2963   }\r
2964 }\r
2965 \r
2966 /* Note: sqcolor is used only in monoMode */\r
2967 /* Note that this code is largely duplicated in woptions.c,\r
2968    function DrawSampleSquare, so that needs to be updated too */\r
2969 VOID\r
2970 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2971 {\r
2972   HBITMAP oldBitmap;\r
2973   HBRUSH oldBrush;\r
2974   int tmpSize;\r
2975 \r
2976   if (appData.blindfold) return;\r
2977 \r
2978   /* [AS] Use font-based pieces if needed */\r
2979   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2980     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2981     CreatePiecesFromFont();\r
2982 \r
2983     if( fontBitmapSquareSize == squareSize ) {\r
2984         int index = TranslatePieceToFontPiece(piece);\r
2985 \r
2986         SelectObject( tmphdc, hPieceMask[ index ] );\r
2987 \r
2988       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2989         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2990       else\r
2991         BitBlt( hdc,\r
2992             x, y,\r
2993             squareSize, squareSize,\r
2994             tmphdc,\r
2995             0, 0,\r
2996             SRCAND );\r
2997 \r
2998         SelectObject( tmphdc, hPieceFace[ index ] );\r
2999 \r
3000       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3001         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
3002       else\r
3003         BitBlt( hdc,\r
3004             x, y,\r
3005             squareSize, squareSize,\r
3006             tmphdc,\r
3007             0, 0,\r
3008             SRCPAINT );\r
3009 \r
3010         return;\r
3011     }\r
3012   }\r
3013 \r
3014   if (appData.monoMode) {\r
3015     SelectObject(tmphdc, PieceBitmap(piece, \r
3016       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3017     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3018            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3019   } else {\r
3020     HBRUSH xBrush = whitePieceBrush;\r
3021     tmpSize = squareSize;\r
3022     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
3023     if(minorSize &&\r
3024         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3025          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3026       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3027       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3028       x += (squareSize - minorSize)>>1;\r
3029       y += squareSize - minorSize - 2;\r
3030       tmpSize = minorSize;\r
3031     }\r
3032     if (color || appData.allWhite ) {\r
3033       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3034       if( color )\r
3035               oldBrush = SelectObject(hdc, xBrush);\r
3036       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3037       if(appData.upsideDown && color==flipView)\r
3038         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3039       else\r
3040         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3041       /* Use black for outline of white pieces */\r
3042       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3043       if(appData.upsideDown && color==flipView)\r
3044         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3045       else\r
3046         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3047     } else if(appData.pieceDirectory[0]) {\r
3048       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3049       oldBrush = SelectObject(hdc, xBrush);\r
3050       if(appData.upsideDown && color==flipView)\r
3051         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3052       else\r
3053         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3054       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3055       if(appData.upsideDown && color==flipView)\r
3056         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3057       else\r
3058         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3059     } else {\r
3060       /* Use square color for details of black pieces */\r
3061       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3062       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3063       if(appData.upsideDown && !flipView)\r
3064         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3065       else\r
3066         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3067     }\r
3068     SelectObject(hdc, oldBrush);\r
3069     SelectObject(tmphdc, oldBitmap);\r
3070   }\r
3071 }\r
3072 \r
3073 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3074 int GetBackTextureMode( int algo )\r
3075 {\r
3076     int result = BACK_TEXTURE_MODE_DISABLED;\r
3077 \r
3078     switch( algo ) \r
3079     {\r
3080         case BACK_TEXTURE_MODE_PLAIN:\r
3081             result = 1; /* Always use identity map */\r
3082             break;\r
3083         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3084             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3085             break;\r
3086     }\r
3087 \r
3088     return result;\r
3089 }\r
3090 \r
3091 /* \r
3092     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3093     to handle redraws cleanly (as random numbers would always be different).\r
3094 */\r
3095 VOID RebuildTextureSquareInfo()\r
3096 {\r
3097     BITMAP bi;\r
3098     int lite_w = 0;\r
3099     int lite_h = 0;\r
3100     int dark_w = 0;\r
3101     int dark_h = 0;\r
3102     int row;\r
3103     int col;\r
3104 \r
3105     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3106 \r
3107     if( liteBackTexture != NULL ) {\r
3108         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3109             lite_w = bi.bmWidth;\r
3110             lite_h = bi.bmHeight;\r
3111         }\r
3112     }\r
3113 \r
3114     if( darkBackTexture != NULL ) {\r
3115         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3116             dark_w = bi.bmWidth;\r
3117             dark_h = bi.bmHeight;\r
3118         }\r
3119     }\r
3120 \r
3121     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3122         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3123             if( (col + row) & 1 ) {\r
3124                 /* Lite square */\r
3125                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3126                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3127                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3128                   else\r
3129                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3130                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3131                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3132                   else\r
3133                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3134                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3135                 }\r
3136             }\r
3137             else {\r
3138                 /* Dark square */\r
3139                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3140                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3141                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3142                   else\r
3143                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3144                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3145                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3146                   else\r
3147                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3148                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3149                 }\r
3150             }\r
3151         }\r
3152     }\r
3153 }\r
3154 \r
3155 /* [AS] Arrow highlighting support */\r
3156 \r
3157 static double A_WIDTH = 5; /* Width of arrow body */\r
3158 \r
3159 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3160 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3161 \r
3162 static double Sqr( double x )\r
3163 {\r
3164     return x*x;\r
3165 }\r
3166 \r
3167 static int Round( double x )\r
3168 {\r
3169     return (int) (x + 0.5);\r
3170 }\r
3171 \r
3172 /* Draw an arrow between two points using current settings */\r
3173 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3174 {\r
3175     POINT arrow[7];\r
3176     double dx, dy, j, k, x, y;\r
3177 \r
3178     if( d_x == s_x ) {\r
3179         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3180 \r
3181         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3182         arrow[0].y = s_y;\r
3183 \r
3184         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3185         arrow[1].y = d_y - h;\r
3186 \r
3187         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3188         arrow[2].y = d_y - h;\r
3189 \r
3190         arrow[3].x = d_x;\r
3191         arrow[3].y = d_y;\r
3192 \r
3193         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3194         arrow[5].y = d_y - h;\r
3195 \r
3196         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3197         arrow[4].y = d_y - h;\r
3198 \r
3199         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3200         arrow[6].y = s_y;\r
3201     }\r
3202     else if( d_y == s_y ) {\r
3203         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3204 \r
3205         arrow[0].x = s_x;\r
3206         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3207 \r
3208         arrow[1].x = d_x - w;\r
3209         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3210 \r
3211         arrow[2].x = d_x - w;\r
3212         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3213 \r
3214         arrow[3].x = d_x;\r
3215         arrow[3].y = d_y;\r
3216 \r
3217         arrow[5].x = d_x - w;\r
3218         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3219 \r
3220         arrow[4].x = d_x - w;\r
3221         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3222 \r
3223         arrow[6].x = s_x;\r
3224         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3225     }\r
3226     else {\r
3227         /* [AS] Needed a lot of paper for this! :-) */\r
3228         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3229         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3230   \r
3231         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3232 \r
3233         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3234 \r
3235         x = s_x;\r
3236         y = s_y;\r
3237 \r
3238         arrow[0].x = Round(x - j);\r
3239         arrow[0].y = Round(y + j*dx);\r
3240 \r
3241         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3242         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3243 \r
3244         if( d_x > s_x ) {\r
3245             x = (double) d_x - k;\r
3246             y = (double) d_y - k*dy;\r
3247         }\r
3248         else {\r
3249             x = (double) d_x + k;\r
3250             y = (double) d_y + k*dy;\r
3251         }\r
3252 \r
3253         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3254 \r
3255         arrow[6].x = Round(x - j);\r
3256         arrow[6].y = Round(y + j*dx);\r
3257 \r
3258         arrow[2].x = Round(arrow[6].x + 2*j);\r
3259         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3260 \r
3261         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3262         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3263 \r
3264         arrow[4].x = d_x;\r
3265         arrow[4].y = d_y;\r
3266 \r
3267         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3268         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3269     }\r
3270 \r
3271     Polygon( hdc, arrow, 7 );\r
3272 }\r
3273 \r
3274 /* [AS] Draw an arrow between two squares */\r
3275 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3276 {\r
3277     int s_x, s_y, d_x, d_y;\r
3278     HPEN hpen;\r
3279     HPEN holdpen;\r
3280     HBRUSH hbrush;\r
3281     HBRUSH holdbrush;\r
3282     LOGBRUSH stLB;\r
3283 \r
3284     if( s_col == d_col && s_row == d_row ) {\r
3285         return;\r
3286     }\r
3287 \r
3288     /* Get source and destination points */\r
3289     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3290     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3291 \r
3292     if( d_y > s_y ) {\r
3293         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3294     }\r
3295     else if( d_y < s_y ) {\r
3296         d_y += squareSize / 2 + squareSize / 4;\r
3297     }\r
3298     else {\r
3299         d_y += squareSize / 2;\r
3300     }\r
3301 \r
3302     if( d_x > s_x ) {\r
3303         d_x += squareSize / 2 - squareSize / 4;\r
3304     }\r
3305     else if( d_x < s_x ) {\r
3306         d_x += squareSize / 2 + squareSize / 4;\r
3307     }\r
3308     else {\r
3309         d_x += squareSize / 2;\r
3310     }\r
3311 \r
3312     s_x += squareSize / 2;\r
3313     s_y += squareSize / 2;\r
3314 \r
3315     /* Adjust width */\r
3316     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3317 \r
3318     /* Draw */\r
3319     stLB.lbStyle = BS_SOLID;\r
3320     stLB.lbColor = appData.highlightArrowColor;\r
3321     stLB.lbHatch = 0;\r
3322 \r
3323     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3324     holdpen = SelectObject( hdc, hpen );\r
3325     hbrush = CreateBrushIndirect( &stLB );\r
3326     holdbrush = SelectObject( hdc, hbrush );\r
3327 \r
3328     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3329 \r
3330     SelectObject( hdc, holdpen );\r
3331     SelectObject( hdc, holdbrush );\r
3332     DeleteObject( hpen );\r
3333     DeleteObject( hbrush );\r
3334 }\r
3335 \r
3336 BOOL HasHighlightInfo()\r
3337 {\r
3338     BOOL result = FALSE;\r
3339 \r
3340     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3341         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3342     {\r
3343         result = TRUE;\r
3344     }\r
3345 \r
3346     return result;\r
3347 \r
3348 \r
3349 \r
3350 }\r
3351 \r
3352 BOOL IsDrawArrowEnabled()\r
3353 {\r
3354     BOOL result = FALSE;\r
3355 \r
3356     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3357         result = TRUE;\r
3358     }\r
3359 \r
3360     return result;\r
3361 }\r
3362 \r
3363 VOID DrawArrowHighlight( HDC hdc )\r
3364 {\r
3365     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3366         DrawArrowBetweenSquares( hdc,\r
3367             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3368             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3369     }\r
3370 }\r
3371 \r
3372 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3373 {\r
3374     HRGN result = NULL;\r
3375 \r
3376     if( HasHighlightInfo() ) {\r
3377         int x1, y1, x2, y2;\r
3378         int sx, sy, dx, dy;\r
3379 \r
3380         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3381         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3382 \r
3383         sx = MIN( x1, x2 );\r
3384         sy = MIN( y1, y2 );\r
3385         dx = MAX( x1, x2 ) + squareSize;\r
3386         dy = MAX( y1, y2 ) + squareSize;\r
3387 \r
3388         result = CreateRectRgn( sx, sy, dx, dy );\r
3389     }\r
3390 \r
3391     return result;\r
3392 }\r
3393 \r
3394 /*\r
3395     Warning: this function modifies the behavior of several other functions. \r
3396     \r
3397     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3398     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3399     repaint is scattered all over the place, which is not good for features such as\r
3400     "arrow highlighting" that require a full repaint of the board.\r
3401 \r
3402     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3403     user interaction, when speed is not so important) but especially to avoid errors\r
3404     in the displayed graphics.\r
3405 \r
3406     In such patched places, I always try refer to this function so there is a single\r
3407     place to maintain knowledge.\r
3408     \r
3409     To restore the original behavior, just return FALSE unconditionally.\r
3410 */\r
3411 BOOL IsFullRepaintPreferrable()\r
3412 {\r
3413     BOOL result = FALSE;\r
3414 \r
3415     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3416         /* Arrow may appear on the board */\r
3417         result = TRUE;\r
3418     }\r
3419 \r
3420     return result;\r
3421 }\r
3422 \r
3423 /* \r
3424     This function is called by DrawPosition to know whether a full repaint must\r
3425     be forced or not.\r
3426 \r
3427     Only DrawPosition may directly call this function, which makes use of \r
3428     some state information. Other function should call DrawPosition specifying \r
3429     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3430 */\r
3431 BOOL DrawPositionNeedsFullRepaint()\r
3432 {\r
3433     BOOL result = FALSE;\r
3434 \r
3435     /* \r
3436         Probably a slightly better policy would be to trigger a full repaint\r
3437         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3438         but animation is fast enough that it's difficult to notice.\r
3439     */\r
3440     if( animInfo.piece == EmptySquare ) {\r
3441         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3442             result = TRUE;\r
3443         }\r
3444     }\r
3445 \r
3446     return result;\r
3447 }\r
3448 \r
3449 static HBITMAP borderBitmap;\r
3450 \r
3451 VOID\r
3452 DrawBackgroundOnDC(HDC hdc)\r
3453 {\r
3454   \r
3455   BITMAP bi;\r
3456   HDC tmphdc;\r
3457   HBITMAP hbm;\r
3458   static char oldBorder[MSG_SIZ];\r
3459   int w = 600, h = 600, mode;\r
3460 \r
3461   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3462     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3463     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3464   }\r
3465   if(borderBitmap == NULL) { // loading failed, use white\r
3466     FillRect( hdc, &boardRect, whitePieceBrush );\r
3467     return;\r
3468   }\r
3469   tmphdc = CreateCompatibleDC(hdc);\r
3470   hbm = SelectObject(tmphdc, borderBitmap);\r
3471   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3472             w = bi.bmWidth;\r
3473             h = bi.bmHeight;\r
3474   }\r
3475   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3476   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3477                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3478   SetStretchBltMode(hdc, mode);\r
3479   SelectObject(tmphdc, hbm);\r
3480   DeleteDC(tmphdc);\r
3481 }\r
3482 \r
3483 VOID\r
3484 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3485 {\r
3486   int row, column, x, y, square_color, piece_color;\r
3487   ChessSquare piece;\r
3488   HBRUSH oldBrush;\r
3489   HDC texture_hdc = NULL;\r
3490 \r
3491   /* [AS] Initialize background textures if needed */\r
3492   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3493       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3494       if( backTextureSquareSize != squareSize \r
3495        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3496           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3497           backTextureSquareSize = squareSize;\r
3498           RebuildTextureSquareInfo();\r
3499       }\r
3500 \r
3501       texture_hdc = CreateCompatibleDC( hdc );\r
3502   }\r
3503 \r
3504   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3505     for (column = 0; column < BOARD_WIDTH; column++) {\r
3506   \r
3507       SquareToPos(row, column, &x, &y);\r
3508 \r
3509       piece = board[row][column];\r
3510 \r
3511       square_color = ((column + row) % 2) == 1;\r
3512       if( gameInfo.variant == VariantXiangqi ) {\r
3513           square_color = !InPalace(row, column);\r
3514           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3515           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3516       }\r
3517       piece_color = (int) piece < (int) BlackPawn;\r
3518 \r
3519 \r
3520       /* [HGM] holdings file: light square or black */\r
3521       if(column == BOARD_LEFT-2) {\r
3522             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3523                 square_color = 1;\r
3524             else {\r
3525                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3526                 continue;\r
3527             }\r
3528       } else\r
3529       if(column == BOARD_RGHT + 1 ) {\r
3530             if( row < gameInfo.holdingsSize )\r
3531                 square_color = 1;\r
3532             else {\r
3533                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3534                 continue;\r
3535             }\r
3536       }\r
3537       if(column == BOARD_LEFT-1 ) /* left align */\r
3538             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3539       else if( column == BOARD_RGHT) /* right align */\r
3540             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3541       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3542       else\r
3543       if (appData.monoMode) {\r
3544         if (piece == EmptySquare) {\r
3545           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3546                  square_color ? WHITENESS : BLACKNESS);\r
3547         } else {\r
3548           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3549         }\r
3550       } \r
3551       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3552           /* [AS] Draw the square using a texture bitmap */\r
3553           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3554           int r = row, c = column; // [HGM] do not flip board in flipView\r
3555           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3556 \r
3557           DrawTile( x, y, \r
3558               squareSize, squareSize, \r
3559               hdc, \r
3560               texture_hdc,\r
3561               backTextureSquareInfo[r][c].mode,\r
3562               backTextureSquareInfo[r][c].x,\r
3563               backTextureSquareInfo[r][c].y );\r
3564 \r
3565           SelectObject( texture_hdc, hbm );\r
3566 \r
3567           if (piece != EmptySquare) {\r
3568               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3569           }\r
3570       }\r
3571       else {\r
3572         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3573 \r
3574         oldBrush = SelectObject(hdc, brush );\r
3575         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3576         SelectObject(hdc, oldBrush);\r
3577         if (piece != EmptySquare)\r
3578           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3579       }\r
3580     }\r
3581   }\r
3582 \r
3583   if( texture_hdc != NULL ) {\r
3584     DeleteDC( texture_hdc );\r
3585   }\r
3586 }\r
3587 \r
3588 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3589 void fputDW(FILE *f, int x)\r
3590 {\r
3591         fputc(x     & 255, f);\r
3592         fputc(x>>8  & 255, f);\r
3593         fputc(x>>16 & 255, f);\r
3594         fputc(x>>24 & 255, f);\r
3595 }\r
3596 \r
3597 #define MAX_CLIPS 200   /* more than enough */\r
3598 \r
3599 VOID\r
3600 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3601 {\r
3602 //  HBITMAP bufferBitmap;\r
3603   BITMAP bi;\r
3604 //  RECT Rect;\r
3605   HDC tmphdc;\r
3606   HBITMAP hbm;\r
3607   int w = 100, h = 50;\r
3608 \r
3609   if(logo == NULL) {\r
3610     if(!logoHeight) return;\r
3611     FillRect( hdc, &logoRect, whitePieceBrush );\r
3612   }\r
3613 //  GetClientRect(hwndMain, &Rect);\r
3614 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3615 //                                      Rect.bottom-Rect.top+1);\r
3616   tmphdc = CreateCompatibleDC(hdc);\r
3617   hbm = SelectObject(tmphdc, logo);\r
3618   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3619             w = bi.bmWidth;\r
3620             h = bi.bmHeight;\r
3621   }\r
3622   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3623                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3624   SelectObject(tmphdc, hbm);\r
3625   DeleteDC(tmphdc);\r
3626 }\r
3627 \r
3628 VOID\r
3629 DisplayLogos()\r
3630 {\r
3631   if(logoHeight) {\r
3632         HDC hdc = GetDC(hwndMain);\r
3633         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3634         if(appData.autoLogo) {\r
3635           \r
3636           switch(gameMode) { // pick logos based on game mode\r
3637             case IcsObserving:\r
3638                 whiteLogo = second.programLogo; // ICS logo\r
3639                 blackLogo = second.programLogo;\r
3640             default:\r
3641                 break;\r
3642             case IcsPlayingWhite:\r
3643                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3644                 blackLogo = second.programLogo; // ICS logo\r
3645                 break;\r
3646             case IcsPlayingBlack:\r
3647                 whiteLogo = second.programLogo; // ICS logo\r
3648                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3649                 break;\r
3650             case TwoMachinesPlay:\r
3651                 if(first.twoMachinesColor[0] == 'b') {\r
3652                     whiteLogo = second.programLogo;\r
3653                     blackLogo = first.programLogo;\r
3654                 }\r
3655                 break;\r
3656             case MachinePlaysWhite:\r
3657                 blackLogo = userLogo;\r
3658                 break;\r
3659             case MachinePlaysBlack:\r
3660                 whiteLogo = userLogo;\r
3661                 blackLogo = first.programLogo;\r
3662           }\r
3663         }\r
3664         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3665         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3666         ReleaseDC(hwndMain, hdc);\r
3667   }\r
3668 }\r
3669 \r
3670 void\r
3671 UpdateLogos(int display)\r
3672 { // called after loading new engine(s), in tourney or from menu\r
3673   LoadLogo(&first, 0, FALSE);\r
3674   LoadLogo(&second, 1, appData.icsActive);\r
3675   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3676   if(display) DisplayLogos();\r
3677 }\r
3678 \r
3679 static HDC hdcSeek;\r
3680 \r
3681 // [HGM] seekgraph\r
3682 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3683 {\r
3684     POINT stPt;\r
3685     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3686     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3687     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3688     SelectObject( hdcSeek, hp );\r
3689 }\r
3690 \r
3691 // front-end wrapper for drawing functions to do rectangles\r
3692 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3693 {\r
3694     HPEN hp;\r
3695     RECT rc;\r
3696 \r
3697     if (hdcSeek == NULL) {\r
3698     hdcSeek = GetDC(hwndMain);\r
3699       if (!appData.monoMode) {\r
3700         SelectPalette(hdcSeek, hPal, FALSE);\r
3701         RealizePalette(hdcSeek);\r
3702       }\r
3703     }\r
3704     hp = SelectObject( hdcSeek, gridPen );\r
3705     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3706     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3707     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3708     SelectObject( hdcSeek, hp );\r
3709 }\r
3710 \r
3711 // front-end wrapper for putting text in graph\r
3712 void DrawSeekText(char *buf, int x, int y)\r
3713 {\r
3714         SIZE stSize;\r
3715         SetBkMode( hdcSeek, TRANSPARENT );\r
3716         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3717         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3718 }\r
3719 \r
3720 void DrawSeekDot(int x, int y, int color)\r
3721 {\r
3722         int square = color & 0x80;\r
3723         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3724                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3725         color &= 0x7F;\r
3726         if(square)\r
3727             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3728                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3729         else\r
3730             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3731                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3732             SelectObject(hdcSeek, oldBrush);\r
3733 }\r
3734 \r
3735 void DrawSeekOpen()\r
3736 {\r
3737 }\r
3738 \r
3739 void DrawSeekClose()\r
3740 {\r
3741 }\r
3742 \r
3743 VOID\r
3744 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3745 {\r
3746   static Board lastReq[2], lastDrawn[2];\r
3747   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3748   static int lastDrawnFlipView = 0;\r
3749   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3750   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3751   HDC tmphdc;\r
3752   HDC hdcmem;\r
3753   HBITMAP bufferBitmap;\r
3754   HBITMAP oldBitmap;\r
3755   RECT Rect;\r
3756   HRGN clips[MAX_CLIPS];\r
3757   ChessSquare dragged_piece = EmptySquare;\r
3758   int nr = twoBoards*partnerUp;\r
3759 \r
3760   /* I'm undecided on this - this function figures out whether a full\r
3761    * repaint is necessary on its own, so there's no real reason to have the\r
3762    * caller tell it that.  I think this can safely be set to FALSE - but\r
3763    * if we trust the callers not to request full repaints unnessesarily, then\r
3764    * we could skip some clipping work.  In other words, only request a full\r
3765    * redraw when the majority of pieces have changed positions (ie. flip, \r
3766    * gamestart and similar)  --Hawk\r
3767    */\r
3768   Boolean fullrepaint = repaint;\r
3769 \r
3770   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3771 \r
3772   if( DrawPositionNeedsFullRepaint() ) {\r
3773       fullrepaint = TRUE;\r
3774   }\r
3775 \r
3776   if (board == NULL) {\r
3777     if (!lastReqValid[nr]) {\r
3778       return;\r
3779     }\r
3780     board = lastReq[nr];\r
3781   } else {\r
3782     CopyBoard(lastReq[nr], board);\r
3783     lastReqValid[nr] = 1;\r
3784   }\r
3785 \r
3786   if (doingSizing) {\r
3787     return;\r
3788   }\r
3789 \r
3790   if (IsIconic(hwndMain)) {\r
3791     return;\r
3792   }\r
3793 \r
3794   if (hdc == NULL) {\r
3795     hdc = GetDC(hwndMain);\r
3796     if (!appData.monoMode) {\r
3797       SelectPalette(hdc, hPal, FALSE);\r
3798       RealizePalette(hdc);\r
3799     }\r
3800     releaseDC = TRUE;\r
3801   } else {\r
3802     releaseDC = FALSE;\r
3803   }\r
3804 \r
3805   /* Create some work-DCs */\r
3806   hdcmem = CreateCompatibleDC(hdc);\r
3807   tmphdc = CreateCompatibleDC(hdc);\r
3808 \r
3809   /* If dragging is in progress, we temporarely remove the piece */\r
3810   /* [HGM] or temporarily decrease count if stacked              */\r
3811   /*       !! Moved to before board compare !!                   */\r
3812   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3813     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3814     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3815             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3816         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3817     } else \r
3818     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3819             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3820         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3821     } else \r
3822         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3823   }\r
3824 \r
3825   /* Figure out which squares need updating by comparing the \r
3826    * newest board with the last drawn board and checking if\r
3827    * flipping has changed.\r
3828    */\r
3829   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3830     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3831       for (column = 0; column < BOARD_WIDTH; column++) {\r
3832         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3833           SquareToPos(row, column, &x, &y);\r
3834           clips[num_clips++] =\r
3835             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3836         }\r
3837       }\r
3838     }\r
3839    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3840     for (i=0; i<2; i++) {\r
3841       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3842           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3843         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3844             lastDrawnHighlight.sq[i].y >= 0) {\r
3845           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3846                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3847           clips[num_clips++] =\r
3848             CreateRectRgn(x - lineGap, y - lineGap, \r
3849                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3850         }\r
3851         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3852           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3853           clips[num_clips++] =\r
3854             CreateRectRgn(x - lineGap, y - lineGap, \r
3855                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3856         }\r
3857       }\r
3858     }\r
3859     for (i=0; i<2; i++) {\r
3860       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3861           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3862         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3863             lastDrawnPremove.sq[i].y >= 0) {\r
3864           SquareToPos(lastDrawnPremove.sq[i].y,\r
3865                       lastDrawnPremove.sq[i].x, &x, &y);\r
3866           clips[num_clips++] =\r
3867             CreateRectRgn(x - lineGap, y - lineGap, \r
3868                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3869         }\r
3870         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3871             premoveHighlightInfo.sq[i].y >= 0) {\r
3872           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3873                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3874           clips[num_clips++] =\r
3875             CreateRectRgn(x - lineGap, y - lineGap, \r
3876                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3877         }\r
3878       }\r
3879     }\r
3880    } else { // nr == 1\r
3881         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3882         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3883         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3884         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3885       for (i=0; i<2; i++) {\r
3886         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3887             partnerHighlightInfo.sq[i].y >= 0) {\r
3888           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3889                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3890           clips[num_clips++] =\r
3891             CreateRectRgn(x - lineGap, y - lineGap, \r
3892                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3893         }\r
3894         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3895             oldPartnerHighlight.sq[i].y >= 0) {\r
3896           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3897                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3898           clips[num_clips++] =\r
3899             CreateRectRgn(x - lineGap, y - lineGap, \r
3900                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3901         }\r
3902       }\r
3903    }\r
3904   } else {\r
3905     fullrepaint = TRUE;\r
3906   }\r
3907 \r
3908   /* Create a buffer bitmap - this is the actual bitmap\r
3909    * being written to.  When all the work is done, we can\r
3910    * copy it to the real DC (the screen).  This avoids\r
3911    * the problems with flickering.\r
3912    */\r
3913   GetClientRect(hwndMain, &Rect);\r
3914   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3915                                         Rect.bottom-Rect.top+1);\r
3916   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3917   if (!appData.monoMode) {\r
3918     SelectPalette(hdcmem, hPal, FALSE);\r
3919   }\r
3920 \r
3921   /* Create clips for dragging */\r
3922   if (!fullrepaint) {\r
3923     if (dragInfo.from.x >= 0) {\r
3924       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3925       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3926     }\r
3927     if (dragInfo.start.x >= 0) {\r
3928       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3929       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3930     }\r
3931     if (dragInfo.pos.x >= 0) {\r
3932       x = dragInfo.pos.x - squareSize / 2;\r
3933       y = dragInfo.pos.y - squareSize / 2;\r
3934       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3935     }\r
3936     if (dragInfo.lastpos.x >= 0) {\r
3937       x = dragInfo.lastpos.x - squareSize / 2;\r
3938       y = dragInfo.lastpos.y - squareSize / 2;\r
3939       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3940     }\r
3941   }\r
3942 \r
3943   /* Are we animating a move?  \r
3944    * If so, \r
3945    *   - remove the piece from the board (temporarely)\r
3946    *   - calculate the clipping region\r
3947    */\r
3948   if (!fullrepaint) {\r
3949     if (animInfo.piece != EmptySquare) {\r
3950       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3951       x = boardRect.left + animInfo.lastpos.x;\r
3952       y = boardRect.top + animInfo.lastpos.y;\r
3953       x2 = boardRect.left + animInfo.pos.x;\r
3954       y2 = boardRect.top + animInfo.pos.y;\r
3955       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3956       /* Slight kludge.  The real problem is that after AnimateMove is\r
3957          done, the position on the screen does not match lastDrawn.\r
3958          This currently causes trouble only on e.p. captures in\r
3959          atomic, where the piece moves to an empty square and then\r
3960          explodes.  The old and new positions both had an empty square\r
3961          at the destination, but animation has drawn a piece there and\r
3962          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3963       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3964     }\r
3965   }\r
3966 \r
3967   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3968   if (num_clips == 0)\r
3969     fullrepaint = TRUE;\r
3970 \r
3971   /* Set clipping on the memory DC */\r
3972   if (!fullrepaint) {\r
3973     SelectClipRgn(hdcmem, clips[0]);\r
3974     for (x = 1; x < num_clips; x++) {\r
3975       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3976         abort();  // this should never ever happen!\r
3977     }\r
3978   }\r
3979 \r
3980   /* Do all the drawing to the memory DC */\r
3981   if(explodeInfo.radius) { // [HGM] atomic\r
3982         HBRUSH oldBrush;\r
3983         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3984         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3985         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3986         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3987         x += squareSize/2;\r
3988         y += squareSize/2;\r
3989         if(!fullrepaint) {\r
3990           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3991           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3992         }\r
3993         DrawGridOnDC(hdcmem);\r
3994         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3995         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3996         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3997         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3998         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3999         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4000         SelectObject(hdcmem, oldBrush);\r
4001   } else {\r
4002     if(border) DrawBackgroundOnDC(hdcmem);\r
4003     DrawGridOnDC(hdcmem);\r
4004     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
4005         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4006         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4007     } else {\r
4008         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
4009         oldPartnerHighlight = partnerHighlightInfo;\r
4010     }\r
4011     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4012   }\r
4013   if(nr == 0) // [HGM] dual: markers only on left board\r
4014   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4015     for (column = 0; column < BOARD_WIDTH; column++) {\r
4016         if (marker[row][column]) { // marker changes only occur with full repaint!\r
4017             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
4018             SquareToPos(row, column, &x, &y);\r
4019             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
4020                           x + 3*squareSize/4, y + 3*squareSize/4);\r
4021             SelectObject(hdcmem, oldBrush);\r
4022         }\r
4023     }\r
4024   }\r
4025 \r
4026   if( appData.highlightMoveWithArrow ) {\r
4027 \r
4028     DrawArrowHighlight(hdcmem);\r
4029   }\r
4030 \r
4031   DrawCoordsOnDC(hdcmem);\r
4032 \r
4033   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4034                  /* to make sure lastDrawn contains what is actually drawn */\r
4035 \r
4036   /* Put the dragged piece back into place and draw it (out of place!) */\r
4037     if (dragged_piece != EmptySquare) {\r
4038     /* [HGM] or restack */\r
4039     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4040                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4041     else\r
4042     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4043                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4044 \r
4045     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4046     x = dragInfo.pos.x - squareSize / 2;\r
4047     y = dragInfo.pos.y - squareSize / 2;\r
4048     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4049                   ((int) dragInfo.piece < (int) BlackPawn), \r
4050                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4051   }   \r
4052   \r
4053   /* Put the animated piece back into place and draw it */\r
4054   if (animInfo.piece != EmptySquare) {\r
4055     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4056     x = boardRect.left + animInfo.pos.x;\r
4057     y = boardRect.top + animInfo.pos.y;\r
4058     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4059                   ((int) animInfo.piece < (int) BlackPawn),\r
4060                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4061   }\r
4062 \r
4063   /* Release the bufferBitmap by selecting in the old bitmap \r
4064    * and delete the memory DC\r
4065    */\r
4066   SelectObject(hdcmem, oldBitmap);\r
4067   DeleteDC(hdcmem);\r
4068 \r
4069   /* Set clipping on the target DC */\r
4070   if (!fullrepaint) {\r
4071     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4072         RECT rect;\r
4073         GetRgnBox(clips[x], &rect);\r
4074         DeleteObject(clips[x]);\r
4075         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4076                           rect.right + wpMain.width/2, rect.bottom);\r
4077     }\r
4078     SelectClipRgn(hdc, clips[0]);\r
4079     for (x = 1; x < num_clips; x++) {\r
4080       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4081         abort();   // this should never ever happen!\r
4082     } \r
4083   }\r
4084 \r
4085   /* Copy the new bitmap onto the screen in one go.\r
4086    * This way we avoid any flickering\r
4087    */\r
4088   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4089   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4090          boardRect.right - boardRect.left,\r
4091          boardRect.bottom - boardRect.top,\r
4092          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4093   if(saveDiagFlag) { \r
4094     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4095     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4096 \r
4097     GetObject(bufferBitmap, sizeof(b), &b);\r
4098     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4099         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4100         bih.biWidth = b.bmWidth;\r
4101         bih.biHeight = b.bmHeight;\r
4102         bih.biPlanes = 1;\r
4103         bih.biBitCount = b.bmBitsPixel;\r
4104         bih.biCompression = 0;\r
4105         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4106         bih.biXPelsPerMeter = 0;\r
4107         bih.biYPelsPerMeter = 0;\r
4108         bih.biClrUsed = 0;\r
4109         bih.biClrImportant = 0;\r
4110 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4111 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4112         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4113 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4114 \r
4115         wb = b.bmWidthBytes;\r
4116         // count colors\r
4117         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4118                 int k = ((int*) pData)[i];\r
4119                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4120                 if(j >= 16) break;\r
4121                 color[j] = k;\r
4122                 if(j >= nrColors) nrColors = j+1;\r
4123         }\r
4124         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4125                 INT p = 0;\r
4126                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4127                     for(w=0; w<(wb>>2); w+=2) {\r
4128                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4129                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4130                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4131                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4132                         pData[p++] = m | j<<4;\r
4133                     }\r
4134                     while(p&3) pData[p++] = 0;\r
4135                 }\r
4136                 fac = 3;\r
4137                 wb = ((wb+31)>>5)<<2;\r
4138         }\r
4139         // write BITMAPFILEHEADER\r
4140         fprintf(diagFile, "BM");\r
4141         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4142         fputDW(diagFile, 0);\r
4143         fputDW(diagFile, 0x36 + (fac?64:0));\r
4144         // write BITMAPINFOHEADER\r
4145         fputDW(diagFile, 40);\r
4146         fputDW(diagFile, b.bmWidth);\r
4147         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4148         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4149         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4150         fputDW(diagFile, 0);\r
4151         fputDW(diagFile, 0);\r
4152         fputDW(diagFile, 0);\r
4153         fputDW(diagFile, 0);\r
4154         fputDW(diagFile, 0);\r
4155         fputDW(diagFile, 0);\r
4156         // write color table\r
4157         if(fac)\r
4158         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4159         // write bitmap data\r
4160         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4161                 fputc(pData[i], diagFile);\r
4162         free(pData);\r
4163      }\r
4164   }\r
4165 \r
4166   SelectObject(tmphdc, oldBitmap);\r
4167 \r
4168   /* Massive cleanup */\r
4169   for (x = 0; x < num_clips; x++)\r
4170     DeleteObject(clips[x]);\r
4171 \r
4172   DeleteDC(tmphdc);\r
4173   DeleteObject(bufferBitmap);\r
4174 \r
4175   if (releaseDC) \r
4176     ReleaseDC(hwndMain, hdc);\r
4177   \r
4178   if (lastDrawnFlipView != flipView && nr == 0) {\r
4179     if (flipView)\r
4180       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4181     else\r
4182       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4183   }\r
4184 \r
4185 /*  CopyBoard(lastDrawn, board);*/\r
4186   lastDrawnHighlight = highlightInfo;\r
4187   lastDrawnPremove   = premoveHighlightInfo;\r
4188   lastDrawnFlipView = flipView;\r
4189   lastDrawnValid[nr] = 1;\r
4190 }\r
4191 \r
4192 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4193 int\r
4194 SaveDiagram(f)\r
4195      FILE *f;\r
4196 {\r
4197     saveDiagFlag = 1; diagFile = f;\r
4198     HDCDrawPosition(NULL, TRUE, NULL);\r
4199     saveDiagFlag = 0;\r
4200 \r
4201     fclose(f);\r
4202     return TRUE;\r
4203 }\r
4204 \r
4205 \r
4206 /*---------------------------------------------------------------------------*\\r
4207 | CLIENT PAINT PROCEDURE\r
4208 |   This is the main event-handler for the WM_PAINT message.\r
4209 |\r
4210 \*---------------------------------------------------------------------------*/\r
4211 VOID\r
4212 PaintProc(HWND hwnd)\r
4213 {\r
4214   HDC         hdc;\r
4215   PAINTSTRUCT ps;\r
4216   HFONT       oldFont;\r
4217 \r
4218   if((hdc = BeginPaint(hwnd, &ps))) {\r
4219     if (IsIconic(hwnd)) {\r
4220       DrawIcon(hdc, 2, 2, iconCurrent);\r
4221     } else {\r
4222       if (!appData.monoMode) {\r
4223         SelectPalette(hdc, hPal, FALSE);\r
4224         RealizePalette(hdc);\r
4225       }\r
4226       HDCDrawPosition(hdc, 1, NULL);\r
4227       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4228         flipView = !flipView; partnerUp = !partnerUp;\r
4229         HDCDrawPosition(hdc, 1, NULL);\r
4230         flipView = !flipView; partnerUp = !partnerUp;\r
4231       }\r
4232       oldFont =\r
4233         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4234       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4235                  ETO_CLIPPED|ETO_OPAQUE,\r
4236                  &messageRect, messageText, strlen(messageText), NULL);\r
4237       SelectObject(hdc, oldFont);\r
4238       DisplayBothClocks();\r
4239       DisplayLogos();\r
4240     }\r
4241     EndPaint(hwnd,&ps);\r
4242   }\r
4243 \r
4244   return;\r
4245 }\r
4246 \r
4247 \r
4248 /*\r
4249  * If the user selects on a border boundary, return -1; if off the board,\r
4250  *   return -2.  Otherwise map the event coordinate to the square.\r
4251  * The offset boardRect.left or boardRect.top must already have been\r
4252  *   subtracted from x.\r
4253  */\r
4254 int EventToSquare(x, limit)\r
4255      int x, limit;\r
4256 {\r
4257   if (x <= border)\r
4258     return -2;\r
4259   if (x < lineGap + border)\r
4260     return -1;\r
4261   x -= lineGap + border;\r
4262   if ((x % (squareSize + lineGap)) >= squareSize)\r
4263     return -1;\r
4264   x /= (squareSize + lineGap);\r
4265     if (x >= limit)\r
4266     return -2;\r
4267   return x;\r
4268 }\r
4269 \r
4270 typedef struct {\r
4271   char piece;\r
4272   int command;\r
4273   char* name;\r
4274 } DropEnable;\r
4275 \r
4276 DropEnable dropEnables[] = {\r
4277   { 'P', DP_Pawn, N_("Pawn") },\r
4278   { 'N', DP_Knight, N_("Knight") },\r
4279   { 'B', DP_Bishop, N_("Bishop") },\r
4280   { 'R', DP_Rook, N_("Rook") },\r
4281   { 'Q', DP_Queen, N_("Queen") },\r
4282 };\r
4283 \r
4284 VOID\r
4285 SetupDropMenu(HMENU hmenu)\r
4286 {\r
4287   int i, count, enable;\r
4288   char *p;\r
4289   extern char white_holding[], black_holding[];\r
4290   char item[MSG_SIZ];\r
4291 \r
4292   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4293     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4294                dropEnables[i].piece);\r
4295     count = 0;\r
4296     while (p && *p++ == dropEnables[i].piece) count++;\r
4297       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4298     enable = count > 0 || !appData.testLegality\r
4299       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4300                       && !appData.icsActive);\r
4301     ModifyMenu(hmenu, dropEnables[i].command,\r
4302                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4303                dropEnables[i].command, item);\r
4304   }\r
4305 }\r
4306 \r
4307 void DragPieceBegin(int x, int y, Boolean instantly)\r
4308 {\r
4309       dragInfo.lastpos.x = boardRect.left + x;\r
4310       dragInfo.lastpos.y = boardRect.top + y;\r
4311       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4312       dragInfo.from.x = fromX;\r
4313       dragInfo.from.y = fromY;\r
4314       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4315       dragInfo.start = dragInfo.from;\r
4316       SetCapture(hwndMain);\r
4317 }\r
4318 \r
4319 void DragPieceEnd(int x, int y)\r
4320 {\r
4321     ReleaseCapture();\r
4322     dragInfo.start.x = dragInfo.start.y = -1;\r
4323     dragInfo.from = dragInfo.start;\r
4324     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4325 }\r
4326 \r
4327 void ChangeDragPiece(ChessSquare piece)\r
4328 {\r
4329     dragInfo.piece = piece;\r
4330 }\r
4331 \r
4332 /* Event handler for mouse messages */\r
4333 VOID\r
4334 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4335 {\r
4336   int x, y, menuNr;\r
4337   POINT pt;\r
4338   static int recursive = 0;\r
4339   HMENU hmenu;\r
4340   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4341 \r
4342   if (recursive) {\r
4343     if (message == WM_MBUTTONUP) {\r
4344       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4345          to the middle button: we simulate pressing the left button too!\r
4346          */\r
4347       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4348       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4349     }\r
4350     return;\r
4351   }\r
4352   recursive++;\r
4353   \r
4354   pt.x = LOWORD(lParam);\r
4355   pt.y = HIWORD(lParam);\r
4356   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4357   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4358   if (!flipView && y >= 0) {\r
4359     y = BOARD_HEIGHT - 1 - y;\r
4360   }\r
4361   if (flipView && x >= 0) {\r
4362     x = BOARD_WIDTH - 1 - x;\r
4363   }\r
4364 \r
4365   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4366   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4367 \r
4368   switch (message) {\r
4369   case WM_LBUTTONDOWN:\r
4370       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4371         ClockClick(flipClock); break;\r
4372       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4373         ClockClick(!flipClock); break;\r
4374       }\r
4375     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4376       dragInfo.start.x = dragInfo.start.y = -1;\r
4377       dragInfo.from = dragInfo.start;\r
4378     }\r
4379     if(fromX == -1 && frozen) { // not sure where this is for\r
4380                 fromX = fromY = -1; \r
4381       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4382       break;\r
4383     }\r
4384       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4385       DrawPosition(TRUE, NULL);\r
4386     break;\r
4387 \r
4388   case WM_LBUTTONUP:\r
4389       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4390       DrawPosition(TRUE, NULL);\r
4391     break;\r
4392 \r
4393   case WM_MOUSEMOVE:\r
4394     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4395     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4396     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4397     if ((appData.animateDragging || appData.highlightDragging)\r
4398         && (wParam & MK_LBUTTON || dragging == 2)\r
4399         && dragInfo.from.x >= 0) \r
4400     {\r
4401       BOOL full_repaint = FALSE;\r
4402 \r
4403       if (appData.animateDragging) {\r
4404         dragInfo.pos = pt;\r
4405       }\r
4406       if (appData.highlightDragging) {\r
4407         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4408         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4409             full_repaint = TRUE;\r
4410         }\r
4411       }\r
4412       \r
4413       DrawPosition( full_repaint, NULL);\r
4414       \r
4415       dragInfo.lastpos = dragInfo.pos;\r
4416     }\r
4417     break;\r
4418 \r
4419   case WM_MOUSEWHEEL: // [DM]\r
4420     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4421        /* Mouse Wheel is being rolled forward\r
4422         * Play moves forward\r
4423         */\r
4424        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4425                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4426        /* Mouse Wheel is being rolled backward\r
4427         * Play moves backward\r
4428         */\r
4429        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4430                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4431     }\r
4432     break;\r
4433 \r
4434   case WM_MBUTTONUP:\r
4435   case WM_RBUTTONUP:\r
4436     ReleaseCapture();\r
4437     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4438     break;\r
4439  \r
4440   case WM_MBUTTONDOWN:\r
4441   case WM_RBUTTONDOWN:\r
4442     ErrorPopDown();\r
4443     ReleaseCapture();\r
4444     fromX = fromY = -1;\r
4445     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4446     dragInfo.start.x = dragInfo.start.y = -1;\r
4447     dragInfo.from = dragInfo.start;\r
4448     dragInfo.lastpos = dragInfo.pos;\r
4449     if (appData.highlightDragging) {\r
4450       ClearHighlights();\r
4451     }\r
4452     if(y == -2) {\r
4453       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4454       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4455           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4456       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4457           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4458       }\r
4459       break;\r
4460     }\r
4461     DrawPosition(TRUE, NULL);\r
4462 \r
4463     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4464     switch (menuNr) {\r
4465     case 0:\r
4466       if (message == WM_MBUTTONDOWN) {\r
4467         buttonCount = 3;  /* even if system didn't think so */\r
4468         if (wParam & MK_SHIFT) \r
4469           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4470         else\r
4471           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4472       } else { /* message == WM_RBUTTONDOWN */\r
4473         /* Just have one menu, on the right button.  Windows users don't\r
4474            think to try the middle one, and sometimes other software steals\r
4475            it, or it doesn't really exist. */\r
4476         if(gameInfo.variant != VariantShogi)\r
4477             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4478         else\r
4479             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4480       }\r
4481       break;\r
4482     case 2:\r
4483       SetCapture(hwndMain);\r
4484       break;\r
4485     case 1:\r
4486       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4487       SetupDropMenu(hmenu);\r
4488       MenuPopup(hwnd, pt, hmenu, -1);\r
4489     default:\r
4490       break;\r
4491     }\r
4492     break;\r
4493   }\r
4494 \r
4495   recursive--;\r
4496 }\r
4497 \r
4498 /* Preprocess messages for buttons in main window */\r
4499 LRESULT CALLBACK\r
4500 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4501 {\r
4502   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4503   int i, dir;\r
4504 \r
4505   for (i=0; i<N_BUTTONS; i++) {\r
4506     if (buttonDesc[i].id == id) break;\r
4507   }\r
4508   if (i == N_BUTTONS) return 0;\r
4509   switch (message) {\r
4510   case WM_KEYDOWN:\r
4511     switch (wParam) {\r
4512     case VK_LEFT:\r
4513     case VK_RIGHT:\r
4514       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4515       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4516       return TRUE;\r
4517     }\r
4518     break;\r
4519   case WM_CHAR:\r
4520     switch (wParam) {\r
4521     case '\r':\r
4522       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4523       return TRUE;\r
4524     default:\r
4525       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4526         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4527         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4528         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4529         SetFocus(h);\r
4530         SendMessage(h, WM_CHAR, wParam, lParam);\r
4531         return TRUE;\r
4532       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4533         TypeInEvent((char)wParam);\r
4534       }\r
4535       break;\r
4536     }\r
4537     break;\r
4538   }\r
4539   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4540 }\r
4541 \r
4542 static int promoStyle;\r
4543 \r
4544 /* Process messages for Promotion dialog box */\r
4545 LRESULT CALLBACK\r
4546 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4547 {\r
4548 \r
4549   char promoChar;\r
4550 \r
4551   switch (message) {\r
4552 \r
4553   case WM_INITDIALOG: /* message: initialize dialog box */\r
4554     /* Center the dialog over the application window */\r
4555     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4556     Translate(hDlg, DLG_PromotionKing);\r
4557     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4558       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4559        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4560        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4561                SW_SHOW : SW_HIDE);\r
4562     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4563     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4564        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4565          PieceToChar(WhiteAngel) != '~') ||\r
4566         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4567          PieceToChar(BlackAngel) != '~')   ) ?\r
4568                SW_SHOW : SW_HIDE);\r
4569     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4570        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4571          PieceToChar(WhiteMarshall) != '~') ||\r
4572         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4573          PieceToChar(BlackMarshall) != '~')   ) ?\r
4574                SW_SHOW : SW_HIDE);\r
4575     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4576     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4577     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4578     if(promoStyle) {\r
4579         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4580         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4581         SetWindowText(hDlg, "Promote?");\r
4582     }\r
4583     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4584        gameInfo.variant == VariantSuper ?\r
4585                SW_SHOW : SW_HIDE);\r
4586     return TRUE;\r
4587 \r
4588   case WM_COMMAND: /* message: received a command */\r
4589     switch (LOWORD(wParam)) {\r
4590     case IDCANCEL:\r
4591       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4592       ClearHighlights();\r
4593       DrawPosition(FALSE, NULL);\r
4594       return TRUE;\r
4595     case PB_King:\r
4596       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4597       break;\r
4598     case PB_Queen:\r
4599       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4600       break;\r
4601     case PB_Rook:\r
4602       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4603       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4604       break;\r
4605     case PB_Bishop:\r
4606       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4607       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4608       break;\r
4609     case PB_Chancellor:\r
4610       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4611       break;\r
4612     case PB_Archbishop:\r
4613       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4614       break;\r
4615     case PB_Knight:\r
4616       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4617                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4618       break;\r
4619     default:\r
4620       return FALSE;\r
4621     }\r
4622     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4623     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4624     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4625     fromX = fromY = -1;\r
4626     if (!appData.highlightLastMove) {\r
4627       ClearHighlights();\r
4628       DrawPosition(FALSE, NULL);\r
4629     }\r
4630     return TRUE;\r
4631   }\r
4632   return FALSE;\r
4633 }\r
4634 \r
4635 /* Pop up promotion dialog */\r
4636 VOID\r
4637 PromotionPopup(HWND hwnd)\r
4638 {\r
4639   FARPROC lpProc;\r
4640 \r
4641   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4642   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4643     hwnd, (DLGPROC)lpProc);\r
4644   FreeProcInstance(lpProc);\r
4645 }\r
4646 \r
4647 void\r
4648 PromotionPopUp(char choice)\r
4649 {\r
4650   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4651   DrawPosition(TRUE, NULL);\r
4652   PromotionPopup(hwndMain);\r
4653 }\r
4654 \r
4655 VOID\r
4656 LoadGameDialog(HWND hwnd, char* title)\r
4657 {\r
4658   UINT number = 0;\r
4659   FILE *f;\r
4660   char fileTitle[MSG_SIZ];\r
4661   f = OpenFileDialog(hwnd, "rb", "",\r
4662                      appData.oldSaveStyle ? "gam" : "pgn",\r
4663                      GAME_FILT,\r
4664                      title, &number, fileTitle, NULL);\r
4665   if (f != NULL) {\r
4666     cmailMsgLoaded = FALSE;\r
4667     if (number == 0) {\r
4668       int error = GameListBuild(f);\r
4669       if (error) {\r
4670         DisplayError(_("Cannot build game list"), error);\r
4671       } else if (!ListEmpty(&gameList) &&\r
4672                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4673         GameListPopUp(f, fileTitle);\r
4674         return;\r
4675       }\r
4676       GameListDestroy();\r
4677       number = 1;\r
4678     }\r
4679     LoadGame(f, number, fileTitle, FALSE);\r
4680   }\r
4681 }\r
4682 \r
4683 int get_term_width()\r
4684 {\r
4685     HDC hdc;\r
4686     TEXTMETRIC tm;\r
4687     RECT rc;\r
4688     HFONT hfont, hold_font;\r
4689     LOGFONT lf;\r
4690     HWND hText;\r
4691 \r
4692     if (hwndConsole)\r
4693         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4694     else\r
4695         return 79;\r
4696 \r
4697     // get the text metrics\r
4698     hdc = GetDC(hText);\r
4699     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4700     if (consoleCF.dwEffects & CFE_BOLD)\r
4701         lf.lfWeight = FW_BOLD;\r
4702     if (consoleCF.dwEffects & CFE_ITALIC)\r
4703         lf.lfItalic = TRUE;\r
4704     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4705         lf.lfStrikeOut = TRUE;\r
4706     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4707         lf.lfUnderline = TRUE;\r
4708     hfont = CreateFontIndirect(&lf);\r
4709     hold_font = SelectObject(hdc, hfont);\r
4710     GetTextMetrics(hdc, &tm);\r
4711     SelectObject(hdc, hold_font);\r
4712     DeleteObject(hfont);\r
4713     ReleaseDC(hText, hdc);\r
4714 \r
4715     // get the rectangle\r
4716     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4717 \r
4718     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4719 }\r
4720 \r
4721 void UpdateICSWidth(HWND hText)\r
4722 {\r
4723     LONG old_width, new_width;\r
4724 \r
4725     new_width = get_term_width(hText, FALSE);\r
4726     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4727     if (new_width != old_width)\r
4728     {\r
4729         ics_update_width(new_width);\r
4730         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4731     }\r
4732 }\r
4733 \r
4734 VOID\r
4735 ChangedConsoleFont()\r
4736 {\r
4737   CHARFORMAT cfmt;\r
4738   CHARRANGE tmpsel, sel;\r
4739   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4740   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4741   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4742   PARAFORMAT paraf;\r
4743 \r
4744   cfmt.cbSize = sizeof(CHARFORMAT);\r
4745   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4746     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4747                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4748   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4749    * size.  This was undocumented in the version of MSVC++ that I had\r
4750    * when I wrote the code, but is apparently documented now.\r
4751    */\r
4752   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4753   cfmt.bCharSet = f->lf.lfCharSet;\r
4754   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4755   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4756   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4757   /* Why are the following seemingly needed too? */\r
4758   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4759   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4760   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4761   tmpsel.cpMin = 0;\r
4762   tmpsel.cpMax = -1; /*999999?*/\r
4763   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4764   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4765   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4766    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4767    */\r
4768   paraf.cbSize = sizeof(paraf);\r
4769   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4770   paraf.dxStartIndent = 0;\r
4771   paraf.dxOffset = WRAP_INDENT;\r
4772   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4773   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4774   UpdateICSWidth(hText);\r
4775 }\r
4776 \r
4777 /*---------------------------------------------------------------------------*\\r
4778  *\r
4779  * Window Proc for main window\r
4780  *\r
4781 \*---------------------------------------------------------------------------*/\r
4782 \r
4783 /* Process messages for main window, etc. */\r
4784 LRESULT CALLBACK\r
4785 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4786 {\r
4787   FARPROC lpProc;\r
4788   int wmId;\r
4789   char *defName;\r
4790   FILE *f;\r
4791   UINT number;\r
4792   char fileTitle[MSG_SIZ];\r
4793   static SnapData sd;\r
4794   static int peek=0;\r
4795 \r
4796   switch (message) {\r
4797 \r
4798   case WM_PAINT: /* message: repaint portion of window */\r
4799     PaintProc(hwnd);\r
4800     break;\r
4801 \r
4802   case WM_ERASEBKGND:\r
4803     if (IsIconic(hwnd)) {\r
4804       /* Cheat; change the message */\r
4805       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4806     } else {\r
4807       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4808     }\r
4809     break;\r
4810 \r
4811   case WM_LBUTTONDOWN:\r
4812   case WM_MBUTTONDOWN:\r
4813   case WM_RBUTTONDOWN:\r
4814   case WM_LBUTTONUP:\r
4815   case WM_MBUTTONUP:\r
4816   case WM_RBUTTONUP:\r
4817   case WM_MOUSEMOVE:\r
4818   case WM_MOUSEWHEEL:\r
4819     MouseEvent(hwnd, message, wParam, lParam);\r
4820     break;\r
4821 \r
4822   case WM_KEYUP:\r
4823     if((char)wParam == '\b') {\r
4824       ForwardEvent(); peek = 0;\r
4825     }\r
4826 \r
4827     JAWS_KBUP_NAVIGATION\r
4828 \r
4829     break;\r
4830 \r
4831   case WM_KEYDOWN:\r
4832     if((char)wParam == '\b') {\r
4833       if(!peek) BackwardEvent(), peek = 1;\r
4834     }\r
4835 \r
4836     JAWS_KBDOWN_NAVIGATION\r
4837 \r
4838     break;\r
4839 \r
4840   case WM_CHAR:\r
4841     \r
4842     JAWS_ALT_INTERCEPT\r
4843 \r
4844     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4845         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4846         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4847         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4848         SetFocus(h);\r
4849         SendMessage(h, message, wParam, lParam);\r
4850     } else if(lParam != KF_REPEAT) {\r
4851         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4852                 TypeInEvent((char)wParam);\r
4853         } else if((char)wParam == 003) CopyGameToClipboard();\r
4854          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4855     }\r
4856 \r
4857     break;\r
4858 \r
4859   case WM_PALETTECHANGED:\r
4860     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4861       int nnew;\r
4862       HDC hdc = GetDC(hwndMain);\r
4863       SelectPalette(hdc, hPal, TRUE);\r
4864       nnew = RealizePalette(hdc);\r
4865       if (nnew > 0) {\r
4866         paletteChanged = TRUE;\r
4867 \r
4868         InvalidateRect(hwnd, &boardRect, FALSE);\r
4869       }\r
4870       ReleaseDC(hwnd, hdc);\r
4871     }\r
4872     break;\r
4873 \r
4874   case WM_QUERYNEWPALETTE:\r
4875     if (!appData.monoMode /*&& paletteChanged*/) {\r
4876       int nnew;\r
4877       HDC hdc = GetDC(hwndMain);\r
4878       paletteChanged = FALSE;\r
4879       SelectPalette(hdc, hPal, FALSE);\r
4880       nnew = RealizePalette(hdc);\r
4881       if (nnew > 0) {\r
4882         InvalidateRect(hwnd, &boardRect, FALSE);\r
4883       }\r
4884       ReleaseDC(hwnd, hdc);\r
4885       return TRUE;\r
4886     }\r
4887     return FALSE;\r
4888 \r
4889   case WM_COMMAND: /* message: command from application menu */\r
4890     wmId    = LOWORD(wParam);\r
4891 \r
4892     switch (wmId) {\r
4893     case IDM_NewGame:\r
4894       ResetGameEvent();\r
4895       SAY("new game enter a move to play against the computer with white");\r
4896       break;\r
4897 \r
4898     case IDM_NewGameFRC:\r
4899       if( NewGameFRC() == 0 ) {\r
4900         ResetGameEvent();\r
4901       }\r
4902       break;\r
4903 \r
4904     case IDM_NewVariant:\r
4905       NewVariantPopup(hwnd);\r
4906       break;\r
4907 \r
4908     case IDM_LoadGame:\r
4909       LoadGameDialog(hwnd, _("Load Game from File"));\r
4910       break;\r
4911 \r
4912     case IDM_LoadNextGame:\r
4913       ReloadGame(1);\r
4914       break;\r
4915 \r
4916     case IDM_LoadPrevGame:\r
4917       ReloadGame(-1);\r
4918       break;\r
4919 \r
4920     case IDM_ReloadGame:\r
4921       ReloadGame(0);\r
4922       break;\r
4923 \r
4924     case IDM_LoadPosition:\r
4925       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4926         Reset(FALSE, TRUE);\r
4927       }\r
4928       number = 1;\r
4929       f = OpenFileDialog(hwnd, "rb", "",\r
4930                          appData.oldSaveStyle ? "pos" : "fen",\r
4931                          POSITION_FILT,\r
4932                          _("Load Position from File"), &number, fileTitle, NULL);\r
4933       if (f != NULL) {\r
4934         LoadPosition(f, number, fileTitle);\r
4935       }\r
4936       break;\r
4937 \r
4938     case IDM_LoadNextPosition:\r
4939       ReloadPosition(1);\r
4940       break;\r
4941 \r
4942     case IDM_LoadPrevPosition:\r
4943       ReloadPosition(-1);\r
4944       break;\r
4945 \r
4946     case IDM_ReloadPosition:\r
4947       ReloadPosition(0);\r
4948       break;\r
4949 \r
4950     case IDM_SaveGame:\r
4951       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4952       f = OpenFileDialog(hwnd, "a", defName,\r
4953                          appData.oldSaveStyle ? "gam" : "pgn",\r
4954                          GAME_FILT,\r
4955                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4956       if (f != NULL) {\r
4957         SaveGame(f, 0, "");\r
4958       }\r
4959       break;\r
4960 \r
4961     case IDM_SavePosition:\r
4962       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4963       f = OpenFileDialog(hwnd, "a", defName,\r
4964                          appData.oldSaveStyle ? "pos" : "fen",\r
4965                          POSITION_FILT,\r
4966                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4967       if (f != NULL) {\r
4968         SavePosition(f, 0, "");\r
4969       }\r
4970       break;\r
4971 \r
4972     case IDM_SaveDiagram:\r
4973       defName = "diagram";\r
4974       f = OpenFileDialog(hwnd, "wb", defName,\r
4975                          "bmp",\r
4976                          DIAGRAM_FILT,\r
4977                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4978       if (f != NULL) {\r
4979         SaveDiagram(f);\r
4980       }\r
4981       break;\r
4982 \r
4983     case IDM_SaveSelected:\r
4984       f = OpenFileDialog(hwnd, "a", "",\r
4985                          "pgn",\r
4986                          GAME_FILT,\r
4987                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4988       if (f != NULL) {\r
4989         SaveSelected(f, 0, "");\r
4990       }\r
4991       break;\r
4992 \r
4993     case IDM_CreateBook:\r
4994       CreateBookEvent();\r
4995       break;\r
4996 \r
4997     case IDM_CopyGame:\r
4998       CopyGameToClipboard();\r
4999       break;\r
5000 \r
5001     case IDM_PasteGame:\r
5002       PasteGameFromClipboard();\r
5003       break;\r
5004 \r
5005     case IDM_CopyGameListToClipboard:\r
5006       CopyGameListToClipboard();\r
5007       break;\r
5008 \r
5009     /* [AS] Autodetect FEN or PGN data */\r
5010     case IDM_PasteAny:\r
5011       PasteGameOrFENFromClipboard();\r
5012       break;\r
5013 \r
5014     /* [AS] Move history */\r
5015     case IDM_ShowMoveHistory:\r
5016         if( MoveHistoryIsUp() ) {\r
5017             MoveHistoryPopDown();\r
5018         }\r
5019         else {\r
5020             MoveHistoryPopUp();\r
5021         }\r
5022         break;\r
5023 \r
5024     /* [AS] Eval graph */\r
5025     case IDM_ShowEvalGraph:\r
5026         if( EvalGraphIsUp() ) {\r
5027             EvalGraphPopDown();\r
5028         }\r
5029         else {\r
5030             EvalGraphPopUp();\r
5031             SetFocus(hwndMain);\r
5032         }\r
5033         break;\r
5034 \r
5035     /* [AS] Engine output */\r
5036     case IDM_ShowEngineOutput:\r
5037         if( EngineOutputIsUp() ) {\r
5038             EngineOutputPopDown();\r
5039         }\r
5040         else {\r
5041             EngineOutputPopUp();\r
5042         }\r
5043         break;\r
5044 \r
5045     /* [AS] User adjudication */\r
5046     case IDM_UserAdjudication_White:\r
5047         UserAdjudicationEvent( +1 );\r
5048         break;\r
5049 \r
5050     case IDM_UserAdjudication_Black:\r
5051         UserAdjudicationEvent( -1 );\r
5052         break;\r
5053 \r
5054     case IDM_UserAdjudication_Draw:\r
5055         UserAdjudicationEvent( 0 );\r
5056         break;\r
5057 \r
5058     /* [AS] Game list options dialog */\r
5059     case IDM_GameListOptions:\r
5060       GameListOptions();\r
5061       break;\r
5062 \r
5063     case IDM_NewChat:\r
5064       ChatPopUp(NULL);\r
5065       break;\r
5066 \r
5067     case IDM_CopyPosition:\r
5068       CopyFENToClipboard();\r
5069       break;\r
5070 \r
5071     case IDM_PastePosition:\r
5072       PasteFENFromClipboard();\r
5073       break;\r
5074 \r
5075     case IDM_MailMove:\r
5076       MailMoveEvent();\r
5077       break;\r
5078 \r
5079     case IDM_ReloadCMailMsg:\r
5080       Reset(TRUE, TRUE);\r
5081       ReloadCmailMsgEvent(FALSE);\r
5082       break;\r
5083 \r
5084     case IDM_Minimize:\r
5085       ShowWindow(hwnd, SW_MINIMIZE);\r
5086       break;\r
5087 \r
5088     case IDM_Exit:\r
5089       ExitEvent(0);\r
5090       break;\r
5091 \r
5092     case IDM_MachineWhite:\r
5093       MachineWhiteEvent();\r
5094       /*\r
5095        * refresh the tags dialog only if it's visible\r
5096        */\r
5097       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5098           char *tags;\r
5099           tags = PGNTags(&gameInfo);\r
5100           TagsPopUp(tags, CmailMsg());\r
5101           free(tags);\r
5102       }\r
5103       SAY("computer starts playing white");\r
5104       break;\r
5105 \r
5106     case IDM_MachineBlack:\r
5107       MachineBlackEvent();\r
5108       /*\r
5109        * refresh the tags dialog only if it's visible\r
5110        */\r
5111       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5112           char *tags;\r
5113           tags = PGNTags(&gameInfo);\r
5114           TagsPopUp(tags, CmailMsg());\r
5115           free(tags);\r
5116       }\r
5117       SAY("computer starts playing black");\r
5118       break;\r
5119 \r
5120     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5121       if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
5122       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5123       break;\r
5124 \r
5125     case IDM_TwoMachines:\r
5126       TwoMachinesEvent();\r
5127       /*\r
5128 \r
5129        * refresh the tags dialog only if it's visible\r
5130        */\r
5131       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5132           char *tags;\r
5133           tags = PGNTags(&gameInfo);\r
5134           TagsPopUp(tags, CmailMsg());\r
5135           free(tags);\r
5136       }\r
5137       SAY("computer starts playing both sides");\r
5138       break;\r
5139 \r
5140     case IDM_AnalysisMode:\r
5141       if(AnalyzeModeEvent()) {\r
5142         SAY("analyzing current position");\r
5143       }\r
5144       break;\r
5145 \r
5146     case IDM_AnalyzeFile:\r
5147       AnalyzeFileEvent();\r
5148       break;\r
5149 \r
5150     case IDM_IcsClient:\r
5151       IcsClientEvent();\r
5152       break;\r
5153 \r
5154     case IDM_EditGame:\r
5155     case IDM_EditGame2:\r
5156       EditGameEvent();\r
5157       SAY("edit game");\r
5158       break;\r
5159 \r
5160     case IDM_EditPosition:\r
5161     case IDM_EditPosition2:\r
5162       EditPositionEvent();\r
5163       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5164       break;\r
5165 \r
5166     case IDM_Training:\r
5167       TrainingEvent();\r
5168       break;\r
5169 \r
5170     case IDM_ShowGameList:\r
5171       ShowGameListProc();\r
5172       break;\r
5173 \r
5174     case IDM_EditProgs1:\r
5175       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5176       break;\r
5177 \r
5178     case IDM_LoadProg1:\r
5179      LoadEnginePopUp(hwndMain, 0);\r
5180       break;\r
5181 \r
5182     case IDM_LoadProg2:\r
5183      LoadEnginePopUp(hwndMain, 1);\r
5184       break;\r
5185 \r
5186     case IDM_EditServers:\r
5187       EditTagsPopUp(icsNames, &icsNames);\r
5188       break;\r
5189 \r
5190     case IDM_EditTags:\r
5191     case IDM_Tags:\r
5192       EditTagsProc();\r
5193       break;\r
5194 \r
5195     case IDM_EditBook:\r
5196       EditBookEvent();\r
5197       break;\r
5198 \r
5199     case IDM_EditComment:\r
5200     case IDM_Comment:\r
5201       if (commentUp && editComment) {\r
5202         CommentPopDown();\r
5203       } else {\r
5204         EditCommentEvent();\r
5205       }\r
5206       break;\r
5207 \r
5208     case IDM_Pause:\r
5209       PauseEvent();\r
5210       break;\r
5211 \r
5212     case IDM_Accept:\r
5213       AcceptEvent();\r
5214       break;\r
5215 \r
5216     case IDM_Decline:\r
5217       DeclineEvent();\r
5218       break;\r
5219 \r
5220     case IDM_Rematch:\r
5221 \r
5222       RematchEvent();\r
5223       break;\r
5224 \r
5225     case IDM_CallFlag:\r
5226       CallFlagEvent();\r
5227       break;\r
5228 \r
5229     case IDM_Draw:\r
5230       DrawEvent();\r
5231       break;\r
5232 \r
5233     case IDM_Adjourn:\r
5234       AdjournEvent();\r
5235       break;\r
5236 \r
5237     case IDM_Abort:\r
5238       AbortEvent();\r
5239       break;\r
5240 \r
5241     case IDM_Resign:\r
5242       ResignEvent();\r
5243       break;\r
5244 \r
5245     case IDM_StopObserving:\r
5246       StopObservingEvent();\r
5247       break;\r
5248 \r
5249     case IDM_StopExamining:\r
5250       StopExaminingEvent();\r
5251       break;\r
5252 \r
5253     case IDM_Upload:\r
5254       UploadGameEvent();\r
5255       break;\r
5256 \r
5257     case IDM_TypeInMove:\r
5258       TypeInEvent('\000');\r
5259       break;\r
5260 \r
5261     case IDM_TypeInName:\r
5262       PopUpNameDialog('\000');\r
5263       break;\r
5264 \r
5265     case IDM_Backward:\r
5266       BackwardEvent();\r
5267       SetFocus(hwndMain);\r
5268       break;\r
5269 \r
5270     JAWS_MENU_ITEMS\r
5271 \r
5272     case IDM_Forward:\r
5273       ForwardEvent();\r
5274       SetFocus(hwndMain);\r
5275       break;\r
5276 \r
5277     case IDM_ToStart:\r
5278       ToStartEvent();\r
5279       SetFocus(hwndMain);\r
5280       break;\r
5281 \r
5282     case IDM_ToEnd:\r
5283       ToEndEvent();\r
5284       SetFocus(hwndMain);\r
5285       break;\r
5286 \r
5287     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5288     case OPT_GameListPrev:\r
5289       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5290       break;\r
5291 \r
5292     case IDM_Revert:\r
5293       RevertEvent(FALSE);\r
5294       break;\r
5295 \r
5296     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5297       RevertEvent(TRUE);\r
5298       break;\r
5299 \r
5300     case IDM_TruncateGame:\r
5301       TruncateGameEvent();\r
5302       break;\r
5303 \r
5304     case IDM_MoveNow:\r
5305       MoveNowEvent();\r
5306       break;\r
5307 \r
5308     case IDM_RetractMove:\r
5309       RetractMoveEvent();\r
5310       break;\r
5311 \r
5312     case IDM_FlipView:\r
5313       flipView = !flipView;\r
5314       DrawPosition(FALSE, NULL);\r
5315       break;\r
5316 \r
5317     case IDM_FlipClock:\r
5318       flipClock = !flipClock;\r
5319       DisplayBothClocks();\r
5320       DisplayLogos();\r
5321       break;\r
5322 \r
5323     case IDM_MuteSounds:\r
5324       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5325       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5326                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5327       break;\r
5328 \r
5329     case IDM_GeneralOptions:\r
5330       GeneralOptionsPopup(hwnd);\r
5331       DrawPosition(TRUE, NULL);\r
5332       break;\r
5333 \r
5334     case IDM_BoardOptions:\r
5335       BoardOptionsPopup(hwnd);\r
5336       break;\r
5337 \r
5338     case IDM_ThemeOptions:\r
5339       ThemeOptionsPopup(hwnd);\r
5340       break;\r
5341 \r
5342     case IDM_EnginePlayOptions:\r
5343       EnginePlayOptionsPopup(hwnd);\r
5344       break;\r
5345 \r
5346     case IDM_Engine1Options:\r
5347       EngineOptionsPopup(hwnd, &first);\r
5348       break;\r
5349 \r
5350     case IDM_Engine2Options:\r
5351       savedHwnd = hwnd;\r
5352       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5353       EngineOptionsPopup(hwnd, &second);\r
5354       break;\r
5355 \r
5356     case IDM_OptionsUCI:\r
5357       UciOptionsPopup(hwnd);\r
5358       break;\r
5359 \r
5360     case IDM_Tourney:\r
5361       TourneyPopup(hwnd);\r
5362       break;\r
5363 \r
5364     case IDM_IcsOptions:\r
5365       IcsOptionsPopup(hwnd);\r
5366       break;\r
5367 \r
5368     case IDM_Fonts:\r
5369       FontsOptionsPopup(hwnd);\r
5370       break;\r
5371 \r
5372     case IDM_Sounds:\r
5373       SoundOptionsPopup(hwnd);\r
5374       break;\r
5375 \r
5376     case IDM_CommPort:\r
5377       CommPortOptionsPopup(hwnd);\r
5378       break;\r
5379 \r
5380     case IDM_LoadOptions:\r
5381       LoadOptionsPopup(hwnd);\r
5382       break;\r
5383 \r
5384     case IDM_SaveOptions:\r
5385       SaveOptionsPopup(hwnd);\r
5386       break;\r
5387 \r
5388     case IDM_TimeControl:\r
5389       TimeControlOptionsPopup(hwnd);\r
5390       break;\r
5391 \r
5392     case IDM_SaveSettings:\r
5393       SaveSettings(settingsFileName);\r
5394       break;\r
5395 \r
5396     case IDM_SaveSettingsOnExit:\r
5397       saveSettingsOnExit = !saveSettingsOnExit;\r
5398       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5399                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5400                                          MF_CHECKED : MF_UNCHECKED));\r
5401       break;\r
5402 \r
5403     case IDM_Hint:\r
5404       HintEvent();\r
5405       break;\r
5406 \r
5407     case IDM_Book:\r
5408       BookEvent();\r
5409       break;\r
5410 \r
5411     case IDM_AboutGame:\r
5412       AboutGameEvent();\r
5413       break;\r
5414 \r
5415     case IDM_Debug:\r
5416       appData.debugMode = !appData.debugMode;\r
5417       if (appData.debugMode) {\r
5418         char dir[MSG_SIZ];\r
5419         GetCurrentDirectory(MSG_SIZ, dir);\r
5420         SetCurrentDirectory(installDir);\r
5421         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5422         SetCurrentDirectory(dir);\r
5423         setbuf(debugFP, NULL);\r
5424       } else {\r
5425         fclose(debugFP);\r
5426         debugFP = NULL;\r
5427       }\r
5428       break;\r
5429 \r
5430     case IDM_HELPCONTENTS:\r
5431       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5432           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5433           MessageBox (GetFocus(),\r
5434                     _("Unable to activate help"),\r
5435                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5436       }\r
5437       break;\r
5438 \r
5439     case IDM_HELPSEARCH:\r
5440         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5441             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5442         MessageBox (GetFocus(),\r
5443                     _("Unable to activate help"),\r
5444                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5445       }\r
5446       break;\r
5447 \r
5448     case IDM_HELPHELP:\r
5449       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5450         MessageBox (GetFocus(),\r
5451                     _("Unable to activate help"),\r
5452                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5453       }\r
5454       break;\r
5455 \r
5456     case IDM_ABOUT:\r
5457       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5458       DialogBox(hInst, \r
5459         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5460         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5461       FreeProcInstance(lpProc);\r
5462       break;\r
5463 \r
5464     case IDM_DirectCommand1:\r
5465       AskQuestionEvent(_("Direct Command"),\r
5466                        _("Send to chess program:"), "", "1");\r
5467       break;\r
5468     case IDM_DirectCommand2:\r
5469       AskQuestionEvent(_("Direct Command"),\r
5470                        _("Send to second chess program:"), "", "2");\r
5471       break;\r
5472 \r
5473     case EP_WhitePawn:\r
5474       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5475       fromX = fromY = -1;\r
5476       break;\r
5477 \r
5478     case EP_WhiteKnight:\r
5479       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5480       fromX = fromY = -1;\r
5481       break;\r
5482 \r
5483     case EP_WhiteBishop:\r
5484       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5485       fromX = fromY = -1;\r
5486       break;\r
5487 \r
5488     case EP_WhiteRook:\r
5489       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5490       fromX = fromY = -1;\r
5491       break;\r
5492 \r
5493     case EP_WhiteQueen:\r
5494       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5495       fromX = fromY = -1;\r
5496       break;\r
5497 \r
5498     case EP_WhiteFerz:\r
5499       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5500       fromX = fromY = -1;\r
5501       break;\r
5502 \r
5503     case EP_WhiteWazir:\r
5504       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5505       fromX = fromY = -1;\r
5506       break;\r
5507 \r
5508     case EP_WhiteAlfil:\r
5509       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5510       fromX = fromY = -1;\r
5511       break;\r
5512 \r
5513     case EP_WhiteCannon:\r
5514       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5515       fromX = fromY = -1;\r
5516       break;\r
5517 \r
5518     case EP_WhiteCardinal:\r
5519       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5520       fromX = fromY = -1;\r
5521       break;\r
5522 \r
5523     case EP_WhiteMarshall:\r
5524       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5525       fromX = fromY = -1;\r
5526       break;\r
5527 \r
5528     case EP_WhiteKing:\r
5529       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5530       fromX = fromY = -1;\r
5531       break;\r
5532 \r
5533     case EP_BlackPawn:\r
5534       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5535       fromX = fromY = -1;\r
5536       break;\r
5537 \r
5538     case EP_BlackKnight:\r
5539       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5540       fromX = fromY = -1;\r
5541       break;\r
5542 \r
5543     case EP_BlackBishop:\r
5544       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5545       fromX = fromY = -1;\r
5546       break;\r
5547 \r
5548     case EP_BlackRook:\r
5549       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5550       fromX = fromY = -1;\r
5551       break;\r
5552 \r
5553     case EP_BlackQueen:\r
5554       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5555       fromX = fromY = -1;\r
5556       break;\r
5557 \r
5558     case EP_BlackFerz:\r
5559       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5560       fromX = fromY = -1;\r
5561       break;\r
5562 \r
5563     case EP_BlackWazir:\r
5564       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5565       fromX = fromY = -1;\r
5566       break;\r
5567 \r
5568     case EP_BlackAlfil:\r
5569       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5570       fromX = fromY = -1;\r
5571       break;\r
5572 \r
5573     case EP_BlackCannon:\r
5574       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5575       fromX = fromY = -1;\r
5576       break;\r
5577 \r
5578     case EP_BlackCardinal:\r
5579       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5580       fromX = fromY = -1;\r
5581       break;\r
5582 \r
5583     case EP_BlackMarshall:\r
5584       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5585       fromX = fromY = -1;\r
5586       break;\r
5587 \r
5588     case EP_BlackKing:\r
5589       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5590       fromX = fromY = -1;\r
5591       break;\r
5592 \r
5593     case EP_EmptySquare:\r
5594       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5595       fromX = fromY = -1;\r
5596       break;\r
5597 \r
5598     case EP_ClearBoard:\r
5599       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5600       fromX = fromY = -1;\r
5601       break;\r
5602 \r
5603     case EP_White:\r
5604       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5605       fromX = fromY = -1;\r
5606       break;\r
5607 \r
5608     case EP_Black:\r
5609       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5610       fromX = fromY = -1;\r
5611       break;\r
5612 \r
5613     case EP_Promote:\r
5614       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5615       fromX = fromY = -1;\r
5616       break;\r
5617 \r
5618     case EP_Demote:\r
5619       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5620       fromX = fromY = -1;\r
5621       break;\r
5622 \r
5623     case DP_Pawn:\r
5624       DropMenuEvent(WhitePawn, fromX, fromY);\r
5625       fromX = fromY = -1;\r
5626       break;\r
5627 \r
5628     case DP_Knight:\r
5629       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5630       fromX = fromY = -1;\r
5631       break;\r
5632 \r
5633     case DP_Bishop:\r
5634       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5635       fromX = fromY = -1;\r
5636       break;\r
5637 \r
5638     case DP_Rook:\r
5639       DropMenuEvent(WhiteRook, fromX, fromY);\r
5640       fromX = fromY = -1;\r
5641       break;\r
5642 \r
5643     case DP_Queen:\r
5644       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5645       fromX = fromY = -1;\r
5646       break;\r
5647 \r
5648     case IDM_English:\r
5649       barbaric = 0; appData.language = "";\r
5650       TranslateMenus(0);\r
5651       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5652       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5653       lastChecked = wmId;\r
5654       break;\r
5655 \r
5656     default:\r
5657       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5658           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5659       else\r
5660       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5661           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5662           TranslateMenus(0);\r
5663           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5664           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5665           lastChecked = wmId;\r
5666           break;\r
5667       }\r
5668       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5669     }\r
5670     break;\r
5671 \r
5672   case WM_TIMER:\r
5673     switch (wParam) {\r
5674     case CLOCK_TIMER_ID:\r
5675       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5676       clockTimerEvent = 0;\r
5677       DecrementClocks(); /* call into back end */\r
5678       break;\r
5679     case LOAD_GAME_TIMER_ID:\r
5680       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5681       loadGameTimerEvent = 0;\r
5682       AutoPlayGameLoop(); /* call into back end */\r
5683       break;\r
5684     case ANALYSIS_TIMER_ID:\r
5685       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5686                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5687         AnalysisPeriodicEvent(0);\r
5688       } else {\r
5689         KillTimer(hwnd, analysisTimerEvent);\r
5690         analysisTimerEvent = 0;\r
5691       }\r
5692       break;\r
5693     case DELAYED_TIMER_ID:\r
5694       KillTimer(hwnd, delayedTimerEvent);\r
5695       delayedTimerEvent = 0;\r
5696       delayedTimerCallback();\r
5697       break;\r
5698     }\r
5699     break;\r
5700 \r
5701   case WM_USER_Input:\r
5702     InputEvent(hwnd, message, wParam, lParam);\r
5703     break;\r
5704 \r
5705   /* [AS] Also move "attached" child windows */\r
5706   case WM_WINDOWPOSCHANGING:\r
5707 \r
5708     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5709         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5710 \r
5711         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5712             /* Window is moving */\r
5713             RECT rcMain;\r
5714 \r
5715 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5716             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5717             rcMain.right  = wpMain.x + wpMain.width;\r
5718             rcMain.top    = wpMain.y;\r
5719             rcMain.bottom = wpMain.y + wpMain.height;\r
5720             \r
5721             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5722             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5723             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5724             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5725             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5726             wpMain.x = lpwp->x;\r
5727             wpMain.y = lpwp->y;\r
5728 \r
5729         }\r
5730     }\r
5731     break;\r
5732 \r
5733   /* [AS] Snapping */\r
5734   case WM_ENTERSIZEMOVE:\r
5735     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5736     if (hwnd == hwndMain) {\r
5737       doingSizing = TRUE;\r
5738       lastSizing = 0;\r
5739     }\r
5740     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5741     break;\r
5742 \r
5743   case WM_SIZING:\r
5744     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5745     if (hwnd == hwndMain) {\r
5746       lastSizing = wParam;\r
5747     }\r
5748     break;\r
5749 \r
5750   case WM_MOVING:\r
5751     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5752       return OnMoving( &sd, hwnd, wParam, lParam );\r
5753 \r
5754   case WM_EXITSIZEMOVE:\r
5755     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5756     if (hwnd == hwndMain) {\r
5757       RECT client;\r
5758       doingSizing = FALSE;\r
5759       InvalidateRect(hwnd, &boardRect, FALSE);\r
5760       GetClientRect(hwnd, &client);\r
5761       ResizeBoard(client.right, client.bottom, lastSizing);\r
5762       lastSizing = 0;\r
5763       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5764     }\r
5765     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5766     break;\r
5767 \r
5768   case WM_DESTROY: /* message: window being destroyed */\r
5769     PostQuitMessage(0);\r
5770     break;\r
5771 \r
5772   case WM_CLOSE:\r
5773     if (hwnd == hwndMain) {\r
5774       ExitEvent(0);\r
5775     }\r
5776     break;\r
5777 \r
5778   default:      /* Passes it on if unprocessed */\r
5779     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5780   }\r
5781 \r
5782 \r
5783   return 0;\r
5784 }\r
5785 \r
5786 /*---------------------------------------------------------------------------*\\r
5787  *\r
5788  * Misc utility routines\r
5789  *\r
5790 \*---------------------------------------------------------------------------*/\r
5791 \r
5792 /*\r
5793  * Decent random number generator, at least not as bad as Windows\r
5794  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5795  */\r
5796 unsigned int randstate;\r
5797 \r
5798 int\r
5799 myrandom(void)\r
5800 {\r
5801   randstate = randstate * 1664525 + 1013904223;\r
5802   return (int) randstate & 0x7fffffff;\r
5803 }\r
5804 \r
5805 void\r
5806 mysrandom(unsigned int seed)\r
5807 {\r
5808   randstate = seed;\r
5809 }\r
5810 \r
5811 \r
5812 /* \r
5813  * returns TRUE if user selects a different color, FALSE otherwise \r
5814  */\r
5815 \r
5816 BOOL\r
5817 ChangeColor(HWND hwnd, COLORREF *which)\r
5818 {\r
5819   static BOOL firstTime = TRUE;\r
5820   static DWORD customColors[16];\r
5821   CHOOSECOLOR cc;\r
5822   COLORREF newcolor;\r
5823   int i;\r
5824   ColorClass ccl;\r
5825 \r
5826   if (firstTime) {\r
5827     /* Make initial colors in use available as custom colors */\r
5828     /* Should we put the compiled-in defaults here instead? */\r
5829     i = 0;\r
5830     customColors[i++] = lightSquareColor & 0xffffff;\r
5831     customColors[i++] = darkSquareColor & 0xffffff;\r
5832     customColors[i++] = whitePieceColor & 0xffffff;\r
5833     customColors[i++] = blackPieceColor & 0xffffff;\r
5834     customColors[i++] = highlightSquareColor & 0xffffff;\r
5835     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5836 \r
5837     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5838       customColors[i++] = textAttribs[ccl].color;\r
5839     }\r
5840     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5841     firstTime = FALSE;\r
5842   }\r
5843 \r
5844   cc.lStructSize = sizeof(cc);\r
5845   cc.hwndOwner = hwnd;\r
5846   cc.hInstance = NULL;\r
5847   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5848   cc.lpCustColors = (LPDWORD) customColors;\r
5849   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5850 \r
5851   if (!ChooseColor(&cc)) return FALSE;\r
5852 \r
5853   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5854   if (newcolor == *which) return FALSE;\r
5855   *which = newcolor;\r
5856   return TRUE;\r
5857 \r
5858   /*\r
5859   InitDrawingColors();\r
5860   InvalidateRect(hwnd, &boardRect, FALSE);\r
5861   */\r
5862 }\r
5863 \r
5864 BOOLEAN\r
5865 MyLoadSound(MySound *ms)\r
5866 {\r
5867   BOOL ok = FALSE;\r
5868   struct stat st;\r
5869   FILE *f;\r
5870 \r
5871   if (ms->data && ms->flag) free(ms->data);\r
5872   ms->data = NULL;\r
5873 \r
5874   switch (ms->name[0]) {\r
5875   case NULLCHAR:\r
5876     /* Silence */\r
5877     ok = TRUE;\r
5878     break;\r
5879   case '$':\r
5880     /* System sound from Control Panel.  Don't preload here. */\r
5881     ok = TRUE;\r
5882     break;\r
5883   case '!':\r
5884     if (ms->name[1] == NULLCHAR) {\r
5885       /* "!" alone = silence */\r
5886       ok = TRUE;\r
5887     } else {\r
5888       /* Builtin wave resource.  Error if not found. */\r
5889       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5890       if (h == NULL) break;\r
5891       ms->data = (void *)LoadResource(hInst, h);\r
5892       ms->flag = 0; // not maloced, so cannot be freed!\r
5893       if (h == NULL) break;\r
5894       ok = TRUE;\r
5895     }\r
5896     break;\r
5897   default:\r
5898     /* .wav file.  Error if not found. */\r
5899     f = fopen(ms->name, "rb");\r
5900     if (f == NULL) break;\r
5901     if (fstat(fileno(f), &st) < 0) break;\r
5902     ms->data = malloc(st.st_size);\r
5903     ms->flag = 1;\r
5904     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5905     fclose(f);\r
5906     ok = TRUE;\r
5907     break;\r
5908   }\r
5909   if (!ok) {\r
5910     char buf[MSG_SIZ];\r
5911       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5912     DisplayError(buf, GetLastError());\r
5913   }\r
5914   return ok;\r
5915 }\r
5916 \r
5917 BOOLEAN\r
5918 MyPlaySound(MySound *ms)\r
5919 {\r
5920   BOOLEAN ok = FALSE;\r
5921 \r
5922   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5923   switch (ms->name[0]) {\r
5924   case NULLCHAR:\r
5925         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5926     /* Silence */\r
5927     ok = TRUE;\r
5928     break;\r
5929   case '$':\r
5930     /* System sound from Control Panel (deprecated feature).\r
5931        "$" alone or an unset sound name gets default beep (still in use). */\r
5932     if (ms->name[1]) {\r
5933       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5934     }\r
5935     if (!ok) ok = MessageBeep(MB_OK);\r
5936     break; \r
5937   case '!':\r
5938     /* Builtin wave resource, or "!" alone for silence */\r
5939     if (ms->name[1]) {\r
5940       if (ms->data == NULL) return FALSE;\r
5941       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5942     } else {\r
5943       ok = TRUE;\r
5944     }\r
5945     break;\r
5946   default:\r
5947     /* .wav file.  Error if not found. */\r
5948     if (ms->data == NULL) return FALSE;\r
5949     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5950     break;\r
5951   }\r
5952   /* Don't print an error: this can happen innocently if the sound driver\r
5953      is busy; for instance, if another instance of WinBoard is playing\r
5954      a sound at about the same time. */\r
5955   return ok;\r
5956 }\r
5957 \r
5958 \r
5959 LRESULT CALLBACK\r
5960 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5961 {\r
5962   BOOL ok;\r
5963   OPENFILENAME *ofn;\r
5964   static UINT *number; /* gross that this is static */\r
5965 \r
5966   switch (message) {\r
5967   case WM_INITDIALOG: /* message: initialize dialog box */\r
5968     /* Center the dialog over the application window */\r
5969     ofn = (OPENFILENAME *) lParam;\r
5970     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5971       number = (UINT *) ofn->lCustData;\r
5972       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5973     } else {\r
5974       number = NULL;\r
5975     }\r
5976     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5977     Translate(hDlg, 1536);\r
5978     return FALSE;  /* Allow for further processing */\r
5979 \r
5980   case WM_COMMAND:\r
5981     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5982       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5983     }\r
5984     return FALSE;  /* Allow for further processing */\r
5985   }\r
5986   return FALSE;\r
5987 }\r
5988 \r
5989 UINT APIENTRY\r
5990 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5991 {\r
5992   static UINT *number;\r
5993   OPENFILENAME *ofname;\r
5994   OFNOTIFY *ofnot;\r
5995   switch (uiMsg) {\r
5996   case WM_INITDIALOG:\r
5997     Translate(hdlg, DLG_IndexNumber);\r
5998     ofname = (OPENFILENAME *)lParam;\r
5999     number = (UINT *)(ofname->lCustData);\r
6000     break;\r
6001   case WM_NOTIFY:\r
6002     ofnot = (OFNOTIFY *)lParam;\r
6003     if (ofnot->hdr.code == CDN_FILEOK) {\r
6004       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6005     }\r
6006     break;\r
6007   }\r
6008   return 0;\r
6009 }\r
6010 \r
6011 \r
6012 FILE *\r
6013 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6014                char *nameFilt, char *dlgTitle, UINT *number,\r
6015                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6016 {\r
6017   OPENFILENAME openFileName;\r
6018   char buf1[MSG_SIZ];\r
6019   FILE *f;\r
6020 \r
6021   if (fileName == NULL) fileName = buf1;\r
6022   if (defName == NULL) {\r
6023     safeStrCpy(fileName, "*.", 3 );\r
6024     strcat(fileName, defExt);\r
6025   } else {\r
6026     safeStrCpy(fileName, defName, MSG_SIZ );\r
6027   }\r
6028     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
6029   if (number) *number = 0;\r
6030 \r
6031   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6032   openFileName.hwndOwner         = hwnd;\r
6033   openFileName.hInstance         = (HANDLE) hInst;\r
6034   openFileName.lpstrFilter       = nameFilt;\r
6035   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6036   openFileName.nMaxCustFilter    = 0L;\r
6037   openFileName.nFilterIndex      = 1L;\r
6038   openFileName.lpstrFile         = fileName;\r
6039   openFileName.nMaxFile          = MSG_SIZ;\r
6040   openFileName.lpstrFileTitle    = fileTitle;\r
6041   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6042   openFileName.lpstrInitialDir   = NULL;\r
6043   openFileName.lpstrTitle        = dlgTitle;\r
6044   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6045     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6046     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6047     | (oldDialog ? 0 : OFN_EXPLORER);\r
6048   openFileName.nFileOffset       = 0;\r
6049   openFileName.nFileExtension    = 0;\r
6050   openFileName.lpstrDefExt       = defExt;\r
6051   openFileName.lCustData         = (LONG) number;\r
6052   openFileName.lpfnHook          = oldDialog ?\r
6053     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6054   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6055 \r
6056   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6057                         GetOpenFileName(&openFileName)) {\r
6058     /* open the file */\r
6059     f = fopen(openFileName.lpstrFile, write);\r
6060     if (f == NULL) {\r
6061       MessageBox(hwnd, _("File open failed"), NULL,\r
6062                  MB_OK|MB_ICONEXCLAMATION);\r
6063       return NULL;\r
6064     }\r
6065   } else {\r
6066     int err = CommDlgExtendedError();\r
6067     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6068     return FALSE;\r
6069   }\r
6070   return f;\r
6071 }\r
6072 \r
6073 \r
6074 \r
6075 VOID APIENTRY\r
6076 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6077 {\r
6078   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6079 \r
6080   /*\r
6081    * Get the first pop-up menu in the menu template. This is the\r
6082    * menu that TrackPopupMenu displays.\r
6083    */\r
6084   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6085   TranslateOneMenu(10, hmenuTrackPopup);\r
6086 \r
6087   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6088 \r
6089   /*\r
6090    * TrackPopup uses screen coordinates, so convert the\r
6091    * coordinates of the mouse click to screen coordinates.\r
6092    */\r
6093   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6094 \r
6095   /* Draw and track the floating pop-up menu. */\r
6096   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6097                  pt.x, pt.y, 0, hwnd, NULL);\r
6098 \r
6099   /* Destroy the menu.*/\r
6100   DestroyMenu(hmenu);\r
6101 }\r
6102    \r
6103 typedef struct {\r
6104   HWND hDlg, hText;\r
6105   int sizeX, sizeY, newSizeX, newSizeY;\r
6106   HDWP hdwp;\r
6107 } ResizeEditPlusButtonsClosure;\r
6108 \r
6109 BOOL CALLBACK\r
6110 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6111 {\r
6112   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6113   RECT rect;\r
6114   POINT pt;\r
6115 \r
6116   if (hChild == cl->hText) return TRUE;\r
6117   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6118   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6119   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6120   ScreenToClient(cl->hDlg, &pt);\r
6121   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6122     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6123   return TRUE;\r
6124 }\r
6125 \r
6126 /* Resize a dialog that has a (rich) edit field filling most of\r
6127    the top, with a row of buttons below */\r
6128 VOID\r
6129 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6130 {\r
6131   RECT rectText;\r
6132   int newTextHeight, newTextWidth;\r
6133   ResizeEditPlusButtonsClosure cl;\r
6134   \r
6135   /*if (IsIconic(hDlg)) return;*/\r
6136   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6137   \r
6138   cl.hdwp = BeginDeferWindowPos(8);\r
6139 \r
6140   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6141   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6142   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6143   if (newTextHeight < 0) {\r
6144     newSizeY += -newTextHeight;\r
6145     newTextHeight = 0;\r
6146   }\r
6147   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6148     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6149 \r
6150   cl.hDlg = hDlg;\r
6151   cl.hText = hText;\r
6152   cl.sizeX = sizeX;\r
6153   cl.sizeY = sizeY;\r
6154   cl.newSizeX = newSizeX;\r
6155   cl.newSizeY = newSizeY;\r
6156   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6157 \r
6158   EndDeferWindowPos(cl.hdwp);\r
6159 }\r
6160 \r
6161 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6162 {\r
6163     RECT    rChild, rParent;\r
6164     int     wChild, hChild, wParent, hParent;\r
6165     int     wScreen, hScreen, xNew, yNew;\r
6166     HDC     hdc;\r
6167 \r
6168     /* Get the Height and Width of the child window */\r
6169     GetWindowRect (hwndChild, &rChild);\r
6170     wChild = rChild.right - rChild.left;\r
6171     hChild = rChild.bottom - rChild.top;\r
6172 \r
6173     /* Get the Height and Width of the parent window */\r
6174     GetWindowRect (hwndParent, &rParent);\r
6175     wParent = rParent.right - rParent.left;\r
6176     hParent = rParent.bottom - rParent.top;\r
6177 \r
6178     /* Get the display limits */\r
6179     hdc = GetDC (hwndChild);\r
6180     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6181     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6182     ReleaseDC(hwndChild, hdc);\r
6183 \r
6184     /* Calculate new X position, then adjust for screen */\r
6185     xNew = rParent.left + ((wParent - wChild) /2);\r
6186     if (xNew < 0) {\r
6187         xNew = 0;\r
6188     } else if ((xNew+wChild) > wScreen) {\r
6189         xNew = wScreen - wChild;\r
6190     }\r
6191 \r
6192     /* Calculate new Y position, then adjust for screen */\r
6193     if( mode == 0 ) {\r
6194         yNew = rParent.top  + ((hParent - hChild) /2);\r
6195     }\r
6196     else {\r
6197         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6198     }\r
6199 \r
6200     if (yNew < 0) {\r
6201         yNew = 0;\r
6202     } else if ((yNew+hChild) > hScreen) {\r
6203         yNew = hScreen - hChild;\r
6204     }\r
6205 \r
6206     /* Set it, and return */\r
6207     return SetWindowPos (hwndChild, NULL,\r
6208                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6209 }\r
6210 \r
6211 /* Center one window over another */\r
6212 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6213 {\r
6214     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6215 }\r
6216 \r
6217 /*---------------------------------------------------------------------------*\\r
6218  *\r
6219  * Startup Dialog functions\r
6220  *\r
6221 \*---------------------------------------------------------------------------*/\r
6222 void\r
6223 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6224 {\r
6225   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6226 \r
6227   while (*cd != NULL) {\r
6228     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6229     cd++;\r
6230   }\r
6231 }\r
6232 \r
6233 void\r
6234 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6235 {\r
6236   char buf1[MAX_ARG_LEN];\r
6237   int len;\r
6238 \r
6239   if (str[0] == '@') {\r
6240     FILE* f = fopen(str + 1, "r");\r
6241     if (f == NULL) {\r
6242       DisplayFatalError(str + 1, errno, 2);\r
6243       return;\r
6244     }\r
6245     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6246     fclose(f);\r
6247     buf1[len] = NULLCHAR;\r
6248     str = buf1;\r
6249   }\r
6250 \r
6251   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6252 \r
6253   for (;;) {\r
6254     char buf[MSG_SIZ];\r
6255     char *end = strchr(str, '\n');\r
6256     if (end == NULL) return;\r
6257     memcpy(buf, str, end - str);\r
6258     buf[end - str] = NULLCHAR;\r
6259     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6260     str = end + 1;\r
6261   }\r
6262 }\r
6263 \r
6264 void\r
6265 SetStartupDialogEnables(HWND hDlg)\r
6266 {\r
6267   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6268     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6269     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6270   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6271     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6272   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6273     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6274   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6275     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6276   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6277     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6278     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6279     IsDlgButtonChecked(hDlg, OPT_View));\r
6280 }\r
6281 \r
6282 char *\r
6283 QuoteForFilename(char *filename)\r
6284 {\r
6285   int dquote, space;\r
6286   dquote = strchr(filename, '"') != NULL;\r
6287   space = strchr(filename, ' ') != NULL;\r
6288   if (dquote || space) {\r
6289     if (dquote) {\r
6290       return "'";\r
6291     } else {\r
6292       return "\"";\r
6293     }\r
6294   } else {\r
6295     return "";\r
6296   }\r
6297 }\r
6298 \r
6299 VOID\r
6300 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6301 {\r
6302   char buf[MSG_SIZ];\r
6303   char *q;\r
6304 \r
6305   InitComboStringsFromOption(hwndCombo, nthnames);\r
6306   q = QuoteForFilename(nthcp);\r
6307     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6308   if (*nthdir != NULLCHAR) {\r
6309     q = QuoteForFilename(nthdir);\r
6310       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6311   }\r
6312   if (*nthcp == NULLCHAR) {\r
6313     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6314   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6315     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6316     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6317   }\r
6318 }\r
6319 \r
6320 LRESULT CALLBACK\r
6321 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6322 {\r
6323   char buf[MSG_SIZ];\r
6324   HANDLE hwndCombo;\r
6325   char *p;\r
6326 \r
6327   switch (message) {\r
6328   case WM_INITDIALOG:\r
6329     /* Center the dialog */\r
6330     CenterWindow (hDlg, GetDesktopWindow());\r
6331     Translate(hDlg, DLG_Startup);\r
6332     /* Initialize the dialog items */\r
6333     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6334                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6335                   firstChessProgramNames);\r
6336     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6337                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6338                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6339     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6340     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6341       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6342     if (*appData.icsHelper != NULLCHAR) {\r
6343       char *q = QuoteForFilename(appData.icsHelper);\r
6344       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6345     }\r
6346     if (*appData.icsHost == NULLCHAR) {\r
6347       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6348       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6349     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6350       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6351       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6352     }\r
6353 \r
6354     if (appData.icsActive) {\r
6355       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6356     }\r
6357     else if (appData.noChessProgram) {\r
6358       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6359     }\r
6360     else {\r
6361       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6362     }\r
6363 \r
6364     SetStartupDialogEnables(hDlg);\r
6365     return TRUE;\r
6366 \r
6367   case WM_COMMAND:\r
6368     switch (LOWORD(wParam)) {\r
6369     case IDOK:\r
6370       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6371         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6372         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6373         p = buf;\r
6374         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6375         ParseArgs(StringGet, &p);\r
6376         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6377         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6378         p = buf;\r
6379         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6380         ParseArgs(StringGet, &p);\r
6381         SwapEngines(singleList); // ... and then make it 'second'\r
6382 \r
6383         appData.noChessProgram = FALSE;\r
6384         appData.icsActive = FALSE;\r
6385       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6386         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6387         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6388         p = buf;\r
6389         ParseArgs(StringGet, &p);\r
6390         if (appData.zippyPlay) {\r
6391           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6392           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6393           p = buf;\r
6394           ParseArgs(StringGet, &p);\r
6395         }\r
6396       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6397         appData.noChessProgram = TRUE;\r
6398         appData.icsActive = FALSE;\r
6399       } else {\r
6400         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6401                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6402         return TRUE;\r
6403       }\r
6404       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6405         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6406         p = buf;\r
6407         ParseArgs(StringGet, &p);\r
6408       }\r
6409       EndDialog(hDlg, TRUE);\r
6410       return TRUE;\r
6411 \r
6412     case IDCANCEL:\r
6413       ExitEvent(0);\r
6414       return TRUE;\r
6415 \r
6416     case IDM_HELPCONTENTS:\r
6417       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6418         MessageBox (GetFocus(),\r
6419                     _("Unable to activate help"),\r
6420                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6421       }\r
6422       break;\r
6423 \r
6424     default:\r
6425       SetStartupDialogEnables(hDlg);\r
6426       break;\r
6427     }\r
6428     break;\r
6429   }\r
6430   return FALSE;\r
6431 }\r
6432 \r
6433 /*---------------------------------------------------------------------------*\\r
6434  *\r
6435  * About box dialog functions\r
6436  *\r
6437 \*---------------------------------------------------------------------------*/\r
6438 \r
6439 /* Process messages for "About" dialog box */\r
6440 LRESULT CALLBACK\r
6441 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6442 {\r
6443   switch (message) {\r
6444   case WM_INITDIALOG: /* message: initialize dialog box */\r
6445     /* Center the dialog over the application window */\r
6446     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6447     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6448     Translate(hDlg, ABOUTBOX);\r
6449     JAWS_COPYRIGHT\r
6450     return (TRUE);\r
6451 \r
6452   case WM_COMMAND: /* message: received a command */\r
6453     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6454         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6455       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6456       return (TRUE);\r
6457     }\r
6458     break;\r
6459   }\r
6460   return (FALSE);\r
6461 }\r
6462 \r
6463 /*---------------------------------------------------------------------------*\\r
6464  *\r
6465  * Comment Dialog functions\r
6466  *\r
6467 \*---------------------------------------------------------------------------*/\r
6468 \r
6469 LRESULT CALLBACK\r
6470 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6471 {\r
6472   static HANDLE hwndText = NULL;\r
6473   int len, newSizeX, newSizeY;\r
6474   static int sizeX, sizeY;\r
6475   char *str;\r
6476   RECT rect;\r
6477   MINMAXINFO *mmi;\r
6478 \r
6479   switch (message) {\r
6480   case WM_INITDIALOG: /* message: initialize dialog box */\r
6481     /* Initialize the dialog items */\r
6482     Translate(hDlg, DLG_EditComment);\r
6483     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6484     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6485     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6486     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6487     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6488     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6489     SetWindowText(hDlg, commentTitle);\r
6490     if (editComment) {\r
6491       SetFocus(hwndText);\r
6492     } else {\r
6493       SetFocus(GetDlgItem(hDlg, IDOK));\r
6494     }\r
6495     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6496                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6497                 MAKELPARAM(FALSE, 0));\r
6498     /* Size and position the dialog */\r
6499     if (!commentDialog) {\r
6500       commentDialog = hDlg;\r
6501       GetClientRect(hDlg, &rect);\r
6502       sizeX = rect.right;\r
6503       sizeY = rect.bottom;\r
6504       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6505           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6506         WINDOWPLACEMENT wp;\r
6507         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6508         wp.length = sizeof(WINDOWPLACEMENT);\r
6509         wp.flags = 0;\r
6510         wp.showCmd = SW_SHOW;\r
6511         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6512         wp.rcNormalPosition.left = wpComment.x;\r
6513         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6514         wp.rcNormalPosition.top = wpComment.y;\r
6515         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6516         SetWindowPlacement(hDlg, &wp);\r
6517 \r
6518         GetClientRect(hDlg, &rect);\r
6519         newSizeX = rect.right;\r
6520         newSizeY = rect.bottom;\r
6521         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6522                               newSizeX, newSizeY);\r
6523         sizeX = newSizeX;\r
6524         sizeY = newSizeY;\r
6525       }\r
6526     }\r
6527     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6528     return FALSE;\r
6529 \r
6530   case WM_COMMAND: /* message: received a command */\r
6531     switch (LOWORD(wParam)) {\r
6532     case IDOK:\r
6533       if (editComment) {\r
6534         char *p, *q;\r
6535         /* Read changed options from the dialog box */\r
6536         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6537         len = GetWindowTextLength(hwndText);\r
6538         str = (char *) malloc(len + 1);\r
6539         GetWindowText(hwndText, str, len + 1);\r
6540         p = q = str;\r
6541         while (*q) {\r
6542           if (*q == '\r')\r
6543             q++;\r
6544           else\r
6545             *p++ = *q++;\r
6546         }\r
6547         *p = NULLCHAR;\r
6548         ReplaceComment(commentIndex, str);\r
6549         free(str);\r
6550       }\r
6551       CommentPopDown();\r
6552       return TRUE;\r
6553 \r
6554     case IDCANCEL:\r
6555     case OPT_CancelComment:\r
6556       CommentPopDown();\r
6557       return TRUE;\r
6558 \r
6559     case OPT_ClearComment:\r
6560       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6561       break;\r
6562 \r
6563     case OPT_EditComment:\r
6564       EditCommentEvent();\r
6565       return TRUE;\r
6566 \r
6567     default:\r
6568       break;\r
6569     }\r
6570     break;\r
6571 \r
6572   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6573         if( wParam == OPT_CommentText ) {\r
6574             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6575 \r
6576             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6577                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6578                 POINTL pt;\r
6579                 LRESULT index;\r
6580 \r
6581                 pt.x = LOWORD( lpMF->lParam );\r
6582                 pt.y = HIWORD( lpMF->lParam );\r
6583 \r
6584                 if(lpMF->msg == WM_CHAR) {\r
6585                         CHARRANGE sel;\r
6586                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6587                         index = sel.cpMin;\r
6588                 } else\r
6589                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6590 \r
6591                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6592                 len = GetWindowTextLength(hwndText);\r
6593                 str = (char *) malloc(len + 1);\r
6594                 GetWindowText(hwndText, str, len + 1);\r
6595                 ReplaceComment(commentIndex, str);\r
6596                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6597                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6598                 free(str);\r
6599 \r
6600                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6601                 lpMF->msg = WM_USER;\r
6602 \r
6603                 return TRUE;\r
6604             }\r
6605         }\r
6606         break;\r
6607 \r
6608   case WM_SIZE:\r
6609     newSizeX = LOWORD(lParam);\r
6610     newSizeY = HIWORD(lParam);\r
6611     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6612     sizeX = newSizeX;\r
6613     sizeY = newSizeY;\r
6614     break;\r
6615 \r
6616   case WM_GETMINMAXINFO:\r
6617     /* Prevent resizing window too small */\r
6618     mmi = (MINMAXINFO *) lParam;\r
6619     mmi->ptMinTrackSize.x = 100;\r
6620     mmi->ptMinTrackSize.y = 100;\r
6621     break;\r
6622   }\r
6623   return FALSE;\r
6624 }\r
6625 \r
6626 VOID\r
6627 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6628 {\r
6629   FARPROC lpProc;\r
6630   char *p, *q;\r
6631 \r
6632   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6633 \r
6634   if (str == NULL) str = "";\r
6635   p = (char *) malloc(2 * strlen(str) + 2);\r
6636   q = p;\r
6637   while (*str) {\r
6638     if (*str == '\n') *q++ = '\r';\r
6639     *q++ = *str++;\r
6640   }\r
6641   *q = NULLCHAR;\r
6642   if (commentText != NULL) free(commentText);\r
6643 \r
6644   commentIndex = index;\r
6645   commentTitle = title;\r
6646   commentText = p;\r
6647   editComment = edit;\r
6648 \r
6649   if (commentDialog) {\r
6650     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6651     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6652   } else {\r
6653     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6654     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6655                  hwndMain, (DLGPROC)lpProc);\r
6656     FreeProcInstance(lpProc);\r
6657   }\r
6658   commentUp = TRUE;\r
6659 }\r
6660 \r
6661 \r
6662 /*---------------------------------------------------------------------------*\\r
6663  *\r
6664  * Type-in move dialog functions\r
6665  * \r
6666 \*---------------------------------------------------------------------------*/\r
6667 \r
6668 LRESULT CALLBACK\r
6669 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6670 {\r
6671   char move[MSG_SIZ];\r
6672   HWND hInput;\r
6673 \r
6674   switch (message) {\r
6675   case WM_INITDIALOG:\r
6676     move[0] = (char) lParam;\r
6677     move[1] = NULLCHAR;\r
6678     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6679     Translate(hDlg, DLG_TypeInMove);\r
6680     hInput = GetDlgItem(hDlg, OPT_Move);\r
6681     SetWindowText(hInput, move);\r
6682     SetFocus(hInput);\r
6683     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6684     return FALSE;\r
6685 \r
6686   case WM_COMMAND:\r
6687     switch (LOWORD(wParam)) {\r
6688     case IDOK:\r
6689 \r
6690       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6691       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6692       TypeInDoneEvent(move);\r
6693       EndDialog(hDlg, TRUE);\r
6694       return TRUE;\r
6695     case IDCANCEL:\r
6696       EndDialog(hDlg, FALSE);\r
6697       return TRUE;\r
6698     default:\r
6699       break;\r
6700     }\r
6701     break;\r
6702   }\r
6703   return FALSE;\r
6704 }\r
6705 \r
6706 VOID\r
6707 PopUpMoveDialog(char firstchar)\r
6708 {\r
6709     FARPROC lpProc;\r
6710 \r
6711       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6712       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6713         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6714       FreeProcInstance(lpProc);\r
6715 }\r
6716 \r
6717 /*---------------------------------------------------------------------------*\\r
6718  *\r
6719  * Type-in name dialog functions\r
6720  * \r
6721 \*---------------------------------------------------------------------------*/\r
6722 \r
6723 LRESULT CALLBACK\r
6724 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6725 {\r
6726   char move[MSG_SIZ];\r
6727   HWND hInput;\r
6728 \r
6729   switch (message) {\r
6730   case WM_INITDIALOG:\r
6731     move[0] = (char) lParam;\r
6732     move[1] = NULLCHAR;\r
6733     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6734     Translate(hDlg, DLG_TypeInName);\r
6735     hInput = GetDlgItem(hDlg, OPT_Name);\r
6736     SetWindowText(hInput, move);\r
6737     SetFocus(hInput);\r
6738     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6739     return FALSE;\r
6740 \r
6741   case WM_COMMAND:\r
6742     switch (LOWORD(wParam)) {\r
6743     case IDOK:\r
6744       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6745       appData.userName = strdup(move);\r
6746       SetUserLogo(); DisplayLogos();\r
6747       SetGameInfo();\r
6748       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6749         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6750         DisplayTitle(move);\r
6751       }\r
6752 \r
6753 \r
6754       EndDialog(hDlg, TRUE);\r
6755       return TRUE;\r
6756     case IDCANCEL:\r
6757       EndDialog(hDlg, FALSE);\r
6758       return TRUE;\r
6759     default:\r
6760       break;\r
6761     }\r
6762     break;\r
6763   }\r
6764   return FALSE;\r
6765 }\r
6766 \r
6767 VOID\r
6768 PopUpNameDialog(char firstchar)\r
6769 {\r
6770     FARPROC lpProc;\r
6771     \r
6772       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6773       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6774         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6775       FreeProcInstance(lpProc);\r
6776 }\r
6777 \r
6778 /*---------------------------------------------------------------------------*\\r
6779  *\r
6780  *  Error dialogs\r
6781  * \r
6782 \*---------------------------------------------------------------------------*/\r
6783 \r
6784 /* Nonmodal error box */\r
6785 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6786                              WPARAM wParam, LPARAM lParam);\r
6787 \r
6788 VOID\r
6789 ErrorPopUp(char *title, char *content)\r
6790 {\r
6791   FARPROC lpProc;\r
6792   char *p, *q;\r
6793   BOOLEAN modal = hwndMain == NULL;\r
6794 \r
6795   p = content;\r
6796   q = errorMessage;\r
6797   while (*p) {\r
6798     if (*p == '\n') {\r
6799       if (modal) {\r
6800         *q++ = ' ';\r
6801         p++;\r
6802       } else {\r
6803         *q++ = '\r';\r
6804         *q++ = *p++;\r
6805       }\r
6806     } else {\r
6807       *q++ = *p++;\r
6808     }\r
6809   }\r
6810   *q = NULLCHAR;\r
6811   strncpy(errorTitle, title, sizeof(errorTitle));\r
6812   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6813   \r
6814   if (modal) {\r
6815     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6816   } else {\r
6817     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6818     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6819                  hwndMain, (DLGPROC)lpProc);\r
6820     FreeProcInstance(lpProc);\r
6821   }\r
6822 }\r
6823 \r
6824 VOID\r
6825 ErrorPopDown()\r
6826 {\r
6827   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6828   if (errorDialog == NULL) return;\r
6829   DestroyWindow(errorDialog);\r
6830   errorDialog = NULL;\r
6831   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6832 }\r
6833 \r
6834 LRESULT CALLBACK\r
6835 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6836 {\r
6837   RECT rChild;\r
6838 \r
6839   switch (message) {\r
6840   case WM_INITDIALOG:\r
6841     GetWindowRect(hDlg, &rChild);\r
6842 \r
6843     /*\r
6844     SetWindowPos(hDlg, NULL, rChild.left,\r
6845       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6846       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6847     */\r
6848 \r
6849     /* \r
6850         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6851         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6852         and it doesn't work when you resize the dialog.\r
6853         For now, just give it a default position.\r
6854     */\r
6855     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6856     Translate(hDlg, DLG_Error);\r
6857 \r
6858     errorDialog = hDlg;\r
6859     SetWindowText(hDlg, errorTitle);\r
6860     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6861     return FALSE;\r
6862 \r
6863   case WM_COMMAND:\r
6864     switch (LOWORD(wParam)) {\r
6865     case IDOK:\r
6866     case IDCANCEL:\r
6867       if (errorDialog == hDlg) errorDialog = NULL;\r
6868       DestroyWindow(hDlg);\r
6869       return TRUE;\r
6870 \r
6871     default:\r
6872       break;\r
6873     }\r
6874     break;\r
6875   }\r
6876   return FALSE;\r
6877 }\r
6878 \r
6879 #ifdef GOTHIC\r
6880 HWND gothicDialog = NULL;\r
6881 \r
6882 LRESULT CALLBACK\r
6883 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6884 {\r
6885   RECT rChild;\r
6886   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6887 \r
6888   switch (message) {\r
6889   case WM_INITDIALOG:\r
6890     GetWindowRect(hDlg, &rChild);\r
6891 \r
6892     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6893                                                              SWP_NOZORDER);\r
6894 \r
6895     /* \r
6896         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6897         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6898         and it doesn't work when you resize the dialog.\r
6899         For now, just give it a default position.\r
6900     */\r
6901     gothicDialog = hDlg;\r
6902     SetWindowText(hDlg, errorTitle);\r
6903     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6904     return FALSE;\r
6905 \r
6906   case WM_COMMAND:\r
6907     switch (LOWORD(wParam)) {\r
6908     case IDOK:\r
6909     case IDCANCEL:\r
6910       if (errorDialog == hDlg) errorDialog = NULL;\r
6911       DestroyWindow(hDlg);\r
6912       return TRUE;\r
6913 \r
6914     default:\r
6915       break;\r
6916     }\r
6917     break;\r
6918   }\r
6919   return FALSE;\r
6920 }\r
6921 \r
6922 VOID\r
6923 GothicPopUp(char *title, VariantClass variant)\r
6924 {\r
6925   FARPROC lpProc;\r
6926   static char *lastTitle;\r
6927 \r
6928   strncpy(errorTitle, title, sizeof(errorTitle));\r
6929   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6930 \r
6931   if(lastTitle != title && gothicDialog != NULL) {\r
6932     DestroyWindow(gothicDialog);\r
6933     gothicDialog = NULL;\r
6934   }\r
6935   if(variant != VariantNormal && gothicDialog == NULL) {\r
6936     title = lastTitle;\r
6937     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6938     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6939                  hwndMain, (DLGPROC)lpProc);\r
6940     FreeProcInstance(lpProc);\r
6941   }\r
6942 }\r
6943 #endif\r
6944 \r
6945 /*---------------------------------------------------------------------------*\\r
6946  *\r
6947  *  Ics Interaction console functions\r
6948  *\r
6949 \*---------------------------------------------------------------------------*/\r
6950 \r
6951 #define HISTORY_SIZE 64\r
6952 static char *history[HISTORY_SIZE];\r
6953 int histIn = 0, histP = 0;\r
6954 \r
6955 \r
6956 VOID\r
6957 SaveInHistory(char *cmd)\r
6958 {\r
6959   if (history[histIn] != NULL) {\r
6960     free(history[histIn]);\r
6961     history[histIn] = NULL;\r
6962   }\r
6963   if (*cmd == NULLCHAR) return;\r
6964   history[histIn] = StrSave(cmd);\r
6965   histIn = (histIn + 1) % HISTORY_SIZE;\r
6966   if (history[histIn] != NULL) {\r
6967     free(history[histIn]);\r
6968 \r
6969     history[histIn] = NULL;\r
6970   }\r
6971   histP = histIn;\r
6972 }\r
6973 \r
6974 char *\r
6975 PrevInHistory(char *cmd)\r
6976 {\r
6977   int newhp;\r
6978   if (histP == histIn) {\r
6979     if (history[histIn] != NULL) free(history[histIn]);\r
6980     history[histIn] = StrSave(cmd);\r
6981   }\r
6982   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6983   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6984   histP = newhp;\r
6985   return history[histP];\r
6986 }\r
6987 \r
6988 char *\r
6989 NextInHistory()\r
6990 {\r
6991   if (histP == histIn) return NULL;\r
6992   histP = (histP + 1) % HISTORY_SIZE;\r
6993   return history[histP];   \r
6994 }\r
6995 \r
6996 HMENU\r
6997 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6998 {\r
6999   HMENU hmenu, h;\r
7000   int i = 0;\r
7001   hmenu = LoadMenu(hInst, "TextMenu");\r
7002   h = GetSubMenu(hmenu, 0);\r
7003   while (e->item) {\r
7004     if (strcmp(e->item, "-") == 0) {\r
7005       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7006     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
7007       int flags = MF_STRING, j = 0;\r
7008       if (e->item[0] == '|') {\r
7009         flags |= MF_MENUBARBREAK;\r
7010         j++;\r
7011       }\r
7012       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
7013       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
7014     }\r
7015     e++;\r
7016     i++;\r
7017   } \r
7018   return hmenu;\r
7019 }\r
7020 \r
7021 WNDPROC consoleTextWindowProc;\r
7022 \r
7023 void\r
7024 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7025 {\r
7026   char buf[MSG_SIZ], name[MSG_SIZ];\r
7027   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7028   CHARRANGE sel;\r
7029 \r
7030   if (!getname) {\r
7031     SetWindowText(hInput, command);\r
7032     if (immediate) {\r
7033       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7034     } else {\r
7035       sel.cpMin = 999999;\r
7036       sel.cpMax = 999999;\r
7037       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7038       SetFocus(hInput);\r
7039     }\r
7040     return;\r
7041   }    \r
7042   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7043   if (sel.cpMin == sel.cpMax) {\r
7044     /* Expand to surrounding word */\r
7045     TEXTRANGE tr;\r
7046     do {\r
7047       tr.chrg.cpMax = sel.cpMin;\r
7048       tr.chrg.cpMin = --sel.cpMin;\r
7049       if (sel.cpMin < 0) break;\r
7050       tr.lpstrText = name;\r
7051       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7052     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7053     sel.cpMin++;\r
7054 \r
7055     do {\r
7056       tr.chrg.cpMin = sel.cpMax;\r
7057       tr.chrg.cpMax = ++sel.cpMax;\r
7058       tr.lpstrText = name;\r
7059       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7060     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7061     sel.cpMax--;\r
7062 \r
7063     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7064       MessageBeep(MB_ICONEXCLAMATION);\r
7065       return;\r
7066     }\r
7067     tr.chrg = sel;\r
7068     tr.lpstrText = name;\r
7069     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7070   } else {\r
7071     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7072       MessageBeep(MB_ICONEXCLAMATION);\r
7073       return;\r
7074     }\r
7075     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7076   }\r
7077   if (immediate) {\r
7078     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7079     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7080     SetWindowText(hInput, buf);\r
7081     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7082   } else {\r
7083     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7084       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7085     SetWindowText(hInput, buf);\r
7086     sel.cpMin = 999999;\r
7087     sel.cpMax = 999999;\r
7088     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7089     SetFocus(hInput);\r
7090   }\r
7091 }\r
7092 \r
7093 LRESULT CALLBACK \r
7094 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7095 {\r
7096   HWND hInput;\r
7097   CHARRANGE sel;\r
7098 \r
7099   switch (message) {\r
7100   case WM_KEYDOWN:\r
7101     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7102     if(wParam=='R') return 0;\r
7103     switch (wParam) {\r
7104     case VK_PRIOR:\r
7105       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7106       return 0;\r
7107     case VK_NEXT:\r
7108       sel.cpMin = 999999;\r
7109       sel.cpMax = 999999;\r
7110       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7111       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7112       return 0;\r
7113     }\r
7114     break;\r
7115   case WM_CHAR:\r
7116    if(wParam != '\022') {\r
7117     if (wParam == '\t') {\r
7118       if (GetKeyState(VK_SHIFT) < 0) {\r
7119         /* shifted */\r
7120         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7121         if (buttonDesc[0].hwnd) {\r
7122           SetFocus(buttonDesc[0].hwnd);\r
7123         } else {\r
7124           SetFocus(hwndMain);\r
7125         }\r
7126       } else {\r
7127         /* unshifted */\r
7128         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7129       }\r
7130     } else {\r
7131       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7132       JAWS_DELETE( SetFocus(hInput); )\r
7133       SendMessage(hInput, message, wParam, lParam);\r
7134     }\r
7135     return 0;\r
7136    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7137    lParam = -1;\r
7138   case WM_RBUTTONDOWN:\r
7139     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7140       /* Move selection here if it was empty */\r
7141       POINT pt;\r
7142       pt.x = LOWORD(lParam);\r
7143       pt.y = HIWORD(lParam);\r
7144       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7145       if (sel.cpMin == sel.cpMax) {\r
7146         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7147         sel.cpMax = sel.cpMin;\r
7148         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7149       }\r
7150       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7151 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7152       POINT pt;\r
7153       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7154       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7155       if (sel.cpMin == sel.cpMax) {\r
7156         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7157         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7158       }\r
7159       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7160         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7161       }\r
7162       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7163       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7164       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7165       MenuPopup(hwnd, pt, hmenu, -1);\r
7166 }\r
7167     }\r
7168     return 0;\r
7169   case WM_RBUTTONUP:\r
7170     if (GetKeyState(VK_SHIFT) & ~1) {\r
7171       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7172         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7173     }\r
7174     return 0;\r
7175   case WM_PASTE:\r
7176     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7177     SetFocus(hInput);\r
7178     return SendMessage(hInput, message, wParam, lParam);\r
7179   case WM_MBUTTONDOWN:\r
7180     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7181   case WM_COMMAND:\r
7182     switch (LOWORD(wParam)) {\r
7183     case IDM_QuickPaste:\r
7184       {\r
7185         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7186         if (sel.cpMin == sel.cpMax) {\r
7187           MessageBeep(MB_ICONEXCLAMATION);\r
7188           return 0;\r
7189         }\r
7190         SendMessage(hwnd, WM_COPY, 0, 0);\r
7191         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7192         SendMessage(hInput, WM_PASTE, 0, 0);\r
7193         SetFocus(hInput);\r
7194         return 0;\r
7195       }\r
7196     case IDM_Cut:\r
7197       SendMessage(hwnd, WM_CUT, 0, 0);\r
7198       return 0;\r
7199     case IDM_Paste:\r
7200       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7201       return 0;\r
7202     case IDM_Copy:\r
7203       SendMessage(hwnd, WM_COPY, 0, 0);\r
7204       return 0;\r
7205     default:\r
7206       {\r
7207         int i = LOWORD(wParam) - IDM_CommandX;\r
7208         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7209             icsTextMenuEntry[i].command != NULL) {\r
7210           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7211                    icsTextMenuEntry[i].getname,\r
7212                    icsTextMenuEntry[i].immediate);\r
7213           return 0;\r
7214         }\r
7215       }\r
7216       break;\r
7217     }\r
7218     break;\r
7219   }\r
7220   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7221 }\r
7222 \r
7223 WNDPROC consoleInputWindowProc;\r
7224 \r
7225 LRESULT CALLBACK\r
7226 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7227 {\r
7228   char buf[MSG_SIZ];\r
7229   char *p;\r
7230   static BOOL sendNextChar = FALSE;\r
7231   static BOOL quoteNextChar = FALSE;\r
7232   InputSource *is = consoleInputSource;\r
7233   CHARFORMAT cf;\r
7234   CHARRANGE sel;\r
7235 \r
7236   switch (message) {\r
7237   case WM_CHAR:\r
7238     if (!appData.localLineEditing || sendNextChar) {\r
7239       is->buf[0] = (CHAR) wParam;\r
7240       is->count = 1;\r
7241       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7242       sendNextChar = FALSE;\r
7243       return 0;\r
7244     }\r
7245     if (quoteNextChar) {\r
7246       buf[0] = (char) wParam;\r
7247       buf[1] = NULLCHAR;\r
7248       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7249       quoteNextChar = FALSE;\r
7250       return 0;\r
7251     }\r
7252     switch (wParam) {\r
7253     case '\r':   /* Enter key */\r
7254       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7255       if (consoleEcho) SaveInHistory(is->buf);\r
7256       is->buf[is->count++] = '\n';\r
7257       is->buf[is->count] = NULLCHAR;\r
7258       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7259       if (consoleEcho) {\r
7260         ConsoleOutput(is->buf, is->count, TRUE);\r
7261       } else if (appData.localLineEditing) {\r
7262         ConsoleOutput("\n", 1, TRUE);\r
7263       }\r
7264       /* fall thru */\r
7265     case '\033': /* Escape key */\r
7266       SetWindowText(hwnd, "");\r
7267       cf.cbSize = sizeof(CHARFORMAT);\r
7268       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7269       if (consoleEcho) {\r
7270         cf.crTextColor = textAttribs[ColorNormal].color;\r
7271       } else {\r
7272         cf.crTextColor = COLOR_ECHOOFF;\r
7273       }\r
7274       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7275       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7276       return 0;\r
7277     case '\t':   /* Tab key */\r
7278       if (GetKeyState(VK_SHIFT) < 0) {\r
7279         /* shifted */\r
7280         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7281       } else {\r
7282         /* unshifted */\r
7283         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7284         if (buttonDesc[0].hwnd) {\r
7285           SetFocus(buttonDesc[0].hwnd);\r
7286         } else {\r
7287           SetFocus(hwndMain);\r
7288         }\r
7289       }\r
7290       return 0;\r
7291     case '\023': /* Ctrl+S */\r
7292       sendNextChar = TRUE;\r
7293       return 0;\r
7294     case '\021': /* Ctrl+Q */\r
7295       quoteNextChar = TRUE;\r
7296       return 0;\r
7297     JAWS_REPLAY\r
7298     default:\r
7299       break;\r
7300     }\r
7301     break;\r
7302   case WM_KEYDOWN:\r
7303     switch (wParam) {\r
7304     case VK_UP:\r
7305       GetWindowText(hwnd, buf, MSG_SIZ);\r
7306       p = PrevInHistory(buf);\r
7307       if (p != NULL) {\r
7308         SetWindowText(hwnd, p);\r
7309         sel.cpMin = 999999;\r
7310         sel.cpMax = 999999;\r
7311         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7312         return 0;\r
7313       }\r
7314       break;\r
7315     case VK_DOWN:\r
7316       p = NextInHistory();\r
7317       if (p != NULL) {\r
7318         SetWindowText(hwnd, p);\r
7319         sel.cpMin = 999999;\r
7320         sel.cpMax = 999999;\r
7321         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7322         return 0;\r
7323       }\r
7324       break;\r
7325     case VK_HOME:\r
7326     case VK_END:\r
7327       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7328       /* fall thru */\r
7329     case VK_PRIOR:\r
7330     case VK_NEXT:\r
7331       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7332       return 0;\r
7333     }\r
7334     break;\r
7335   case WM_MBUTTONDOWN:\r
7336     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7337       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7338     break;\r
7339   case WM_RBUTTONUP:\r
7340     if (GetKeyState(VK_SHIFT) & ~1) {\r
7341       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7342         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7343     } else {\r
7344       POINT pt;\r
7345       HMENU hmenu;\r
7346       hmenu = LoadMenu(hInst, "InputMenu");\r
7347       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7348       if (sel.cpMin == sel.cpMax) {\r
7349         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7350         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7351       }\r
7352       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7353         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7354       }\r
7355       pt.x = LOWORD(lParam);\r
7356       pt.y = HIWORD(lParam);\r
7357       MenuPopup(hwnd, pt, hmenu, -1);\r
7358     }\r
7359     return 0;\r
7360   case WM_COMMAND:\r
7361     switch (LOWORD(wParam)) { \r
7362     case IDM_Undo:\r
7363       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7364       return 0;\r
7365     case IDM_SelectAll:\r
7366       sel.cpMin = 0;\r
7367       sel.cpMax = -1; /*999999?*/\r
7368       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7369       return 0;\r
7370     case IDM_Cut:\r
7371       SendMessage(hwnd, WM_CUT, 0, 0);\r
7372       return 0;\r
7373     case IDM_Paste:\r
7374       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7375       return 0;\r
7376     case IDM_Copy:\r
7377       SendMessage(hwnd, WM_COPY, 0, 0);\r
7378       return 0;\r
7379     }\r
7380     break;\r
7381   }\r
7382   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7383 }\r
7384 \r
7385 #define CO_MAX  100000\r
7386 #define CO_TRIM   1000\r
7387 \r
7388 LRESULT CALLBACK\r
7389 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7390 {\r
7391   static SnapData sd;\r
7392   HWND hText, hInput;\r
7393   RECT rect;\r
7394   static int sizeX, sizeY;\r
7395   int newSizeX, newSizeY;\r
7396   MINMAXINFO *mmi;\r
7397   WORD wMask;\r
7398 \r
7399   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7400   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7401 \r
7402   switch (message) {\r
7403   case WM_NOTIFY:\r
7404     if (((NMHDR*)lParam)->code == EN_LINK)\r
7405     {\r
7406       ENLINK *pLink = (ENLINK*)lParam;\r
7407       if (pLink->msg == WM_LBUTTONUP)\r
7408       {\r
7409         TEXTRANGE tr;\r
7410 \r
7411         tr.chrg = pLink->chrg;\r
7412         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7413         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7414         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7415         free(tr.lpstrText);\r
7416       }\r
7417     }\r
7418     break;\r
7419   case WM_INITDIALOG: /* message: initialize dialog box */\r
7420     hwndConsole = hDlg;\r
7421     SetFocus(hInput);\r
7422     consoleTextWindowProc = (WNDPROC)\r
7423       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7424     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7425     consoleInputWindowProc = (WNDPROC)\r
7426       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7427     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7428     Colorize(ColorNormal, TRUE);\r
7429     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7430     ChangedConsoleFont();\r
7431     GetClientRect(hDlg, &rect);\r
7432     sizeX = rect.right;\r
7433     sizeY = rect.bottom;\r
7434     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7435         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7436       WINDOWPLACEMENT wp;\r
7437       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7438       wp.length = sizeof(WINDOWPLACEMENT);\r
7439       wp.flags = 0;\r
7440       wp.showCmd = SW_SHOW;\r
7441       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7442       wp.rcNormalPosition.left = wpConsole.x;\r
7443       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7444       wp.rcNormalPosition.top = wpConsole.y;\r
7445       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7446       SetWindowPlacement(hDlg, &wp);\r
7447     }\r
7448 \r
7449    // [HGM] Chessknight's change 2004-07-13\r
7450    else { /* Determine Defaults */\r
7451        WINDOWPLACEMENT wp;\r
7452        wpConsole.x = wpMain.width + 1;\r
7453        wpConsole.y = wpMain.y;\r
7454        wpConsole.width = screenWidth -  wpMain.width;\r
7455        wpConsole.height = wpMain.height;\r
7456        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7457        wp.length = sizeof(WINDOWPLACEMENT);\r
7458        wp.flags = 0;\r
7459        wp.showCmd = SW_SHOW;\r
7460        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7461        wp.rcNormalPosition.left = wpConsole.x;\r
7462        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7463        wp.rcNormalPosition.top = wpConsole.y;\r
7464        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7465        SetWindowPlacement(hDlg, &wp);\r
7466     }\r
7467 \r
7468    // Allow hText to highlight URLs and send notifications on them\r
7469    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7470    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7471    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7472    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7473 \r
7474     return FALSE;\r
7475 \r
7476   case WM_SETFOCUS:\r
7477     SetFocus(hInput);\r
7478     return 0;\r
7479 \r
7480   case WM_CLOSE:\r
7481     ExitEvent(0);\r
7482     /* not reached */\r
7483     break;\r
7484 \r
7485   case WM_SIZE:\r
7486     if (IsIconic(hDlg)) break;\r
7487     newSizeX = LOWORD(lParam);\r
7488     newSizeY = HIWORD(lParam);\r
7489     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7490       RECT rectText, rectInput;\r
7491       POINT pt;\r
7492       int newTextHeight, newTextWidth;\r
7493       GetWindowRect(hText, &rectText);\r
7494       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7495       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7496       if (newTextHeight < 0) {\r
7497         newSizeY += -newTextHeight;\r
7498         newTextHeight = 0;\r
7499       }\r
7500       SetWindowPos(hText, NULL, 0, 0,\r
7501         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7502       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7503       pt.x = rectInput.left;\r
7504       pt.y = rectInput.top + newSizeY - sizeY;\r
7505       ScreenToClient(hDlg, &pt);\r
7506       SetWindowPos(hInput, NULL, \r
7507         pt.x, pt.y, /* needs client coords */   \r
7508         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7509         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7510     }\r
7511     sizeX = newSizeX;\r
7512     sizeY = newSizeY;\r
7513     break;\r
7514 \r
7515   case WM_GETMINMAXINFO:\r
7516     /* Prevent resizing window too small */\r
7517     mmi = (MINMAXINFO *) lParam;\r
7518     mmi->ptMinTrackSize.x = 100;\r
7519     mmi->ptMinTrackSize.y = 100;\r
7520     break;\r
7521 \r
7522   /* [AS] Snapping */\r
7523   case WM_ENTERSIZEMOVE:\r
7524     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7525 \r
7526   case WM_SIZING:\r
7527     return OnSizing( &sd, hDlg, wParam, lParam );\r
7528 \r
7529   case WM_MOVING:\r
7530     return OnMoving( &sd, hDlg, wParam, lParam );\r
7531 \r
7532   case WM_EXITSIZEMOVE:\r
7533         UpdateICSWidth(hText);\r
7534     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7535   }\r
7536 \r
7537   return DefWindowProc(hDlg, message, wParam, lParam);\r
7538 }\r
7539 \r
7540 \r
7541 VOID\r
7542 ConsoleCreate()\r
7543 {\r
7544   HWND hCons;\r
7545   if (hwndConsole) return;\r
7546   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7547   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7548 }\r
7549 \r
7550 \r
7551 VOID\r
7552 ConsoleOutput(char* data, int length, int forceVisible)\r
7553 {\r
7554   HWND hText;\r
7555   int trim, exlen;\r
7556   char *p, *q;\r
7557   char buf[CO_MAX+1];\r
7558   POINT pEnd;\r
7559   RECT rect;\r
7560   static int delayLF = 0;\r
7561   CHARRANGE savesel, sel;\r
7562 \r
7563   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7564   p = data;\r
7565   q = buf;\r
7566   if (delayLF) {\r
7567     *q++ = '\r';\r
7568     *q++ = '\n';\r
7569     delayLF = 0;\r
7570   }\r
7571   while (length--) {\r
7572     if (*p == '\n') {\r
7573       if (*++p) {\r
7574         *q++ = '\r';\r
7575         *q++ = '\n';\r
7576       } else {\r
7577         delayLF = 1;\r
7578       }\r
7579     } else if (*p == '\007') {\r
7580        MyPlaySound(&sounds[(int)SoundBell]);\r
7581        p++;\r
7582     } else {\r
7583       *q++ = *p++;\r
7584     }\r
7585   }\r
7586   *q = NULLCHAR;\r
7587   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7588   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7589   /* Save current selection */\r
7590   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7591   exlen = GetWindowTextLength(hText);\r
7592   /* Find out whether current end of text is visible */\r
7593   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7594   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7595   /* Trim existing text if it's too long */\r
7596   if (exlen + (q - buf) > CO_MAX) {\r
7597     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7598     sel.cpMin = 0;\r
7599     sel.cpMax = trim;\r
7600     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7601     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7602     exlen -= trim;\r
7603     savesel.cpMin -= trim;\r
7604     savesel.cpMax -= trim;\r
7605     if (exlen < 0) exlen = 0;\r
7606     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7607     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7608   }\r
7609   /* Append the new text */\r
7610   sel.cpMin = exlen;\r
7611   sel.cpMax = exlen;\r
7612   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7613   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7614   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7615   if (forceVisible || exlen == 0 ||\r
7616       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7617        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7618     /* Scroll to make new end of text visible if old end of text\r
7619        was visible or new text is an echo of user typein */\r
7620     sel.cpMin = 9999999;\r
7621     sel.cpMax = 9999999;\r
7622     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7623     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7624     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7625     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7626   }\r
7627   if (savesel.cpMax == exlen || forceVisible) {\r
7628     /* Move insert point to new end of text if it was at the old\r
7629        end of text or if the new text is an echo of user typein */\r
7630     sel.cpMin = 9999999;\r
7631     sel.cpMax = 9999999;\r
7632     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7633   } else {\r
7634     /* Restore previous selection */\r
7635     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7636   }\r
7637   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7638 }\r
7639 \r
7640 /*---------*/\r
7641 \r
7642 \r
7643 void\r
7644 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7645 {\r
7646   char buf[100];\r
7647   char *str;\r
7648   COLORREF oldFg, oldBg;\r
7649   HFONT oldFont;\r
7650   RECT rect;\r
7651 \r
7652   if(copyNumber > 1)\r
7653     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7654 \r
7655   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7656   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7657   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7658 \r
7659   rect.left = x;\r
7660   rect.right = x + squareSize;\r
7661   rect.top  = y;\r
7662   rect.bottom = y + squareSize;\r
7663   str = buf;\r
7664 \r
7665   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7666                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7667              y, ETO_CLIPPED|ETO_OPAQUE,\r
7668              &rect, str, strlen(str), NULL);\r
7669 \r
7670   (void) SetTextColor(hdc, oldFg);\r
7671   (void) SetBkColor(hdc, oldBg);\r
7672   (void) SelectObject(hdc, oldFont);\r
7673 }\r
7674 \r
7675 void\r
7676 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7677               RECT *rect, char *color, char *flagFell)\r
7678 {\r
7679   char buf[100];\r
7680   char *str;\r
7681   COLORREF oldFg, oldBg;\r
7682   HFONT oldFont;\r
7683 \r
7684   if (twoBoards && partnerUp) return;\r
7685   if (appData.clockMode) {\r
7686     if (tinyLayout == 2)\r
7687       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7688     else\r
7689       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7690     str = buf;\r
7691   } else {\r
7692     str = color;\r
7693   }\r
7694 \r
7695   if (highlight) {\r
7696     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7697     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7698   } else {\r
7699     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7700     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7701   }\r
7702 \r
7703   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7704 \r
7705   JAWS_SILENCE\r
7706 \r
7707   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7708              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7709              rect, str, strlen(str), NULL);\r
7710   if(logoHeight > 0 && appData.clockMode) {\r
7711       RECT r;\r
7712       str += strlen(color)+2;\r
7713       r.top = rect->top + logoHeight/2;\r
7714       r.left = rect->left;\r
7715       r.right = rect->right;\r
7716       r.bottom = rect->bottom;\r
7717       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7718                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7719                  &r, str, strlen(str), NULL);\r
7720   }\r
7721   (void) SetTextColor(hdc, oldFg);\r
7722   (void) SetBkColor(hdc, oldBg);\r
7723   (void) SelectObject(hdc, oldFont);\r
7724 }\r
7725 \r
7726 \r
7727 int\r
7728 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7729            OVERLAPPED *ovl)\r
7730 {\r
7731   int ok, err;\r
7732 \r
7733   /* [AS]  */\r
7734   if( count <= 0 ) {\r
7735     if (appData.debugMode) {\r
7736       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7737     }\r
7738 \r
7739     return ERROR_INVALID_USER_BUFFER;\r
7740   }\r
7741 \r
7742   ResetEvent(ovl->hEvent);\r
7743   ovl->Offset = ovl->OffsetHigh = 0;\r
7744   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7745   if (ok) {\r
7746     err = NO_ERROR;\r
7747   } else {\r
7748     err = GetLastError();\r
7749     if (err == ERROR_IO_PENDING) {\r
7750       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7751       if (ok)\r
7752         err = NO_ERROR;\r
7753       else\r
7754         err = GetLastError();\r
7755     }\r
7756   }\r
7757   return err;\r
7758 }\r
7759 \r
7760 int\r
7761 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7762             OVERLAPPED *ovl)\r
7763 {\r
7764   int ok, err;\r
7765 \r
7766   ResetEvent(ovl->hEvent);\r
7767   ovl->Offset = ovl->OffsetHigh = 0;\r
7768   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7769   if (ok) {\r
7770     err = NO_ERROR;\r
7771   } else {\r
7772     err = GetLastError();\r
7773     if (err == ERROR_IO_PENDING) {\r
7774       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7775       if (ok)\r
7776         err = NO_ERROR;\r
7777       else\r
7778         err = GetLastError();\r
7779     }\r
7780 \r
7781   }\r
7782   return err;\r
7783 }\r
7784 \r
7785 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7786 void CheckForInputBufferFull( InputSource * is )\r
7787 {\r
7788     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7789         /* Look for end of line */\r
7790         char * p = is->buf;\r
7791         \r
7792         while( p < is->next && *p != '\n' ) {\r
7793             p++;\r
7794         }\r
7795 \r
7796         if( p >= is->next ) {\r
7797             if (appData.debugMode) {\r
7798                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7799             }\r
7800 \r
7801             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7802             is->count = (DWORD) -1;\r
7803             is->next = is->buf;\r
7804         }\r
7805     }\r
7806 }\r
7807 \r
7808 DWORD\r
7809 InputThread(LPVOID arg)\r
7810 {\r
7811   InputSource *is;\r
7812   OVERLAPPED ovl;\r
7813 \r
7814   is = (InputSource *) arg;\r
7815   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7816   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7817   while (is->hThread != NULL) {\r
7818     is->error = DoReadFile(is->hFile, is->next,\r
7819                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7820                            &is->count, &ovl);\r
7821     if (is->error == NO_ERROR) {\r
7822       is->next += is->count;\r
7823     } else {\r
7824       if (is->error == ERROR_BROKEN_PIPE) {\r
7825         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7826         is->count = 0;\r
7827       } else {\r
7828         is->count = (DWORD) -1;\r
7829         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7830         break; \r
7831       }\r
7832     }\r
7833 \r
7834     CheckForInputBufferFull( is );\r
7835 \r
7836     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7837 \r
7838     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7839 \r
7840     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7841   }\r
7842 \r
7843   CloseHandle(ovl.hEvent);\r
7844   CloseHandle(is->hFile);\r
7845 \r
7846   if (appData.debugMode) {\r
7847     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7848   }\r
7849 \r
7850   return 0;\r
7851 }\r
7852 \r
7853 \r
7854 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7855 DWORD\r
7856 NonOvlInputThread(LPVOID arg)\r
7857 {\r
7858   InputSource *is;\r
7859   char *p, *q;\r
7860   int i;\r
7861   char prev;\r
7862 \r
7863   is = (InputSource *) arg;\r
7864   while (is->hThread != NULL) {\r
7865     is->error = ReadFile(is->hFile, is->next,\r
7866                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7867                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7868     if (is->error == NO_ERROR) {\r
7869       /* Change CRLF to LF */\r
7870       if (is->next > is->buf) {\r
7871         p = is->next - 1;\r
7872         i = is->count + 1;\r
7873       } else {\r
7874         p = is->next;\r
7875         i = is->count;\r
7876       }\r
7877       q = p;\r
7878       prev = NULLCHAR;\r
7879       while (i > 0) {\r
7880         if (prev == '\r' && *p == '\n') {\r
7881           *(q-1) = '\n';\r
7882           is->count--;\r
7883         } else { \r
7884           *q++ = *p;\r
7885         }\r
7886         prev = *p++;\r
7887         i--;\r
7888       }\r
7889       *q = NULLCHAR;\r
7890       is->next = q;\r
7891     } else {\r
7892       if (is->error == ERROR_BROKEN_PIPE) {\r
7893         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7894         is->count = 0; \r
7895       } else {\r
7896         is->count = (DWORD) -1;\r
7897       }\r
7898     }\r
7899 \r
7900     CheckForInputBufferFull( is );\r
7901 \r
7902     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7903 \r
7904     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7905 \r
7906     if (is->count < 0) break;  /* Quit on error */\r
7907   }\r
7908   CloseHandle(is->hFile);\r
7909   return 0;\r
7910 }\r
7911 \r
7912 DWORD\r
7913 SocketInputThread(LPVOID arg)\r
7914 {\r
7915   InputSource *is;\r
7916 \r
7917   is = (InputSource *) arg;\r
7918   while (is->hThread != NULL) {\r
7919     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7920     if ((int)is->count == SOCKET_ERROR) {\r
7921       is->count = (DWORD) -1;\r
7922       is->error = WSAGetLastError();\r
7923     } else {\r
7924       is->error = NO_ERROR;\r
7925       is->next += is->count;\r
7926       if (is->count == 0 && is->second == is) {\r
7927         /* End of file on stderr; quit with no message */\r
7928         break;\r
7929       }\r
7930     }\r
7931     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7932 \r
7933     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7934 \r
7935     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7936   }\r
7937   return 0;\r
7938 }\r
7939 \r
7940 VOID\r
7941 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7942 {\r
7943   InputSource *is;\r
7944 \r
7945   is = (InputSource *) lParam;\r
7946   if (is->lineByLine) {\r
7947     /* Feed in lines one by one */\r
7948     char *p = is->buf;\r
7949     char *q = p;\r
7950     while (q < is->next) {\r
7951       if (*q++ == '\n') {\r
7952         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7953         p = q;\r
7954       }\r
7955     }\r
7956     \r
7957     /* Move any partial line to the start of the buffer */\r
7958     q = is->buf;\r
7959     while (p < is->next) {\r
7960       *q++ = *p++;\r
7961     }\r
7962     is->next = q;\r
7963 \r
7964     if (is->error != NO_ERROR || is->count == 0) {\r
7965       /* Notify backend of the error.  Note: If there was a partial\r
7966          line at the end, it is not flushed through. */\r
7967       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7968     }\r
7969   } else {\r
7970     /* Feed in the whole chunk of input at once */\r
7971     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7972     is->next = is->buf;\r
7973   }\r
7974 }\r
7975 \r
7976 /*---------------------------------------------------------------------------*\\r
7977  *\r
7978  *  Menu enables. Used when setting various modes.\r
7979  *\r
7980 \*---------------------------------------------------------------------------*/\r
7981 \r
7982 typedef struct {\r
7983   int item;\r
7984   int flags;\r
7985 } Enables;\r
7986 \r
7987 VOID\r
7988 GreyRevert(Boolean grey)\r
7989 { // [HGM] vari: for retracting variations in local mode\r
7990   HMENU hmenu = GetMenu(hwndMain);\r
7991   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7992   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7993 }\r
7994 \r
7995 VOID\r
7996 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7997 {\r
7998   while (enab->item > 0) {\r
7999     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8000     enab++;\r
8001   }\r
8002 }\r
8003 \r
8004 Enables gnuEnables[] = {\r
8005   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8016   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8017   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8018 \r
8019 \r
8020   // Needed to switch from ncp to GNU mode on Engine Load\r
8021   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8022   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8023   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8024   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8025   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8026   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8027   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8028   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8029   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8030   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8031   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8032   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8033   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8034   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8035   { -1, -1 }\r
8036 };\r
8037 \r
8038 Enables icsEnables[] = {\r
8039   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8042   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8043   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8044   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8045   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8046   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8047   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8048   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8050   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8051   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8052   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8053   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8054   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8055   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8056   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8057   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8058   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8059   { -1, -1 }\r
8060 };\r
8061 \r
8062 #if ZIPPY\r
8063 Enables zippyEnables[] = {\r
8064   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8065   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8066   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8067   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8068   { -1, -1 }\r
8069 };\r
8070 #endif\r
8071 \r
8072 Enables ncpEnables[] = {\r
8073   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8074   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8075   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8076   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8077   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8078   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8079   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8080   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8081   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8082   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8083   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8084   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8085   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8086   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8087   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8088   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8089   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8090   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8091   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8092   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8093   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8094   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8095   { -1, -1 }\r
8096 };\r
8097 \r
8098 Enables trainingOnEnables[] = {\r
8099   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8104   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8105   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8107   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8108   { -1, -1 }\r
8109 };\r
8110 \r
8111 Enables trainingOffEnables[] = {\r
8112   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8113   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8114   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8115   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8116   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8117   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8118   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8119   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8120   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8121   { -1, -1 }\r
8122 };\r
8123 \r
8124 /* These modify either ncpEnables or gnuEnables */\r
8125 Enables cmailEnables[] = {\r
8126   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8127   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8128   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8129   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8130   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8131   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8132   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8133   { -1, -1 }\r
8134 };\r
8135 \r
8136 Enables machineThinkingEnables[] = {\r
8137   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8138   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8139   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8140   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8141   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8142   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8143   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8144   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8145   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8146   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8147   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8148   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8149   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8150 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8151   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8152   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8153   { -1, -1 }\r
8154 };\r
8155 \r
8156 Enables userThinkingEnables[] = {\r
8157   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8158   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8159   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8160   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8161   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8162   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8163   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8164   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8165   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8166   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8167   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8168   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8169   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8170 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8171   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8172   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8173   { -1, -1 }\r
8174 };\r
8175 \r
8176 /*---------------------------------------------------------------------------*\\r
8177  *\r
8178  *  Front-end interface functions exported by XBoard.\r
8179  *  Functions appear in same order as prototypes in frontend.h.\r
8180  * \r
8181 \*---------------------------------------------------------------------------*/\r
8182 VOID\r
8183 CheckMark(UINT item, int state)\r
8184 {\r
8185     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8186 }\r
8187 \r
8188 VOID\r
8189 ModeHighlight()\r
8190 {\r
8191   static UINT prevChecked = 0;\r
8192   static int prevPausing = 0;\r
8193   UINT nowChecked;\r
8194 \r
8195   if (pausing != prevPausing) {\r
8196     prevPausing = pausing;\r
8197     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8198                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8199     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8200   }\r
8201 \r
8202   switch (gameMode) {\r
8203   case BeginningOfGame:\r
8204     if (appData.icsActive)\r
8205       nowChecked = IDM_IcsClient;\r
8206     else if (appData.noChessProgram)\r
8207       nowChecked = IDM_EditGame;\r
8208     else\r
8209       nowChecked = IDM_MachineBlack;\r
8210     break;\r
8211   case MachinePlaysBlack:\r
8212     nowChecked = IDM_MachineBlack;\r
8213     break;\r
8214   case MachinePlaysWhite:\r
8215     nowChecked = IDM_MachineWhite;\r
8216     break;\r
8217   case TwoMachinesPlay:\r
8218     nowChecked = IDM_TwoMachines;\r
8219     break;\r
8220   case AnalyzeMode:\r
8221     nowChecked = IDM_AnalysisMode;\r
8222     break;\r
8223   case AnalyzeFile:\r
8224     nowChecked = IDM_AnalyzeFile;\r
8225     break;\r
8226   case EditGame:\r
8227     nowChecked = IDM_EditGame;\r
8228     break;\r
8229   case PlayFromGameFile:\r
8230     nowChecked = IDM_LoadGame;\r
8231     break;\r
8232   case EditPosition:\r
8233     nowChecked = IDM_EditPosition;\r
8234     break;\r
8235   case Training:\r
8236     nowChecked = IDM_Training;\r
8237     break;\r
8238   case IcsPlayingWhite:\r
8239   case IcsPlayingBlack:\r
8240   case IcsObserving:\r
8241   case IcsIdle:\r
8242     nowChecked = IDM_IcsClient;\r
8243     break;\r
8244   default:\r
8245   case EndOfGame:\r
8246     nowChecked = 0;\r
8247     break;\r
8248   }\r
8249   if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8250     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
8251   CheckMark(prevChecked, MF_UNCHECKED);\r
8252   CheckMark(nowChecked, MF_CHECKED);\r
8253   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8254 \r
8255   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8256     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8257                           MF_BYCOMMAND|MF_ENABLED);\r
8258   } else {\r
8259     (void) EnableMenuItem(GetMenu(hwndMain), \r
8260                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8261   }\r
8262 \r
8263   prevChecked = nowChecked;\r
8264 \r
8265   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8266   if (appData.icsActive) {\r
8267        if (appData.icsEngineAnalyze) {\r
8268                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8269        } else {\r
8270                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8271        }\r
8272   }\r
8273   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8274 }\r
8275 \r
8276 VOID\r
8277 SetICSMode()\r
8278 {\r
8279   HMENU hmenu = GetMenu(hwndMain);\r
8280   SetMenuEnables(hmenu, icsEnables);\r
8281   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8282     MF_BYCOMMAND|MF_ENABLED);\r
8283 #if ZIPPY\r
8284   if (appData.zippyPlay) {\r
8285     SetMenuEnables(hmenu, zippyEnables);\r
8286     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8287          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8288           MF_BYCOMMAND|MF_ENABLED);\r
8289   }\r
8290 #endif\r
8291 }\r
8292 \r
8293 VOID\r
8294 SetGNUMode()\r
8295 {\r
8296   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8297 }\r
8298 \r
8299 VOID\r
8300 SetNCPMode()\r
8301 {\r
8302   HMENU hmenu = GetMenu(hwndMain);\r
8303   SetMenuEnables(hmenu, ncpEnables);\r
8304     DrawMenuBar(hwndMain);\r
8305 }\r
8306 \r
8307 VOID\r
8308 SetCmailMode()\r
8309 {\r
8310   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8311 }\r
8312 \r
8313 VOID \r
8314 SetTrainingModeOn()\r
8315 {\r
8316   int i;\r
8317   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8318   for (i = 0; i < N_BUTTONS; i++) {\r
8319     if (buttonDesc[i].hwnd != NULL)\r
8320       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8321   }\r
8322   CommentPopDown();\r
8323 }\r
8324 \r
8325 VOID SetTrainingModeOff()\r
8326 {\r
8327   int i;\r
8328   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8329   for (i = 0; i < N_BUTTONS; i++) {\r
8330     if (buttonDesc[i].hwnd != NULL)\r
8331       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8332   }\r
8333 }\r
8334 \r
8335 \r
8336 VOID\r
8337 SetUserThinkingEnables()\r
8338 {\r
8339   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8340 }\r
8341 \r
8342 VOID\r
8343 SetMachineThinkingEnables()\r
8344 {\r
8345   HMENU hMenu = GetMenu(hwndMain);\r
8346   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8347 \r
8348   SetMenuEnables(hMenu, machineThinkingEnables);\r
8349 \r
8350   if (gameMode == MachinePlaysBlack) {\r
8351     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8352   } else if (gameMode == MachinePlaysWhite) {\r
8353     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8354   } else if (gameMode == TwoMachinesPlay) {\r
8355     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8356   }\r
8357 }\r
8358 \r
8359 \r
8360 VOID\r
8361 DisplayTitle(char *str)\r
8362 {\r
8363   char title[MSG_SIZ], *host;\r
8364   if (str[0] != NULLCHAR) {\r
8365     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8366   } else if (appData.icsActive) {\r
8367     if (appData.icsCommPort[0] != NULLCHAR)\r
8368       host = "ICS";\r
8369     else \r
8370       host = appData.icsHost;\r
8371       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8372   } else if (appData.noChessProgram) {\r
8373     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8374   } else {\r
8375     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8376     strcat(title, ": ");\r
8377     strcat(title, first.tidy);\r
8378   }\r
8379   SetWindowText(hwndMain, title);\r
8380 }\r
8381 \r
8382 \r
8383 VOID\r
8384 DisplayMessage(char *str1, char *str2)\r
8385 {\r
8386   HDC hdc;\r
8387   HFONT oldFont;\r
8388   int remain = MESSAGE_TEXT_MAX - 1;\r
8389   int len;\r
8390 \r
8391   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8392   messageText[0] = NULLCHAR;\r
8393   if (*str1) {\r
8394     len = strlen(str1);\r
8395     if (len > remain) len = remain;\r
8396     strncpy(messageText, str1, len);\r
8397     messageText[len] = NULLCHAR;\r
8398     remain -= len;\r
8399   }\r
8400   if (*str2 && remain >= 2) {\r
8401     if (*str1) {\r
8402       strcat(messageText, "  ");\r
8403       remain -= 2;\r
8404     }\r
8405     len = strlen(str2);\r
8406     if (len > remain) len = remain;\r
8407     strncat(messageText, str2, len);\r
8408   }\r
8409   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8410   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8411 \r
8412   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8413 \r
8414   SAYMACHINEMOVE();\r
8415 \r
8416   hdc = GetDC(hwndMain);\r
8417   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8418   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8419              &messageRect, messageText, strlen(messageText), NULL);\r
8420   (void) SelectObject(hdc, oldFont);\r
8421   (void) ReleaseDC(hwndMain, hdc);\r
8422 }\r
8423 \r
8424 VOID\r
8425 DisplayError(char *str, int error)\r
8426 {\r
8427   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8428   int len;\r
8429 \r
8430   if (error == 0) {\r
8431     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8432   } else {\r
8433     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8434                         NULL, error, LANG_NEUTRAL,\r
8435                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8436     if (len > 0) {\r
8437       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8438     } else {\r
8439       ErrorMap *em = errmap;\r
8440       while (em->err != 0 && em->err != error) em++;\r
8441       if (em->err != 0) {\r
8442         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8443       } else {\r
8444         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8445       }\r
8446     }\r
8447   }\r
8448   \r
8449   ErrorPopUp(_("Error"), buf);\r
8450 }\r
8451 \r
8452 \r
8453 VOID\r
8454 DisplayMoveError(char *str)\r
8455 {\r
8456   fromX = fromY = -1;\r
8457   ClearHighlights();\r
8458   DrawPosition(FALSE, NULL);\r
8459   if (appData.popupMoveErrors) {\r
8460     ErrorPopUp(_("Error"), str);\r
8461   } else {\r
8462     DisplayMessage(str, "");\r
8463     moveErrorMessageUp = TRUE;\r
8464   }\r
8465 }\r
8466 \r
8467 VOID\r
8468 DisplayFatalError(char *str, int error, int exitStatus)\r
8469 {\r
8470   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8471   int len;\r
8472   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8473 \r
8474   if (error != 0) {\r
8475     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8476                         NULL, error, LANG_NEUTRAL,\r
8477                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8478     if (len > 0) {\r
8479       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8480     } else {\r
8481       ErrorMap *em = errmap;\r
8482       while (em->err != 0 && em->err != error) em++;\r
8483       if (em->err != 0) {\r
8484         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8485       } else {\r
8486         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8487       }\r
8488     }\r
8489     str = buf;\r
8490   }\r
8491   if (appData.debugMode) {\r
8492     fprintf(debugFP, "%s: %s\n", label, str);\r
8493   }\r
8494   if (appData.popupExitMessage) {\r
8495     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8496                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8497   }\r
8498   ExitEvent(exitStatus);\r
8499 }\r
8500 \r
8501 \r
8502 VOID\r
8503 DisplayInformation(char *str)\r
8504 {\r
8505   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8506 }\r
8507 \r
8508 \r
8509 VOID\r
8510 DisplayNote(char *str)\r
8511 {\r
8512   ErrorPopUp(_("Note"), str);\r
8513 }\r
8514 \r
8515 \r
8516 typedef struct {\r
8517   char *title, *question, *replyPrefix;\r
8518   ProcRef pr;\r
8519 } QuestionParams;\r
8520 \r
8521 LRESULT CALLBACK\r
8522 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8523 {\r
8524   static QuestionParams *qp;\r
8525   char reply[MSG_SIZ];\r
8526   int len, err;\r
8527 \r
8528   switch (message) {\r
8529   case WM_INITDIALOG:\r
8530     qp = (QuestionParams *) lParam;\r
8531     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8532     Translate(hDlg, DLG_Question);\r
8533     SetWindowText(hDlg, qp->title);\r
8534     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8535     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8536     return FALSE;\r
8537 \r
8538   case WM_COMMAND:\r
8539     switch (LOWORD(wParam)) {\r
8540     case IDOK:\r
8541       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8542       if (*reply) strcat(reply, " ");\r
8543       len = strlen(reply);\r
8544       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8545       strcat(reply, "\n");\r
8546       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8547       EndDialog(hDlg, TRUE);\r
8548       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8549       return TRUE;\r
8550     case IDCANCEL:\r
8551       EndDialog(hDlg, FALSE);\r
8552       return TRUE;\r
8553     default:\r
8554       break;\r
8555     }\r
8556     break;\r
8557   }\r
8558   return FALSE;\r
8559 }\r
8560 \r
8561 VOID\r
8562 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8563 {\r
8564     QuestionParams qp;\r
8565     FARPROC lpProc;\r
8566     \r
8567     qp.title = title;\r
8568     qp.question = question;\r
8569     qp.replyPrefix = replyPrefix;\r
8570     qp.pr = pr;\r
8571     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8572     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8573       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8574     FreeProcInstance(lpProc);\r
8575 }\r
8576 \r
8577 /* [AS] Pick FRC position */\r
8578 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8579 {\r
8580     static int * lpIndexFRC;\r
8581     BOOL index_is_ok;\r
8582     char buf[16];\r
8583 \r
8584     switch( message )\r
8585     {\r
8586     case WM_INITDIALOG:\r
8587         lpIndexFRC = (int *) lParam;\r
8588 \r
8589         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8590         Translate(hDlg, DLG_NewGameFRC);\r
8591 \r
8592         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8593         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8594         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8595         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8596 \r
8597         break;\r
8598 \r
8599     case WM_COMMAND:\r
8600         switch( LOWORD(wParam) ) {\r
8601         case IDOK:\r
8602             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8603             EndDialog( hDlg, 0 );\r
8604             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8605             return TRUE;\r
8606         case IDCANCEL:\r
8607             EndDialog( hDlg, 1 );   \r
8608             return TRUE;\r
8609         case IDC_NFG_Edit:\r
8610             if( HIWORD(wParam) == EN_CHANGE ) {\r
8611                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8612 \r
8613                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8614             }\r
8615             return TRUE;\r
8616         case IDC_NFG_Random:\r
8617           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8618             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8619             return TRUE;\r
8620         }\r
8621 \r
8622         break;\r
8623     }\r
8624 \r
8625     return FALSE;\r
8626 }\r
8627 \r
8628 int NewGameFRC()\r
8629 {\r
8630     int result;\r
8631     int index = appData.defaultFrcPosition;\r
8632     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8633 \r
8634     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8635 \r
8636     if( result == 0 ) {\r
8637         appData.defaultFrcPosition = index;\r
8638     }\r
8639 \r
8640     return result;\r
8641 }\r
8642 \r
8643 /* [AS] Game list options. Refactored by HGM */\r
8644 \r
8645 HWND gameListOptionsDialog;\r
8646 \r
8647 // low-level front-end: clear text edit / list widget\r
8648 void\r
8649 \r
8650 GLT_ClearList()\r
8651 {\r
8652     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8653 }\r
8654 \r
8655 // low-level front-end: clear text edit / list widget\r
8656 void\r
8657 GLT_DeSelectList()\r
8658 {\r
8659     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8660 }\r
8661 \r
8662 // low-level front-end: append line to text edit / list widget\r
8663 void\r
8664 GLT_AddToList( char *name )\r
8665 {\r
8666     if( name != 0 ) {\r
8667             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8668     }\r
8669 }\r
8670 \r
8671 // low-level front-end: get line from text edit / list widget\r
8672 Boolean\r
8673 GLT_GetFromList( int index, char *name )\r
8674 {\r
8675     if( name != 0 ) {\r
8676             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8677                 return TRUE;\r
8678     }\r
8679     return FALSE;\r
8680 }\r
8681 \r
8682 void GLT_MoveSelection( HWND hDlg, int delta )\r
8683 {\r
8684     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8685     int idx2 = idx1 + delta;\r
8686     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8687 \r
8688     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8689         char buf[128];\r
8690 \r
8691         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8692         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8693         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8694         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8695     }\r
8696 }\r
8697 \r
8698 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8699 {\r
8700     switch( message )\r
8701     {\r
8702     case WM_INITDIALOG:\r
8703         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8704         \r
8705         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8706         Translate(hDlg, DLG_GameListOptions);\r
8707 \r
8708         /* Initialize list */\r
8709         GLT_TagsToList( lpUserGLT );\r
8710 \r
8711         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8712 \r
8713         break;\r
8714 \r
8715     case WM_COMMAND:\r
8716         switch( LOWORD(wParam) ) {\r
8717         case IDOK:\r
8718             GLT_ParseList();\r
8719             EndDialog( hDlg, 0 );\r
8720             return TRUE;\r
8721         case IDCANCEL:\r
8722             EndDialog( hDlg, 1 );\r
8723             return TRUE;\r
8724 \r
8725         case IDC_GLT_Default:\r
8726             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8727             return TRUE;\r
8728 \r
8729         case IDC_GLT_Restore:\r
8730             GLT_TagsToList( appData.gameListTags );\r
8731             return TRUE;\r
8732 \r
8733         case IDC_GLT_Up:\r
8734             GLT_MoveSelection( hDlg, -1 );\r
8735             return TRUE;\r
8736 \r
8737         case IDC_GLT_Down:\r
8738             GLT_MoveSelection( hDlg, +1 );\r
8739             return TRUE;\r
8740         }\r
8741 \r
8742         break;\r
8743     }\r
8744 \r
8745     return FALSE;\r
8746 }\r
8747 \r
8748 int GameListOptions()\r
8749 {\r
8750     int result;\r
8751     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8752 \r
8753       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8754 \r
8755     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8756 \r
8757     if( result == 0 ) {\r
8758         char *oldTags = appData.gameListTags;\r
8759         /* [AS] Memory leak here! */\r
8760         appData.gameListTags = strdup( lpUserGLT ); \r
8761         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8762             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8763     }\r
8764 \r
8765     return result;\r
8766 }\r
8767 \r
8768 VOID\r
8769 DisplayIcsInteractionTitle(char *str)\r
8770 {\r
8771   char consoleTitle[MSG_SIZ];\r
8772 \r
8773     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8774     SetWindowText(hwndConsole, consoleTitle);\r
8775 \r
8776     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8777       char buf[MSG_SIZ], *p = buf, *q;\r
8778         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8779       do {\r
8780         q = strchr(p, ';');\r
8781         if(q) *q++ = 0;\r
8782         if(*p) ChatPopUp(p);\r
8783       } while(p=q);\r
8784     }\r
8785 \r
8786     SetActiveWindow(hwndMain);\r
8787 }\r
8788 \r
8789 void\r
8790 DrawPosition(int fullRedraw, Board board)\r
8791 {\r
8792   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8793 }\r
8794 \r
8795 void NotifyFrontendLogin()\r
8796 {\r
8797         if (hwndConsole)\r
8798                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8799 }\r
8800 \r
8801 VOID\r
8802 ResetFrontEnd()\r
8803 {\r
8804   fromX = fromY = -1;\r
8805   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8806     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8807     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8808     dragInfo.lastpos = dragInfo.pos;\r
8809     dragInfo.start.x = dragInfo.start.y = -1;\r
8810     dragInfo.from = dragInfo.start;\r
8811     ReleaseCapture();\r
8812     DrawPosition(TRUE, NULL);\r
8813   }\r
8814   TagsPopDown();\r
8815 }\r
8816 \r
8817 \r
8818 VOID\r
8819 CommentPopUp(char *title, char *str)\r
8820 {\r
8821   HWND hwnd = GetActiveWindow();\r
8822   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8823   SAY(str);\r
8824   SetActiveWindow(hwnd);\r
8825 }\r
8826 \r
8827 VOID\r
8828 CommentPopDown(void)\r
8829 {\r
8830   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8831   if (commentDialog) {\r
8832     ShowWindow(commentDialog, SW_HIDE);\r
8833   }\r
8834   commentUp = FALSE;\r
8835 }\r
8836 \r
8837 VOID\r
8838 EditCommentPopUp(int index, char *title, char *str)\r
8839 {\r
8840   EitherCommentPopUp(index, title, str, TRUE);\r
8841 }\r
8842 \r
8843 \r
8844 int\r
8845 Roar()\r
8846 {\r
8847   MyPlaySound(&sounds[(int)SoundRoar]);\r
8848   return 1;\r
8849 }\r
8850 \r
8851 VOID\r
8852 RingBell()\r
8853 {\r
8854   MyPlaySound(&sounds[(int)SoundMove]);\r
8855 }\r
8856 \r
8857 VOID PlayIcsWinSound()\r
8858 {\r
8859   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8860 }\r
8861 \r
8862 VOID PlayIcsLossSound()\r
8863 {\r
8864   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8865 }\r
8866 \r
8867 VOID PlayIcsDrawSound()\r
8868 {\r
8869   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8870 }\r
8871 \r
8872 VOID PlayIcsUnfinishedSound()\r
8873 {\r
8874   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8875 }\r
8876 \r
8877 VOID\r
8878 PlayAlarmSound()\r
8879 {\r
8880   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8881 }\r
8882 \r
8883 VOID\r
8884 PlayTellSound()\r
8885 {\r
8886   MyPlaySound(&textAttribs[ColorTell].sound);\r
8887 }\r
8888 \r
8889 \r
8890 VOID\r
8891 EchoOn()\r
8892 {\r
8893   HWND hInput;\r
8894   consoleEcho = TRUE;\r
8895   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8896   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8897   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8898 }\r
8899 \r
8900 \r
8901 VOID\r
8902 EchoOff()\r
8903 {\r
8904   CHARFORMAT cf;\r
8905   HWND hInput;\r
8906   consoleEcho = FALSE;\r
8907   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8908   /* This works OK: set text and background both to the same color */\r
8909   cf = consoleCF;\r
8910   cf.crTextColor = COLOR_ECHOOFF;\r
8911   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8912   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8913 }\r
8914 \r
8915 /* No Raw()...? */\r
8916 \r
8917 void Colorize(ColorClass cc, int continuation)\r
8918 {\r
8919   currentColorClass = cc;\r
8920   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8921   consoleCF.crTextColor = textAttribs[cc].color;\r
8922   consoleCF.dwEffects = textAttribs[cc].effects;\r
8923   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8924 }\r
8925 \r
8926 char *\r
8927 UserName()\r
8928 {\r
8929   static char buf[MSG_SIZ];\r
8930   DWORD bufsiz = MSG_SIZ;\r
8931 \r
8932   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8933         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8934   }\r
8935   if (!GetUserName(buf, &bufsiz)) {\r
8936     /*DisplayError("Error getting user name", GetLastError());*/\r
8937     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8938   }\r
8939   return buf;\r
8940 }\r
8941 \r
8942 char *\r
8943 HostName()\r
8944 {\r
8945   static char buf[MSG_SIZ];\r
8946   DWORD bufsiz = MSG_SIZ;\r
8947 \r
8948   if (!GetComputerName(buf, &bufsiz)) {\r
8949     /*DisplayError("Error getting host name", GetLastError());*/\r
8950     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8951   }\r
8952   return buf;\r
8953 }\r
8954 \r
8955 \r
8956 int\r
8957 ClockTimerRunning()\r
8958 {\r
8959   return clockTimerEvent != 0;\r
8960 }\r
8961 \r
8962 int\r
8963 StopClockTimer()\r
8964 {\r
8965   if (clockTimerEvent == 0) return FALSE;\r
8966   KillTimer(hwndMain, clockTimerEvent);\r
8967   clockTimerEvent = 0;\r
8968   return TRUE;\r
8969 }\r
8970 \r
8971 void\r
8972 StartClockTimer(long millisec)\r
8973 {\r
8974   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8975                              (UINT) millisec, NULL);\r
8976 }\r
8977 \r
8978 void\r
8979 DisplayWhiteClock(long timeRemaining, int highlight)\r
8980 {\r
8981   HDC hdc;\r
8982   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8983 \r
8984   if(appData.noGUI) return;\r
8985   hdc = GetDC(hwndMain);\r
8986   if (!IsIconic(hwndMain)) {\r
8987     DisplayAClock(hdc, timeRemaining, highlight, \r
8988                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8989   }\r
8990   if (highlight && iconCurrent == iconBlack) {\r
8991     iconCurrent = iconWhite;\r
8992     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8993     if (IsIconic(hwndMain)) {\r
8994       DrawIcon(hdc, 2, 2, iconCurrent);\r
8995     }\r
8996   }\r
8997   (void) ReleaseDC(hwndMain, hdc);\r
8998   if (hwndConsole)\r
8999     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9000 }\r
9001 \r
9002 void\r
9003 DisplayBlackClock(long timeRemaining, int highlight)\r
9004 {\r
9005   HDC hdc;\r
9006   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9007 \r
9008 \r
9009   if(appData.noGUI) return;\r
9010   hdc = GetDC(hwndMain);\r
9011   if (!IsIconic(hwndMain)) {\r
9012     DisplayAClock(hdc, timeRemaining, highlight, \r
9013                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
9014   }\r
9015   if (highlight && iconCurrent == iconWhite) {\r
9016     iconCurrent = iconBlack;\r
9017     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9018     if (IsIconic(hwndMain)) {\r
9019       DrawIcon(hdc, 2, 2, iconCurrent);\r
9020     }\r
9021   }\r
9022   (void) ReleaseDC(hwndMain, hdc);\r
9023   if (hwndConsole)\r
9024     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9025 }\r
9026 \r
9027 \r
9028 int\r
9029 LoadGameTimerRunning()\r
9030 {\r
9031   return loadGameTimerEvent != 0;\r
9032 }\r
9033 \r
9034 int\r
9035 StopLoadGameTimer()\r
9036 {\r
9037   if (loadGameTimerEvent == 0) return FALSE;\r
9038   KillTimer(hwndMain, loadGameTimerEvent);\r
9039   loadGameTimerEvent = 0;\r
9040   return TRUE;\r
9041 }\r
9042 \r
9043 void\r
9044 StartLoadGameTimer(long millisec)\r
9045 {\r
9046   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9047                                 (UINT) millisec, NULL);\r
9048 }\r
9049 \r
9050 void\r
9051 AutoSaveGame()\r
9052 {\r
9053   char *defName;\r
9054   FILE *f;\r
9055   char fileTitle[MSG_SIZ];\r
9056 \r
9057   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9058   f = OpenFileDialog(hwndMain, "a", defName,\r
9059                      appData.oldSaveStyle ? "gam" : "pgn",\r
9060                      GAME_FILT, \r
9061                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9062   if (f != NULL) {\r
9063     SaveGame(f, 0, "");\r
9064     fclose(f);\r
9065   }\r
9066 }\r
9067 \r
9068 \r
9069 void\r
9070 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9071 {\r
9072   if (delayedTimerEvent != 0) {\r
9073     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9074       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9075     }\r
9076     KillTimer(hwndMain, delayedTimerEvent);\r
9077     delayedTimerEvent = 0;\r
9078     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9079     delayedTimerCallback();\r
9080   }\r
9081   delayedTimerCallback = cb;\r
9082   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9083                                 (UINT) millisec, NULL);\r
9084 }\r
9085 \r
9086 DelayedEventCallback\r
9087 GetDelayedEvent()\r
9088 {\r
9089   if (delayedTimerEvent) {\r
9090     return delayedTimerCallback;\r
9091   } else {\r
9092     return NULL;\r
9093   }\r
9094 }\r
9095 \r
9096 void\r
9097 CancelDelayedEvent()\r
9098 {\r
9099   if (delayedTimerEvent) {\r
9100     KillTimer(hwndMain, delayedTimerEvent);\r
9101     delayedTimerEvent = 0;\r
9102   }\r
9103 }\r
9104 \r
9105 DWORD GetWin32Priority(int nice)\r
9106 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9107 /*\r
9108 REALTIME_PRIORITY_CLASS     0x00000100\r
9109 HIGH_PRIORITY_CLASS         0x00000080\r
9110 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9111 NORMAL_PRIORITY_CLASS       0x00000020\r
9112 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9113 IDLE_PRIORITY_CLASS         0x00000040\r
9114 */\r
9115         if (nice < -15) return 0x00000080;\r
9116         if (nice < 0)   return 0x00008000;\r
9117         if (nice == 0)  return 0x00000020;\r
9118         if (nice < 15)  return 0x00004000;\r
9119         return 0x00000040;\r
9120 }\r
9121 \r
9122 void RunCommand(char *cmdLine)\r
9123 {\r
9124   /* Now create the child process. */\r
9125   STARTUPINFO siStartInfo;\r
9126   PROCESS_INFORMATION piProcInfo;\r
9127 \r
9128   siStartInfo.cb = sizeof(STARTUPINFO);\r
9129   siStartInfo.lpReserved = NULL;\r
9130   siStartInfo.lpDesktop = NULL;\r
9131   siStartInfo.lpTitle = NULL;\r
9132   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9133   siStartInfo.cbReserved2 = 0;\r
9134   siStartInfo.lpReserved2 = NULL;\r
9135   siStartInfo.hStdInput = NULL;\r
9136   siStartInfo.hStdOutput = NULL;\r
9137   siStartInfo.hStdError = NULL;\r
9138 \r
9139   CreateProcess(NULL,\r
9140                 cmdLine,           /* command line */\r
9141                 NULL,      /* process security attributes */\r
9142                 NULL,      /* primary thread security attrs */\r
9143                 TRUE,      /* handles are inherited */\r
9144                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9145                 NULL,      /* use parent's environment */\r
9146                 NULL,\r
9147                 &siStartInfo, /* STARTUPINFO pointer */\r
9148                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9149 \r
9150   CloseHandle(piProcInfo.hThread);\r
9151 }\r
9152 \r
9153 /* Start a child process running the given program.\r
9154    The process's standard output can be read from "from", and its\r
9155    standard input can be written to "to".\r
9156    Exit with fatal error if anything goes wrong.\r
9157    Returns an opaque pointer that can be used to destroy the process\r
9158    later.\r
9159 */\r
9160 int\r
9161 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9162 {\r
9163 #define BUFSIZE 4096\r
9164 \r
9165   HANDLE hChildStdinRd, hChildStdinWr,\r
9166     hChildStdoutRd, hChildStdoutWr;\r
9167   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9168   SECURITY_ATTRIBUTES saAttr;\r
9169   BOOL fSuccess;\r
9170   PROCESS_INFORMATION piProcInfo;\r
9171   STARTUPINFO siStartInfo;\r
9172   ChildProc *cp;\r
9173   char buf[MSG_SIZ];\r
9174   DWORD err;\r
9175 \r
9176   if (appData.debugMode) {\r
9177     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9178   }\r
9179 \r
9180   *pr = NoProc;\r
9181 \r
9182   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9183   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9184   saAttr.bInheritHandle = TRUE;\r
9185   saAttr.lpSecurityDescriptor = NULL;\r
9186 \r
9187   /*\r
9188    * The steps for redirecting child's STDOUT:\r
9189    *     1. Create anonymous pipe to be STDOUT for child.\r
9190    *     2. Create a noninheritable duplicate of read handle,\r
9191    *         and close the inheritable read handle.\r
9192    */\r
9193 \r
9194   /* Create a pipe for the child's STDOUT. */\r
9195   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9196     return GetLastError();\r
9197   }\r
9198 \r
9199   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9200   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9201                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9202                              FALSE,     /* not inherited */\r
9203                              DUPLICATE_SAME_ACCESS);\r
9204   if (! fSuccess) {\r
9205     return GetLastError();\r
9206   }\r
9207   CloseHandle(hChildStdoutRd);\r
9208 \r
9209   /*\r
9210    * The steps for redirecting child's STDIN:\r
9211    *     1. Create anonymous pipe to be STDIN for child.\r
9212    *     2. Create a noninheritable duplicate of write handle,\r
9213    *         and close the inheritable write handle.\r
9214    */\r
9215 \r
9216   /* Create a pipe for the child's STDIN. */\r
9217   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9218     return GetLastError();\r
9219   }\r
9220 \r
9221   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9222   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9223                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9224                              FALSE,     /* not inherited */\r
9225                              DUPLICATE_SAME_ACCESS);\r
9226   if (! fSuccess) {\r
9227     return GetLastError();\r
9228   }\r
9229   CloseHandle(hChildStdinWr);\r
9230 \r
9231   /* Arrange to (1) look in dir for the child .exe file, and\r
9232    * (2) have dir be the child's working directory.  Interpret\r
9233    * dir relative to the directory WinBoard loaded from. */\r
9234   GetCurrentDirectory(MSG_SIZ, buf);\r
9235   SetCurrentDirectory(installDir);\r
9236   SetCurrentDirectory(dir);\r
9237 \r
9238   /* Now create the child process. */\r
9239 \r
9240   siStartInfo.cb = sizeof(STARTUPINFO);\r
9241   siStartInfo.lpReserved = NULL;\r
9242   siStartInfo.lpDesktop = NULL;\r
9243   siStartInfo.lpTitle = NULL;\r
9244   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9245   siStartInfo.cbReserved2 = 0;\r
9246   siStartInfo.lpReserved2 = NULL;\r
9247   siStartInfo.hStdInput = hChildStdinRd;\r
9248   siStartInfo.hStdOutput = hChildStdoutWr;\r
9249   siStartInfo.hStdError = hChildStdoutWr;\r
9250 \r
9251   fSuccess = CreateProcess(NULL,\r
9252                            cmdLine,        /* command line */\r
9253                            NULL,           /* process security attributes */\r
9254                            NULL,           /* primary thread security attrs */\r
9255                            TRUE,           /* handles are inherited */\r
9256                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9257                            NULL,           /* use parent's environment */\r
9258                            NULL,\r
9259                            &siStartInfo, /* STARTUPINFO pointer */\r
9260                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9261 \r
9262   err = GetLastError();\r
9263   SetCurrentDirectory(buf); /* return to prev directory */\r
9264   if (! fSuccess) {\r
9265     return err;\r
9266   }\r
9267 \r
9268   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9269     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9270     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9271   }\r
9272 \r
9273   /* Close the handles we don't need in the parent */\r
9274   CloseHandle(piProcInfo.hThread);\r
9275   CloseHandle(hChildStdinRd);\r
9276   CloseHandle(hChildStdoutWr);\r
9277 \r
9278   /* Prepare return value */\r
9279   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9280   cp->kind = CPReal;\r
9281   cp->hProcess = piProcInfo.hProcess;\r
9282   cp->pid = piProcInfo.dwProcessId;\r
9283   cp->hFrom = hChildStdoutRdDup;\r
9284   cp->hTo = hChildStdinWrDup;\r
9285 \r
9286   *pr = (void *) cp;\r
9287 \r
9288   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9289      2000 where engines sometimes don't see the initial command(s)\r
9290      from WinBoard and hang.  I don't understand how that can happen,\r
9291      but the Sleep is harmless, so I've put it in.  Others have also\r
9292      reported what may be the same problem, so hopefully this will fix\r
9293      it for them too.  */\r
9294   Sleep(500);\r
9295 \r
9296   return NO_ERROR;\r
9297 }\r
9298 \r
9299 \r
9300 void\r
9301 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9302 {\r
9303   ChildProc *cp; int result;\r
9304 \r
9305   cp = (ChildProc *) pr;\r
9306   if (cp == NULL) return;\r
9307 \r
9308   switch (cp->kind) {\r
9309   case CPReal:\r
9310     /* TerminateProcess is considered harmful, so... */\r
9311     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9312     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9313     /* The following doesn't work because the chess program\r
9314        doesn't "have the same console" as WinBoard.  Maybe\r
9315        we could arrange for this even though neither WinBoard\r
9316        nor the chess program uses a console for stdio? */\r
9317     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9318 \r
9319     /* [AS] Special termination modes for misbehaving programs... */\r
9320     if( signal & 8 ) { \r
9321         result = TerminateProcess( cp->hProcess, 0 );\r
9322 \r
9323         if ( appData.debugMode) {\r
9324             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9325         }\r
9326     }\r
9327     else if( signal & 4 ) {\r
9328         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9329 \r
9330         if( dw != WAIT_OBJECT_0 ) {\r
9331             result = TerminateProcess( cp->hProcess, 0 );\r
9332 \r
9333             if ( appData.debugMode) {\r
9334                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9335             }\r
9336 \r
9337         }\r
9338     }\r
9339 \r
9340     CloseHandle(cp->hProcess);\r
9341     break;\r
9342 \r
9343   case CPComm:\r
9344     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9345     break;\r
9346 \r
9347   case CPSock:\r
9348     closesocket(cp->sock);\r
9349     WSACleanup();\r
9350     break;\r
9351 \r
9352   case CPRcmd:\r
9353     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9354     closesocket(cp->sock);\r
9355     closesocket(cp->sock2);\r
9356     WSACleanup();\r
9357     break;\r
9358   }\r
9359   free(cp);\r
9360 }\r
9361 \r
9362 void\r
9363 InterruptChildProcess(ProcRef pr)\r
9364 {\r
9365   ChildProc *cp;\r
9366 \r
9367   cp = (ChildProc *) pr;\r
9368   if (cp == NULL) return;\r
9369   switch (cp->kind) {\r
9370   case CPReal:\r
9371     /* The following doesn't work because the chess program\r
9372        doesn't "have the same console" as WinBoard.  Maybe\r
9373        we could arrange for this even though neither WinBoard\r
9374        nor the chess program uses a console for stdio */\r
9375     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9376     break;\r
9377 \r
9378   case CPComm:\r
9379   case CPSock:\r
9380     /* Can't interrupt */\r
9381     break;\r
9382 \r
9383   case CPRcmd:\r
9384     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9385     break;\r
9386   }\r
9387 }\r
9388 \r
9389 \r
9390 int\r
9391 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9392 {\r
9393   char cmdLine[MSG_SIZ];\r
9394 \r
9395   if (port[0] == NULLCHAR) {\r
9396     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9397   } else {\r
9398     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9399   }\r
9400   return StartChildProcess(cmdLine, "", pr);\r
9401 }\r
9402 \r
9403 \r
9404 /* Code to open TCP sockets */\r
9405 \r
9406 int\r
9407 OpenTCP(char *host, char *port, ProcRef *pr)\r
9408 {\r
9409   ChildProc *cp;\r
9410   int err;\r
9411   SOCKET s;\r
9412 \r
9413   struct sockaddr_in sa, mysa;\r
9414   struct hostent FAR *hp;\r
9415   unsigned short uport;\r
9416   WORD wVersionRequested;\r
9417   WSADATA wsaData;\r
9418 \r
9419   /* Initialize socket DLL */\r
9420   wVersionRequested = MAKEWORD(1, 1);\r
9421   err = WSAStartup(wVersionRequested, &wsaData);\r
9422   if (err != 0) return err;\r
9423 \r
9424   /* Make socket */\r
9425   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9426     err = WSAGetLastError();\r
9427     WSACleanup();\r
9428     return err;\r
9429   }\r
9430 \r
9431   /* Bind local address using (mostly) don't-care values.\r
9432    */\r
9433   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9434   mysa.sin_family = AF_INET;\r
9435   mysa.sin_addr.s_addr = INADDR_ANY;\r
9436   uport = (unsigned short) 0;\r
9437   mysa.sin_port = htons(uport);\r
9438   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9439       == SOCKET_ERROR) {\r
9440     err = WSAGetLastError();\r
9441     WSACleanup();\r
9442     return err;\r
9443   }\r
9444 \r
9445   /* Resolve remote host name */\r
9446   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9447   if (!(hp = gethostbyname(host))) {\r
9448     unsigned int b0, b1, b2, b3;\r
9449 \r
9450     err = WSAGetLastError();\r
9451 \r
9452     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9453       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9454       hp->h_addrtype = AF_INET;\r
9455       hp->h_length = 4;\r
9456       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9457       hp->h_addr_list[0] = (char *) malloc(4);\r
9458       hp->h_addr_list[0][0] = (char) b0;\r
9459       hp->h_addr_list[0][1] = (char) b1;\r
9460       hp->h_addr_list[0][2] = (char) b2;\r
9461       hp->h_addr_list[0][3] = (char) b3;\r
9462     } else {\r
9463       WSACleanup();\r
9464       return err;\r
9465     }\r
9466   }\r
9467   sa.sin_family = hp->h_addrtype;\r
9468   uport = (unsigned short) atoi(port);\r
9469   sa.sin_port = htons(uport);\r
9470   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9471 \r
9472   /* Make connection */\r
9473   if (connect(s, (struct sockaddr *) &sa,\r
9474               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9475     err = WSAGetLastError();\r
9476     WSACleanup();\r
9477     return err;\r
9478   }\r
9479 \r
9480   /* Prepare return value */\r
9481   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9482   cp->kind = CPSock;\r
9483   cp->sock = s;\r
9484   *pr = (ProcRef *) cp;\r
9485 \r
9486   return NO_ERROR;\r
9487 }\r
9488 \r
9489 int\r
9490 OpenCommPort(char *name, ProcRef *pr)\r
9491 {\r
9492   HANDLE h;\r
9493   COMMTIMEOUTS ct;\r
9494   ChildProc *cp;\r
9495   char fullname[MSG_SIZ];\r
9496 \r
9497   if (*name != '\\')\r
9498     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9499   else\r
9500     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9501 \r
9502   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9503                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9504   if (h == (HANDLE) -1) {\r
9505     return GetLastError();\r
9506   }\r
9507   hCommPort = h;\r
9508 \r
9509   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9510 \r
9511   /* Accumulate characters until a 100ms pause, then parse */\r
9512   ct.ReadIntervalTimeout = 100;\r
9513   ct.ReadTotalTimeoutMultiplier = 0;\r
9514   ct.ReadTotalTimeoutConstant = 0;\r
9515   ct.WriteTotalTimeoutMultiplier = 0;\r
9516   ct.WriteTotalTimeoutConstant = 0;\r
9517   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9518 \r
9519   /* Prepare return value */\r
9520   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9521   cp->kind = CPComm;\r
9522   cp->hFrom = h;\r
9523   cp->hTo = h;\r
9524   *pr = (ProcRef *) cp;\r
9525 \r
9526   return NO_ERROR;\r
9527 }\r
9528 \r
9529 int\r
9530 OpenLoopback(ProcRef *pr)\r
9531 {\r
9532   DisplayFatalError(_("Not implemented"), 0, 1);\r
9533   return NO_ERROR;\r
9534 }\r
9535 \r
9536 \r
9537 int\r
9538 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9539 {\r
9540   ChildProc *cp;\r
9541   int err;\r
9542   SOCKET s, s2, s3;\r
9543   struct sockaddr_in sa, mysa;\r
9544   struct hostent FAR *hp;\r
9545   unsigned short uport;\r
9546   WORD wVersionRequested;\r
9547   WSADATA wsaData;\r
9548   int fromPort;\r
9549   char stderrPortStr[MSG_SIZ];\r
9550 \r
9551   /* Initialize socket DLL */\r
9552   wVersionRequested = MAKEWORD(1, 1);\r
9553   err = WSAStartup(wVersionRequested, &wsaData);\r
9554   if (err != 0) return err;\r
9555 \r
9556   /* Resolve remote host name */\r
9557   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9558   if (!(hp = gethostbyname(host))) {\r
9559     unsigned int b0, b1, b2, b3;\r
9560 \r
9561     err = WSAGetLastError();\r
9562 \r
9563     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9564       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9565       hp->h_addrtype = AF_INET;\r
9566       hp->h_length = 4;\r
9567       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9568       hp->h_addr_list[0] = (char *) malloc(4);\r
9569       hp->h_addr_list[0][0] = (char) b0;\r
9570       hp->h_addr_list[0][1] = (char) b1;\r
9571       hp->h_addr_list[0][2] = (char) b2;\r
9572       hp->h_addr_list[0][3] = (char) b3;\r
9573     } else {\r
9574       WSACleanup();\r
9575       return err;\r
9576     }\r
9577   }\r
9578   sa.sin_family = hp->h_addrtype;\r
9579   uport = (unsigned short) 514;\r
9580   sa.sin_port = htons(uport);\r
9581   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9582 \r
9583   /* Bind local socket to unused "privileged" port address\r
9584    */\r
9585   s = INVALID_SOCKET;\r
9586   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9587   mysa.sin_family = AF_INET;\r
9588   mysa.sin_addr.s_addr = INADDR_ANY;\r
9589   for (fromPort = 1023;; fromPort--) {\r
9590     if (fromPort < 0) {\r
9591       WSACleanup();\r
9592       return WSAEADDRINUSE;\r
9593     }\r
9594     if (s == INVALID_SOCKET) {\r
9595       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9596         err = WSAGetLastError();\r
9597         WSACleanup();\r
9598         return err;\r
9599       }\r
9600     }\r
9601     uport = (unsigned short) fromPort;\r
9602     mysa.sin_port = htons(uport);\r
9603     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9604         == SOCKET_ERROR) {\r
9605       err = WSAGetLastError();\r
9606       if (err == WSAEADDRINUSE) continue;\r
9607       WSACleanup();\r
9608       return err;\r
9609     }\r
9610     if (connect(s, (struct sockaddr *) &sa,\r
9611       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9612       err = WSAGetLastError();\r
9613       if (err == WSAEADDRINUSE) {\r
9614         closesocket(s);\r
9615         s = -1;\r
9616         continue;\r
9617       }\r
9618       WSACleanup();\r
9619       return err;\r
9620     }\r
9621     break;\r
9622   }\r
9623 \r
9624   /* Bind stderr local socket to unused "privileged" port address\r
9625    */\r
9626   s2 = INVALID_SOCKET;\r
9627   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9628   mysa.sin_family = AF_INET;\r
9629   mysa.sin_addr.s_addr = INADDR_ANY;\r
9630   for (fromPort = 1023;; fromPort--) {\r
9631     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9632     if (fromPort < 0) {\r
9633       (void) closesocket(s);\r
9634       WSACleanup();\r
9635       return WSAEADDRINUSE;\r
9636     }\r
9637     if (s2 == INVALID_SOCKET) {\r
9638       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9639         err = WSAGetLastError();\r
9640         closesocket(s);\r
9641         WSACleanup();\r
9642         return err;\r
9643       }\r
9644     }\r
9645     uport = (unsigned short) fromPort;\r
9646     mysa.sin_port = htons(uport);\r
9647     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9648         == SOCKET_ERROR) {\r
9649       err = WSAGetLastError();\r
9650       if (err == WSAEADDRINUSE) continue;\r
9651       (void) closesocket(s);\r
9652       WSACleanup();\r
9653       return err;\r
9654     }\r
9655     if (listen(s2, 1) == SOCKET_ERROR) {\r
9656       err = WSAGetLastError();\r
9657       if (err == WSAEADDRINUSE) {\r
9658         closesocket(s2);\r
9659         s2 = INVALID_SOCKET;\r
9660         continue;\r
9661       }\r
9662       (void) closesocket(s);\r
9663       (void) closesocket(s2);\r
9664       WSACleanup();\r
9665       return err;\r
9666     }\r
9667     break;\r
9668   }\r
9669   prevStderrPort = fromPort; // remember port used\r
9670   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9671 \r
9672   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9673     err = WSAGetLastError();\r
9674     (void) closesocket(s);\r
9675     (void) closesocket(s2);\r
9676     WSACleanup();\r
9677     return err;\r
9678   }\r
9679 \r
9680   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9681     err = WSAGetLastError();\r
9682     (void) closesocket(s);\r
9683     (void) closesocket(s2);\r
9684     WSACleanup();\r
9685     return err;\r
9686   }\r
9687   if (*user == NULLCHAR) user = UserName();\r
9688   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9689     err = WSAGetLastError();\r
9690     (void) closesocket(s);\r
9691     (void) closesocket(s2);\r
9692     WSACleanup();\r
9693     return err;\r
9694   }\r
9695   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9696     err = WSAGetLastError();\r
9697     (void) closesocket(s);\r
9698     (void) closesocket(s2);\r
9699     WSACleanup();\r
9700     return err;\r
9701   }\r
9702 \r
9703   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9704     err = WSAGetLastError();\r
9705     (void) closesocket(s);\r
9706     (void) closesocket(s2);\r
9707     WSACleanup();\r
9708     return err;\r
9709   }\r
9710   (void) closesocket(s2);  /* Stop listening */\r
9711 \r
9712   /* Prepare return value */\r
9713   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9714   cp->kind = CPRcmd;\r
9715   cp->sock = s;\r
9716   cp->sock2 = s3;\r
9717   *pr = (ProcRef *) cp;\r
9718 \r
9719   return NO_ERROR;\r
9720 }\r
9721 \r
9722 \r
9723 InputSourceRef\r
9724 AddInputSource(ProcRef pr, int lineByLine,\r
9725                InputCallback func, VOIDSTAR closure)\r
9726 {\r
9727   InputSource *is, *is2 = NULL;\r
9728   ChildProc *cp = (ChildProc *) pr;\r
9729 \r
9730   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9731   is->lineByLine = lineByLine;\r
9732   is->func = func;\r
9733   is->closure = closure;\r
9734   is->second = NULL;\r
9735   is->next = is->buf;\r
9736   if (pr == NoProc) {\r
9737     is->kind = CPReal;\r
9738     consoleInputSource = is;\r
9739   } else {\r
9740     is->kind = cp->kind;\r
9741     /* \r
9742         [AS] Try to avoid a race condition if the thread is given control too early:\r
9743         we create all threads suspended so that the is->hThread variable can be\r
9744         safely assigned, then let the threads start with ResumeThread.\r
9745     */\r
9746     switch (cp->kind) {\r
9747     case CPReal:\r
9748       is->hFile = cp->hFrom;\r
9749       cp->hFrom = NULL; /* now owned by InputThread */\r
9750       is->hThread =\r
9751         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9752                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9753       break;\r
9754 \r
9755     case CPComm:\r
9756       is->hFile = cp->hFrom;\r
9757       cp->hFrom = NULL; /* now owned by InputThread */\r
9758       is->hThread =\r
9759         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9760                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9761       break;\r
9762 \r
9763     case CPSock:\r
9764       is->sock = cp->sock;\r
9765       is->hThread =\r
9766         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9767                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9768       break;\r
9769 \r
9770     case CPRcmd:\r
9771       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9772       *is2 = *is;\r
9773       is->sock = cp->sock;\r
9774       is->second = is2;\r
9775       is2->sock = cp->sock2;\r
9776       is2->second = is2;\r
9777       is->hThread =\r
9778         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9779                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9780       is2->hThread =\r
9781         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9782                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9783       break;\r
9784     }\r
9785 \r
9786     if( is->hThread != NULL ) {\r
9787         ResumeThread( is->hThread );\r
9788     }\r
9789 \r
9790     if( is2 != NULL && is2->hThread != NULL ) {\r
9791         ResumeThread( is2->hThread );\r
9792     }\r
9793   }\r
9794 \r
9795   return (InputSourceRef) is;\r
9796 }\r
9797 \r
9798 void\r
9799 RemoveInputSource(InputSourceRef isr)\r
9800 {\r
9801   InputSource *is;\r
9802 \r
9803   is = (InputSource *) isr;\r
9804   is->hThread = NULL;  /* tell thread to stop */\r
9805   CloseHandle(is->hThread);\r
9806   if (is->second != NULL) {\r
9807     is->second->hThread = NULL;\r
9808     CloseHandle(is->second->hThread);\r
9809   }\r
9810 }\r
9811 \r
9812 int no_wrap(char *message, int count)\r
9813 {\r
9814     ConsoleOutput(message, count, FALSE);\r
9815     return count;\r
9816 }\r
9817 \r
9818 int\r
9819 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9820 {\r
9821   DWORD dOutCount;\r
9822   int outCount = SOCKET_ERROR;\r
9823   ChildProc *cp = (ChildProc *) pr;\r
9824   static OVERLAPPED ovl;\r
9825 \r
9826   static int line = 0;\r
9827 \r
9828   if (pr == NoProc)\r
9829   {\r
9830     if (appData.noJoin || !appData.useInternalWrap)\r
9831       return no_wrap(message, count);\r
9832     else\r
9833     {\r
9834       int width = get_term_width();\r
9835       int len = wrap(NULL, message, count, width, &line);\r
9836       char *msg = malloc(len);\r
9837       int dbgchk;\r
9838 \r
9839       if (!msg)\r
9840         return no_wrap(message, count);\r
9841       else\r
9842       {\r
9843         dbgchk = wrap(msg, message, count, width, &line);\r
9844         if (dbgchk != len && appData.debugMode)\r
9845             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9846         ConsoleOutput(msg, len, FALSE);\r
9847         free(msg);\r
9848         return len;\r
9849       }\r
9850     }\r
9851   }\r
9852 \r
9853   if (ovl.hEvent == NULL) {\r
9854     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9855   }\r
9856   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9857 \r
9858   switch (cp->kind) {\r
9859   case CPSock:\r
9860   case CPRcmd:\r
9861     outCount = send(cp->sock, message, count, 0);\r
9862     if (outCount == SOCKET_ERROR) {\r
9863       *outError = WSAGetLastError();\r
9864     } else {\r
9865       *outError = NO_ERROR;\r
9866     }\r
9867     break;\r
9868 \r
9869   case CPReal:\r
9870     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9871                   &dOutCount, NULL)) {\r
9872       *outError = NO_ERROR;\r
9873       outCount = (int) dOutCount;\r
9874     } else {\r
9875       *outError = GetLastError();\r
9876     }\r
9877     break;\r
9878 \r
9879   case CPComm:\r
9880     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9881                             &dOutCount, &ovl);\r
9882     if (*outError == NO_ERROR) {\r
9883       outCount = (int) dOutCount;\r
9884     }\r
9885     break;\r
9886   }\r
9887   return outCount;\r
9888 }\r
9889 \r
9890 void\r
9891 DoSleep(int n)\r
9892 {\r
9893     if(n != 0) Sleep(n);\r
9894 }\r
9895 \r
9896 int\r
9897 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9898                        long msdelay)\r
9899 {\r
9900   /* Ignore delay, not implemented for WinBoard */\r
9901   return OutputToProcess(pr, message, count, outError);\r
9902 }\r
9903 \r
9904 \r
9905 void\r
9906 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9907                         char *buf, int count, int error)\r
9908 {\r
9909   DisplayFatalError(_("Not implemented"), 0, 1);\r
9910 }\r
9911 \r
9912 /* see wgamelist.c for Game List functions */\r
9913 /* see wedittags.c for Edit Tags functions */\r
9914 \r
9915 \r
9916 int\r
9917 ICSInitScript()\r
9918 {\r
9919   FILE *f;\r
9920   char buf[MSG_SIZ];\r
9921   char *dummy;\r
9922 \r
9923   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9924     f = fopen(buf, "r");\r
9925     if (f != NULL) {\r
9926       ProcessICSInitScript(f);\r
9927       fclose(f);\r
9928       return TRUE;\r
9929     }\r
9930   }\r
9931   return FALSE;\r
9932 }\r
9933 \r
9934 \r
9935 VOID\r
9936 StartAnalysisClock()\r
9937 {\r
9938   if (analysisTimerEvent) return;\r
9939   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9940                                         (UINT) 2000, NULL);\r
9941 }\r
9942 \r
9943 VOID\r
9944 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9945 {\r
9946   highlightInfo.sq[0].x = fromX;\r
9947   highlightInfo.sq[0].y = fromY;\r
9948   highlightInfo.sq[1].x = toX;\r
9949   highlightInfo.sq[1].y = toY;\r
9950 }\r
9951 \r
9952 VOID\r
9953 ClearHighlights()\r
9954 {\r
9955   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9956     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9957 }\r
9958 \r
9959 VOID\r
9960 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9961 {\r
9962   premoveHighlightInfo.sq[0].x = fromX;\r
9963   premoveHighlightInfo.sq[0].y = fromY;\r
9964   premoveHighlightInfo.sq[1].x = toX;\r
9965   premoveHighlightInfo.sq[1].y = toY;\r
9966 }\r
9967 \r
9968 VOID\r
9969 ClearPremoveHighlights()\r
9970 {\r
9971   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9972     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9973 }\r
9974 \r
9975 VOID\r
9976 ShutDownFrontEnd()\r
9977 {\r
9978   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9979   DeleteClipboardTempFiles();\r
9980 }\r
9981 \r
9982 void\r
9983 BoardToTop()\r
9984 {\r
9985     if (IsIconic(hwndMain))\r
9986       ShowWindow(hwndMain, SW_RESTORE);\r
9987 \r
9988     SetActiveWindow(hwndMain);\r
9989 }\r
9990 \r
9991 /*\r
9992  * Prototypes for animation support routines\r
9993  */\r
9994 static void ScreenSquare(int column, int row, POINT * pt);\r
9995 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9996      POINT frames[], int * nFrames);\r
9997 \r
9998 \r
9999 #define kFactor 4\r
10000 \r
10001 void\r
10002 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
10003 {       // [HGM] atomic: animate blast wave\r
10004         int i;\r
10005 \r
10006         explodeInfo.fromX = fromX;\r
10007         explodeInfo.fromY = fromY;\r
10008         explodeInfo.toX = toX;\r
10009         explodeInfo.toY = toY;\r
10010         for(i=1; i<4*kFactor; i++) {\r
10011             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
10012             DrawPosition(FALSE, board);\r
10013             Sleep(appData.animSpeed);\r
10014         }\r
10015         explodeInfo.radius = 0;\r
10016         DrawPosition(TRUE, board);\r
10017 }\r
10018 \r
10019 void\r
10020 AnimateMove(board, fromX, fromY, toX, toY)\r
10021      Board board;\r
10022      int fromX;\r
10023      int fromY;\r
10024      int toX;\r
10025      int toY;\r
10026 {\r
10027   ChessSquare piece;\r
10028   int x = toX, y = toY;\r
10029   POINT start, finish, mid;\r
10030   POINT frames[kFactor * 2 + 1];\r
10031   int nFrames, n;\r
10032 \r
10033   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10034 \r
10035   if (!appData.animate) return;\r
10036   if (doingSizing) return;\r
10037   if (fromY < 0 || fromX < 0) return;\r
10038   piece = board[fromY][fromX];\r
10039   if (piece >= EmptySquare) return;\r
10040 \r
10041   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
10042 \r
10043 again:\r
10044 \r
10045   ScreenSquare(fromX, fromY, &start);\r
10046   ScreenSquare(toX, toY, &finish);\r
10047 \r
10048   /* All moves except knight jumps move in straight line */\r
10049   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10050     mid.x = start.x + (finish.x - start.x) / 2;\r
10051     mid.y = start.y + (finish.y - start.y) / 2;\r
10052   } else {\r
10053     /* Knight: make straight movement then diagonal */\r
10054     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10055        mid.x = start.x + (finish.x - start.x) / 2;\r
10056        mid.y = start.y;\r
10057      } else {\r
10058        mid.x = start.x;\r
10059        mid.y = start.y + (finish.y - start.y) / 2;\r
10060      }\r
10061   }\r
10062   \r
10063   /* Don't use as many frames for very short moves */\r
10064   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10065     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10066   else\r
10067     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10068 \r
10069   animInfo.from.x = fromX;\r
10070   animInfo.from.y = fromY;\r
10071   animInfo.to.x = toX;\r
10072   animInfo.to.y = toY;\r
10073   animInfo.lastpos = start;\r
10074   animInfo.piece = piece;\r
10075   for (n = 0; n < nFrames; n++) {\r
10076     animInfo.pos = frames[n];\r
10077     DrawPosition(FALSE, NULL);\r
10078     animInfo.lastpos = animInfo.pos;\r
10079     Sleep(appData.animSpeed);\r
10080   }\r
10081   animInfo.pos = finish;\r
10082   DrawPosition(FALSE, NULL);\r
10083 \r
10084   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10085 \r
10086   animInfo.piece = EmptySquare;\r
10087   Explode(board, fromX, fromY, toX, toY);\r
10088 }\r
10089 \r
10090 /*      Convert board position to corner of screen rect and color       */\r
10091 \r
10092 static void\r
10093 ScreenSquare(column, row, pt)\r
10094      int column; int row; POINT * pt;\r
10095 {\r
10096   if (flipView) {\r
10097     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10098     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10099   } else {\r
10100     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10101     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10102   }\r
10103 }\r
10104 \r
10105 /*      Generate a series of frame coords from start->mid->finish.\r
10106         The movement rate doubles until the half way point is\r
10107         reached, then halves back down to the final destination,\r
10108         which gives a nice slow in/out effect. The algorithmn\r
10109         may seem to generate too many intermediates for short\r
10110         moves, but remember that the purpose is to attract the\r
10111         viewers attention to the piece about to be moved and\r
10112         then to where it ends up. Too few frames would be less\r
10113         noticeable.                                             */\r
10114 \r
10115 static void\r
10116 Tween(start, mid, finish, factor, frames, nFrames)\r
10117      POINT * start; POINT * mid;\r
10118      POINT * finish; int factor;\r
10119      POINT frames[]; int * nFrames;\r
10120 {\r
10121   int n, fraction = 1, count = 0;\r
10122 \r
10123   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10124   for (n = 0; n < factor; n++)\r
10125     fraction *= 2;\r
10126   for (n = 0; n < factor; n++) {\r
10127     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10128     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10129     count ++;\r
10130     fraction = fraction / 2;\r
10131   }\r
10132   \r
10133   /* Midpoint */\r
10134   frames[count] = *mid;\r
10135   count ++;\r
10136   \r
10137   /* Slow out, stepping 1/2, then 1/4, ... */\r
10138   fraction = 2;\r
10139   for (n = 0; n < factor; n++) {\r
10140     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10141     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10142     count ++;\r
10143     fraction = fraction * 2;\r
10144   }\r
10145   *nFrames = count;\r
10146 }\r
10147 \r
10148 void\r
10149 SettingsPopUp(ChessProgramState *cps)\r
10150 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10151       EngineOptionsPopup(savedHwnd, cps);\r
10152 }\r
10153 \r
10154 int flock(int fid, int code)\r
10155 {\r
10156     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10157     OVERLAPPED ov;\r
10158     ov.hEvent = NULL;\r
10159     ov.Offset = 0;\r
10160     ov.OffsetHigh = 0;\r
10161     switch(code) {\r
10162       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10163 \r
10164       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10165       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10166       default: return -1;\r
10167     }\r
10168     return 0;\r
10169 }\r
10170 \r
10171 char *\r
10172 Col2Text (int n)\r
10173 {\r
10174     static int i=0;\r
10175     static char col[8][20];\r
10176     COLORREF color = *(COLORREF *) colorVariable[n];\r
10177     i = i+1 & 7;\r
10178     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10179     return col[i];\r
10180 }\r
10181 \r
10182 void\r
10183 ActivateTheme (int new)\r
10184 {   // Redo initialization of features depending on options that can occur in themes\r
10185    InitTextures();\r
10186    if(new) InitDrawingColors();\r
10187    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10188    InitDrawingSizes(boardSize, 0);\r
10189    InvalidateRect(hwndMain, NULL, TRUE);\r
10190 }\r