Correctly remember checkboxes on Continue Later (WB)
[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, 1, 0, 0 },\r
538   { "teeny",    25, 1, 1, 1, 0, 0 },\r
539   { "dinky",    29, 1, 1, 1, 0, 0 },\r
540   { "petite",   33, 1, 1, 1, 0, 0 },\r
541   { "slim",     37, 2, 1, 0, 0, 0 },\r
542   { "small",    40, 2, 1, 0, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
574   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
575   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
576   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
577   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
578   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
607 };\r
608 \r
609 \r
610 MySound sounds[(int)NSoundClasses];\r
611 MyTextAttribs textAttribs[(int)NColorClasses];\r
612 \r
613 MyColorizeAttribs colorizeAttribs[] = {\r
614   { (COLORREF)0, 0, N_("Shout Text") },\r
615   { (COLORREF)0, 0, N_("SShout/CShout") },\r
616   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
617   { (COLORREF)0, 0, N_("Channel Text") },\r
618   { (COLORREF)0, 0, N_("Kibitz Text") },\r
619   { (COLORREF)0, 0, N_("Tell Text") },\r
620   { (COLORREF)0, 0, N_("Challenge Text") },\r
621   { (COLORREF)0, 0, N_("Request Text") },\r
622   { (COLORREF)0, 0, N_("Seek Text") },\r
623   { (COLORREF)0, 0, N_("Normal Text") },\r
624   { (COLORREF)0, 0, N_("None") }\r
625 };\r
626 \r
627 \r
628 \r
629 static char *commentTitle;\r
630 static char *commentText;\r
631 static int commentIndex;\r
632 static Boolean editComment = FALSE;\r
633 \r
634 \r
635 char errorTitle[MSG_SIZ];\r
636 char errorMessage[2*MSG_SIZ];\r
637 HWND errorDialog = NULL;\r
638 BOOLEAN moveErrorMessageUp = FALSE;\r
639 BOOLEAN consoleEcho = TRUE;\r
640 CHARFORMAT consoleCF;\r
641 COLORREF consoleBackgroundColor;\r
642 \r
643 char *programVersion;\r
644 \r
645 #define CPReal 1\r
646 #define CPComm 2\r
647 #define CPSock 3\r
648 #define CPRcmd 4\r
649 typedef int CPKind;\r
650 \r
651 typedef struct {\r
652   CPKind kind;\r
653   HANDLE hProcess;\r
654   DWORD pid;\r
655   HANDLE hTo;\r
656   HANDLE hFrom;\r
657   SOCKET sock;\r
658   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
659 } ChildProc;\r
660 \r
661 #define INPUT_SOURCE_BUF_SIZE 4096\r
662 \r
663 typedef struct _InputSource {\r
664   CPKind kind;\r
665   HANDLE hFile;\r
666   SOCKET sock;\r
667   int lineByLine;\r
668   HANDLE hThread;\r
669   DWORD id;\r
670   char buf[INPUT_SOURCE_BUF_SIZE];\r
671   char *next;\r
672   DWORD count;\r
673   int error;\r
674   InputCallback func;\r
675   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
676   VOIDSTAR closure;\r
677 } InputSource;\r
678 \r
679 InputSource *consoleInputSource;\r
680 \r
681 DCB dcb;\r
682 \r
683 /* forward */\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
685 VOID ConsoleCreate();\r
686 LRESULT CALLBACK\r
687   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);\r
691 LRESULT CALLBACK\r
692   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
694 void ParseIcsTextMenu(char *icsTextMenuString);\r
695 VOID PopUpNameDialog(char firstchar);\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
697 \r
698 /* [AS] */\r
699 int NewGameFRC();\r
700 int GameListOptions();\r
701 \r
702 int dummy; // [HGM] for obsolete args\r
703 \r
704 HWND hwndMain = NULL;        /* root window*/\r
705 HWND hwndConsole = NULL;\r
706 HWND commentDialog = NULL;\r
707 HWND moveHistoryDialog = NULL;\r
708 HWND evalGraphDialog = NULL;\r
709 HWND engineOutputDialog = NULL;\r
710 HWND gameListDialog = NULL;\r
711 HWND editTagsDialog = NULL;\r
712 \r
713 int commentUp = FALSE;\r
714 \r
715 WindowPlacement wpMain;\r
716 WindowPlacement wpConsole;\r
717 WindowPlacement wpComment;\r
718 WindowPlacement wpMoveHistory;\r
719 WindowPlacement wpEvalGraph;\r
720 WindowPlacement wpEngineOutput;\r
721 WindowPlacement wpGameList;\r
722 WindowPlacement wpTags;\r
723 \r
724 VOID EngineOptionsPopup(); // [HGM] settings\r
725 \r
726 VOID GothicPopUp(char *title, VariantClass variant);\r
727 /*\r
728  * Setting "frozen" should disable all user input other than deleting\r
729  * the window.  We do this while engines are initializing themselves.\r
730  */\r
731 static int frozen = 0;\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];\r
733 void FreezeUI()\r
734 {\r
735   HMENU hmenu;\r
736   int i;\r
737 \r
738   if (frozen) return;\r
739   frozen = 1;\r
740   hmenu = GetMenu(hwndMain);\r
741   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
742     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
743   }\r
744   DrawMenuBar(hwndMain);\r
745 }\r
746 \r
747 /* Undo a FreezeUI */\r
748 void ThawUI()\r
749 {\r
750   HMENU hmenu;\r
751   int i;\r
752 \r
753   if (!frozen) return;\r
754   frozen = 0;\r
755   hmenu = GetMenu(hwndMain);\r
756   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
757     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
758   }\r
759   DrawMenuBar(hwndMain);\r
760 }\r
761 \r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
763 \r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
765 #ifdef JAWS\r
766 #include "jaws.c"\r
767 #else\r
768 #define JAWS_INIT\r
769 #define JAWS_ARGS\r
770 #define JAWS_ALT_INTERCEPT\r
771 #define JAWS_KBUP_NAVIGATION\r
772 #define JAWS_KBDOWN_NAVIGATION\r
773 #define JAWS_MENU_ITEMS\r
774 #define JAWS_SILENCE\r
775 #define JAWS_REPLAY\r
776 #define JAWS_ACCEL\r
777 #define JAWS_COPYRIGHT\r
778 #define JAWS_DELETE(X) X\r
779 #define SAYMACHINEMOVE()\r
780 #define SAY(X)\r
781 #endif\r
782 \r
783 /*---------------------------------------------------------------------------*\\r
784  *\r
785  * WinMain\r
786  *\r
787 \*---------------------------------------------------------------------------*/\r
788 \r
789 static void HandleMessage P((MSG *message));\r
790 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
791 \r
792 int APIENTRY\r
793 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
794         LPSTR lpCmdLine, int nCmdShow)\r
795 {\r
796   MSG msg;\r
797 //  INITCOMMONCONTROLSEX ex;\r
798 \r
799   debugFP = stderr;\r
800 \r
801   LoadLibrary("RICHED32.DLL");\r
802   consoleCF.cbSize = sizeof(CHARFORMAT);\r
803 \r
804   if (!InitApplication(hInstance)) {\r
805     return (FALSE);\r
806   }\r
807   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
808     return (FALSE);\r
809   }\r
810 \r
811   JAWS_INIT\r
812   TranslateMenus(1);\r
813 \r
814 //  InitCommonControlsEx(&ex);\r
815   InitCommonControls();\r
816 \r
817   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
818   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
819   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
820 \r
821   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
822 \r
823   while (GetMessage(&msg, /* message structure */\r
824                     NULL, /* handle of window receiving the message */\r
825                     0,    /* lowest message to examine */\r
826                     0))   /* highest message to examine */\r
827     {\r
828         HandleMessage(&msg);\r
829     }\r
830 \r
831 \r
832   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
833 }\r
834 \r
835 static void\r
836 HandleMessage (MSG *message)\r
837 {\r
838     MSG msg = *message;\r
839 \r
840       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
841         // [HGM] navigate: switch between all windows with tab\r
842         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
843         int i, currentElement = 0;\r
844 \r
845         // first determine what element of the chain we come from (if any)\r
846         if(appData.icsActive) {\r
847             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
848             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
849         }\r
850         if(engineOutputDialog && EngineOutputIsUp()) {\r
851             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
852             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
853         }\r
854         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
855             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
856         }\r
857         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
858         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
859         if(msg.hwnd == e1)                 currentElement = 2; else\r
860         if(msg.hwnd == e2)                 currentElement = 3; else\r
861         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
862         if(msg.hwnd == mh)                currentElement = 4; else\r
863         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
864         if(msg.hwnd == hText)  currentElement = 5; else\r
865         if(msg.hwnd == hInput) currentElement = 6; else\r
866         for (i = 0; i < N_BUTTONS; i++) {\r
867             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
868         }\r
869 \r
870         // determine where to go to\r
871         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
872           do {\r
873             currentElement = (currentElement + direction) % 7;\r
874             switch(currentElement) {\r
875                 case 0:\r
876                   h = hwndMain; break; // passing this case always makes the loop exit\r
877                 case 1:\r
878                   h = buttonDesc[0].hwnd; break; // could be NULL\r
879                 case 2:\r
880                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
881                   h = e1; break;\r
882                 case 3:\r
883                   if(!EngineOutputIsUp()) continue;\r
884                   h = e2; break;\r
885                 case 4:\r
886                   if(!MoveHistoryIsUp()) continue;\r
887                   h = mh; break;\r
888 //              case 6: // input to eval graph does not seem to get here!\r
889 //                if(!EvalGraphIsUp()) continue;\r
890 //                h = evalGraphDialog; break;\r
891                 case 5:\r
892                   if(!appData.icsActive) continue;\r
893                   SAY("display");\r
894                   h = hText; break;\r
895                 case 6:\r
896                   if(!appData.icsActive) continue;\r
897                   SAY("input");\r
898                   h = hInput; break;\r
899             }\r
900           } while(h == 0);\r
901 \r
902           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
903           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
904           SetFocus(h);\r
905 \r
906           return; // this message now has been processed\r
907         }\r
908       }\r
909 \r
910       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
911           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
912           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
913           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
914           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
915           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
916           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
917           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
918           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
919           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
920         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
921         for(i=0; i<MAX_CHAT; i++) \r
922             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
923                 done = 1; break;\r
924         }\r
925         if(done) return; // [HGM] chat: end patch\r
926         TranslateMessage(&msg); /* Translates virtual key codes */\r
927         DispatchMessage(&msg);  /* Dispatches message to window */\r
928       }\r
929 }\r
930 \r
931 void\r
932 DoEvents ()\r
933 { /* Dispatch pending messages */\r
934   MSG msg;\r
935   while (PeekMessage(&msg, /* message structure */\r
936                      NULL, /* handle of window receiving the message */\r
937                      0,    /* lowest message to examine */\r
938                      0,    /* highest message to examine */\r
939                      PM_REMOVE))\r
940     {\r
941         HandleMessage(&msg);\r
942     }\r
943 }\r
944 \r
945 /*---------------------------------------------------------------------------*\\r
946  *\r
947  * Initialization functions\r
948  *\r
949 \*---------------------------------------------------------------------------*/\r
950 \r
951 void\r
952 SetUserLogo()\r
953 {   // update user logo if necessary\r
954     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
955 \r
956     if(appData.autoLogo) {\r
957           curName = UserName();\r
958           if(strcmp(curName, oldUserName)) {\r
959                 GetCurrentDirectory(MSG_SIZ, dir);\r
960                 SetCurrentDirectory(installDir);\r
961                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
962                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
963                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
964                 if(userLogo == NULL)\r
965                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
966                 SetCurrentDirectory(dir); /* return to prev directory */\r
967           }\r
968     }\r
969 }\r
970 \r
971 BOOL\r
972 InitApplication(HINSTANCE hInstance)\r
973 {\r
974   WNDCLASS wc;\r
975 \r
976   /* Fill in window class structure with parameters that describe the */\r
977   /* main window. */\r
978 \r
979   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
980   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
981   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
982   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
983   wc.hInstance     = hInstance;         /* Owner of this class */\r
984   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
985   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
986   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
987   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
988   wc.lpszClassName = szAppName;                 /* Name to register as */\r
989 \r
990   /* Register the window class and return success/failure code. */\r
991   if (!RegisterClass(&wc)) return FALSE;\r
992 \r
993   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
994   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
995   wc.cbClsExtra    = 0;\r
996   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
997   wc.hInstance     = hInstance;\r
998   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
999   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1000   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1001   wc.lpszMenuName  = NULL;\r
1002   wc.lpszClassName = szConsoleName;\r
1003 \r
1004   if (!RegisterClass(&wc)) return FALSE;\r
1005   return TRUE;\r
1006 }\r
1007 \r
1008 \r
1009 /* Set by InitInstance, used by EnsureOnScreen */\r
1010 int screenHeight, screenWidth;\r
1011 RECT screenGeometry;\r
1012 \r
1013 void\r
1014 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1015 {\r
1016 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1017   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1018   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1019   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1020   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1021   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1022 }\r
1023 \r
1024 VOID\r
1025 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1026 {\r
1027   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1028   GetCurrentDirectory(MSG_SIZ, dir);\r
1029   SetCurrentDirectory(installDir);\r
1030   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1031       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1032 \r
1033       if (cps->programLogo == NULL && appData.debugMode) {\r
1034           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1035       }\r
1036   } else if(appData.autoLogo) {\r
1037       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1038         char *opponent = "";\r
1039         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1040         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1041         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1042         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1043             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1044             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1045         }\r
1046       } else\r
1047       if(appData.directory[n] && appData.directory[n][0]) {\r
1048         SetCurrentDirectory(appData.directory[n]);\r
1049         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1050       }\r
1051   }\r
1052   SetCurrentDirectory(dir); /* return to prev directory */\r
1053 }\r
1054 \r
1055 VOID\r
1056 InitTextures()\r
1057 {\r
1058   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1059   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1060   \r
1061   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1062       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1063       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1064       liteBackTextureMode = appData.liteBackTextureMode;\r
1065 \r
1066       if (liteBackTexture == NULL && appData.debugMode) {\r
1067           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1068       }\r
1069   }\r
1070   \r
1071   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1072       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1073       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1074       darkBackTextureMode = appData.darkBackTextureMode;\r
1075 \r
1076       if (darkBackTexture == NULL && appData.debugMode) {\r
1077           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1078       }\r
1079   }\r
1080 }\r
1081 \r
1082 #ifndef SM_CXVIRTUALSCREEN\r
1083 #define SM_CXVIRTUALSCREEN 78\r
1084 #endif\r
1085 #ifndef SM_CYVIRTUALSCREEN\r
1086 #define SM_CYVIRTUALSCREEN 79\r
1087 #endif\r
1088 #ifndef SM_XVIRTUALSCREEN \r
1089 #define SM_XVIRTUALSCREEN 76\r
1090 #endif\r
1091 #ifndef SM_YVIRTUALSCREEN \r
1092 #define SM_YVIRTUALSCREEN 77\r
1093 #endif\r
1094 \r
1095 VOID\r
1096 InitGeometry()\r
1097 {\r
1098   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1099   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1100   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1101   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1102   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1103   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1104   screenGeometry.right = screenGeometry.left + screenWidth;\r
1105   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1106 }\r
1107 \r
1108 BOOL\r
1109 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1110 {\r
1111   HWND hwnd; /* Main window handle. */\r
1112   int ibs;\r
1113   WINDOWPLACEMENT wp;\r
1114   char *filepart;\r
1115 \r
1116   hInst = hInstance;    /* Store instance handle in our global variable */\r
1117   programName = szAppName;\r
1118 \r
1119   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1120     *filepart = NULLCHAR;\r
1121     SetCurrentDirectory(installDir);\r
1122   } else {\r
1123     GetCurrentDirectory(MSG_SIZ, installDir);\r
1124   }\r
1125   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1126   InitGeometry();\r
1127   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1128   /* xboard, and older WinBoards, controlled the move sound with the\r
1129      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1130      always turn the option on (so that the backend will call us),\r
1131      then let the user turn the sound off by setting it to silence if\r
1132      desired.  To accommodate old winboard.ini files saved by old\r
1133      versions of WinBoard, we also turn off the sound if the option\r
1134      was initially set to false. [HGM] taken out of InitAppData */\r
1135   if (!appData.ringBellAfterMoves) {\r
1136     sounds[(int)SoundMove].name = strdup("");\r
1137     appData.ringBellAfterMoves = TRUE;\r
1138   }\r
1139   if (appData.debugMode) {\r
1140     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1141     setbuf(debugFP, NULL);\r
1142   }\r
1143 \r
1144   LoadLanguageFile(appData.language);\r
1145 \r
1146   InitBackEnd1();\r
1147 \r
1148 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1149 //  InitEngineUCI( installDir, &second );\r
1150 \r
1151   /* Create a main window for this application instance. */\r
1152   hwnd = CreateWindow(szAppName, szTitle,\r
1153                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1154                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1155                       NULL, NULL, hInstance, NULL);\r
1156   hwndMain = hwnd;\r
1157 \r
1158   /* If window could not be created, return "failure" */\r
1159   if (!hwnd) {\r
1160     return (FALSE);\r
1161   }\r
1162 \r
1163   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1164   LoadLogo(&first, 0, FALSE);\r
1165   LoadLogo(&second, 1, appData.icsActive);\r
1166 \r
1167   SetUserLogo();\r
1168 \r
1169   iconWhite = LoadIcon(hInstance, "icon_white");\r
1170   iconBlack = LoadIcon(hInstance, "icon_black");\r
1171   iconCurrent = iconWhite;\r
1172   InitDrawingColors();\r
1173 \r
1174   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1175   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1176     /* Compute window size for each board size, and use the largest\r
1177        size that fits on this screen as the default. */\r
1178     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1179     if (boardSize == (BoardSize)-1 &&\r
1180         winH <= screenHeight\r
1181            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1182         && winW <= screenWidth) {\r
1183       boardSize = (BoardSize)ibs;\r
1184     }\r
1185   }\r
1186 \r
1187   InitDrawingSizes(boardSize, 0);\r
1188   RecentEngineMenu(appData.recentEngineList);\r
1189   InitMenuChecks();\r
1190   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1191 \r
1192   /* [AS] Load textures if specified */\r
1193   InitTextures();\r
1194 \r
1195   mysrandom( (unsigned) time(NULL) );\r
1196 \r
1197   /* [AS] Restore layout */\r
1198   if( wpMoveHistory.visible ) {\r
1199       MoveHistoryPopUp();\r
1200   }\r
1201 \r
1202   if( wpEvalGraph.visible ) {\r
1203       EvalGraphPopUp();\r
1204   }\r
1205 \r
1206   if( wpEngineOutput.visible ) {\r
1207       EngineOutputPopUp();\r
1208   }\r
1209 \r
1210   /* Make the window visible; update its client area; and return "success" */\r
1211   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1212   wp.length = sizeof(WINDOWPLACEMENT);\r
1213   wp.flags = 0;\r
1214   wp.showCmd = nCmdShow;\r
1215   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1216   wp.rcNormalPosition.left = wpMain.x;\r
1217   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1218   wp.rcNormalPosition.top = wpMain.y;\r
1219   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1220   SetWindowPlacement(hwndMain, &wp);\r
1221 \r
1222   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1223 \r
1224   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1225                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1226 \r
1227   if (hwndConsole) {\r
1228 #if AOT_CONSOLE\r
1229     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1230                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1231 #endif\r
1232     ShowWindow(hwndConsole, nCmdShow);\r
1233     SetActiveWindow(hwndConsole);\r
1234   }\r
1235   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1236   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1237 \r
1238   return TRUE;\r
1239 \r
1240 }\r
1241 \r
1242 VOID\r
1243 InitMenuChecks()\r
1244 {\r
1245   HMENU hmenu = GetMenu(hwndMain);\r
1246 \r
1247   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1248                         MF_BYCOMMAND|((appData.icsActive &&\r
1249                                        *appData.icsCommPort != NULLCHAR) ?\r
1250                                       MF_ENABLED : MF_GRAYED));\r
1251   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1252                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1253                                      MF_CHECKED : MF_UNCHECKED));\r
1254   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1255 }\r
1256 \r
1257 //---------------------------------------------------------------------------------------------------------\r
1258 \r
1259 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1260 #define XBOARD FALSE\r
1261 \r
1262 #define OPTCHAR "/"\r
1263 #define SEPCHAR "="\r
1264 #define TOPLEVEL 0\r
1265 \r
1266 #include "args.h"\r
1267 \r
1268 // front-end part of option handling\r
1269 \r
1270 VOID\r
1271 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1272 {\r
1273   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1274   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1275   DeleteDC(hdc);\r
1276   lf->lfWidth = 0;\r
1277   lf->lfEscapement = 0;\r
1278   lf->lfOrientation = 0;\r
1279   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1280   lf->lfItalic = mfp->italic;\r
1281   lf->lfUnderline = mfp->underline;\r
1282   lf->lfStrikeOut = mfp->strikeout;\r
1283   lf->lfCharSet = mfp->charset;\r
1284   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1285 \r
1286 \r
1287   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1288   lf->lfQuality = DEFAULT_QUALITY;\r
1289   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1290     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1291 }\r
1292 \r
1293 void\r
1294 CreateFontInMF(MyFont *mf)\r
1295\r
1296   LFfromMFP(&mf->lf, &mf->mfp);\r
1297   if (mf->hf) DeleteObject(mf->hf);\r
1298   mf->hf = CreateFontIndirect(&mf->lf);\r
1299 }\r
1300 \r
1301 // [HGM] This platform-dependent table provides the location for storing the color info\r
1302 void *\r
1303 colorVariable[] = {\r
1304   &whitePieceColor, \r
1305   &blackPieceColor, \r
1306   &lightSquareColor,\r
1307   &darkSquareColor, \r
1308   &highlightSquareColor,\r
1309   &premoveHighlightColor,\r
1310   NULL,\r
1311   &consoleBackgroundColor,\r
1312   &appData.fontForeColorWhite,\r
1313   &appData.fontBackColorWhite,\r
1314   &appData.fontForeColorBlack,\r
1315   &appData.fontBackColorBlack,\r
1316   &appData.evalHistColorWhite,\r
1317   &appData.evalHistColorBlack,\r
1318   &appData.highlightArrowColor,\r
1319 };\r
1320 \r
1321 /* Command line font name parser.  NULL name means do nothing.\r
1322    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1323    For backward compatibility, syntax without the colon is also\r
1324    accepted, but font names with digits in them won't work in that case.\r
1325 */\r
1326 VOID\r
1327 ParseFontName(char *name, MyFontParams *mfp)\r
1328 {\r
1329   char *p, *q;\r
1330   if (name == NULL) return;\r
1331   p = name;\r
1332   q = strchr(p, ':');\r
1333   if (q) {\r
1334     if (q - p >= sizeof(mfp->faceName))\r
1335       ExitArgError(_("Font name too long:"), name, TRUE);\r
1336     memcpy(mfp->faceName, p, q - p);\r
1337     mfp->faceName[q - p] = NULLCHAR;\r
1338     p = q + 1;\r
1339   } else {\r
1340     q = mfp->faceName;\r
1341 \r
1342     while (*p && !isdigit(*p)) {\r
1343       *q++ = *p++;\r
1344       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1345         ExitArgError(_("Font name too long:"), name, TRUE);\r
1346     }\r
1347     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1348     *q = NULLCHAR;\r
1349   }\r
1350   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1351   mfp->pointSize = (float) atof(p);\r
1352   mfp->bold = (strchr(p, 'b') != NULL);\r
1353   mfp->italic = (strchr(p, 'i') != NULL);\r
1354   mfp->underline = (strchr(p, 'u') != NULL);\r
1355   mfp->strikeout = (strchr(p, 's') != NULL);\r
1356   mfp->charset = DEFAULT_CHARSET;\r
1357   q = strchr(p, 'c');\r
1358   if (q)\r
1359     mfp->charset = (BYTE) atoi(q+1);\r
1360 }\r
1361 \r
1362 void\r
1363 ParseFont(char *name, int number)\r
1364 { // wrapper to shield back-end from 'font'\r
1365   ParseFontName(name, &font[boardSize][number]->mfp);\r
1366 }\r
1367 \r
1368 void\r
1369 SetFontDefaults()\r
1370 { // in WB  we have a 2D array of fonts; this initializes their description\r
1371   int i, j;\r
1372   /* Point font array elements to structures and\r
1373      parse default font names */\r
1374   for (i=0; i<NUM_FONTS; i++) {\r
1375     for (j=0; j<NUM_SIZES; j++) {\r
1376       font[j][i] = &fontRec[j][i];\r
1377       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1378     }\r
1379   }\r
1380 }\r
1381 \r
1382 void\r
1383 CreateFonts()\r
1384 { // here we create the actual fonts from the selected descriptions\r
1385   int i, j;\r
1386   for (i=0; i<NUM_FONTS; i++) {\r
1387     for (j=0; j<NUM_SIZES; j++) {\r
1388       CreateFontInMF(font[j][i]);\r
1389     }\r
1390   }\r
1391 }\r
1392 /* Color name parser.\r
1393    X version accepts X color names, but this one\r
1394    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1395 COLORREF\r
1396 ParseColorName(char *name)\r
1397 {\r
1398   int red, green, blue, count;\r
1399   char buf[MSG_SIZ];\r
1400 \r
1401   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1402   if (count != 3) {\r
1403     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1404       &red, &green, &blue);\r
1405   }\r
1406   if (count != 3) {\r
1407     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1408     DisplayError(buf, 0);\r
1409     return RGB(0, 0, 0);\r
1410   }\r
1411   return PALETTERGB(red, green, blue);\r
1412 }\r
1413 \r
1414 void\r
1415 ParseColor(int n, char *name)\r
1416 { // for WinBoard the color is an int, which needs to be derived from the string\r
1417   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1418 }\r
1419 \r
1420 void\r
1421 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1422 {\r
1423   char *e = argValue;\r
1424   int eff = 0;\r
1425 \r
1426   while (*e) {\r
1427     if (*e == 'b')      eff |= CFE_BOLD;\r
1428     else if (*e == 'i') eff |= CFE_ITALIC;\r
1429     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1430     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1431     else if (*e == '#' || isdigit(*e)) break;\r
1432     e++;\r
1433   }\r
1434   *effects = eff;\r
1435   *color   = ParseColorName(e);\r
1436 }\r
1437 \r
1438 void\r
1439 ParseTextAttribs(ColorClass cc, char *s)\r
1440 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1441     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1442     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1443 }\r
1444 \r
1445 void\r
1446 ParseBoardSize(void *addr, char *name)\r
1447 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1448   BoardSize bs = SizeTiny;\r
1449   while (sizeInfo[bs].name != NULL) {\r
1450     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1451         *(BoardSize *)addr = bs;\r
1452         return;\r
1453     }\r
1454     bs++;\r
1455   }\r
1456   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1457 }\r
1458 \r
1459 void\r
1460 LoadAllSounds()\r
1461 { // [HGM] import name from appData first\r
1462   ColorClass cc;\r
1463   SoundClass sc;\r
1464   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1465     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1466     textAttribs[cc].sound.data = NULL;\r
1467     MyLoadSound(&textAttribs[cc].sound);\r
1468   }\r
1469   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1470     textAttribs[cc].sound.name = strdup("");\r
1471     textAttribs[cc].sound.data = NULL;\r
1472   }\r
1473   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1474     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1475     sounds[sc].data = NULL;\r
1476     MyLoadSound(&sounds[sc]);\r
1477   }\r
1478 }\r
1479 \r
1480 void\r
1481 SetCommPortDefaults()\r
1482 {\r
1483    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1484   dcb.DCBlength = sizeof(DCB);\r
1485   dcb.BaudRate = 9600;\r
1486   dcb.fBinary = TRUE;\r
1487   dcb.fParity = FALSE;\r
1488   dcb.fOutxCtsFlow = FALSE;\r
1489   dcb.fOutxDsrFlow = FALSE;\r
1490   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1491   dcb.fDsrSensitivity = FALSE;\r
1492   dcb.fTXContinueOnXoff = TRUE;\r
1493   dcb.fOutX = FALSE;\r
1494   dcb.fInX = FALSE;\r
1495   dcb.fNull = FALSE;\r
1496   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1497   dcb.fAbortOnError = FALSE;\r
1498   dcb.ByteSize = 7;\r
1499   dcb.Parity = SPACEPARITY;\r
1500   dcb.StopBits = ONESTOPBIT;\r
1501 }\r
1502 \r
1503 // [HGM] args: these three cases taken out to stay in front-end\r
1504 void\r
1505 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1506 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1507         // while the curent board size determines the element. This system should be ported to XBoard.\r
1508         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1509         int bs;\r
1510         for (bs=0; bs<NUM_SIZES; bs++) {\r
1511           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1512           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1513           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1514             ad->argName, mfp->faceName, mfp->pointSize,\r
1515             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1516             mfp->bold ? "b" : "",\r
1517             mfp->italic ? "i" : "",\r
1518             mfp->underline ? "u" : "",\r
1519             mfp->strikeout ? "s" : "",\r
1520             (int)mfp->charset);\r
1521         }\r
1522       }\r
1523 \r
1524 void\r
1525 ExportSounds()\r
1526 { // [HGM] copy the names from the internal WB variables to appData\r
1527   ColorClass cc;\r
1528   SoundClass sc;\r
1529   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1530     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1531   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1532     (&appData.soundMove)[sc] = sounds[sc].name;\r
1533 }\r
1534 \r
1535 void\r
1536 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1537 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1538         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1539         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1540           (ta->effects & CFE_BOLD) ? "b" : "",\r
1541           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1542           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1543           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1544           (ta->effects) ? " " : "",\r
1545           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1546       }\r
1547 \r
1548 void\r
1549 SaveColor(FILE *f, ArgDescriptor *ad)\r
1550 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1551         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1552         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1553           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1554 }\r
1555 \r
1556 void\r
1557 SaveBoardSize(FILE *f, char *name, void *addr)\r
1558 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1559   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1560 }\r
1561 \r
1562 void\r
1563 ParseCommPortSettings(char *s)\r
1564 { // wrapper to keep dcb from back-end\r
1565   ParseCommSettings(s, &dcb);\r
1566 }\r
1567 \r
1568 void\r
1569 GetWindowCoords()\r
1570 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1571   GetActualPlacement(hwndMain, &wpMain);\r
1572   GetActualPlacement(hwndConsole, &wpConsole);\r
1573   GetActualPlacement(commentDialog, &wpComment);\r
1574   GetActualPlacement(editTagsDialog, &wpTags);\r
1575   GetActualPlacement(gameListDialog, &wpGameList);\r
1576   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1577   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1578   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1579 }\r
1580 \r
1581 void\r
1582 PrintCommPortSettings(FILE *f, char *name)\r
1583 { // wrapper to shield back-end from DCB\r
1584       PrintCommSettings(f, name, &dcb);\r
1585 }\r
1586 \r
1587 int\r
1588 MySearchPath(char *installDir, char *name, char *fullname)\r
1589 {\r
1590   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1591   if(name[0]== '%') {\r
1592     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1593     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1594       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1595       *strchr(buf, '%') = 0;\r
1596       strcat(fullname, getenv(buf));\r
1597       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1598     }\r
1599     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1600     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1601     return (int) strlen(fullname);\r
1602   }\r
1603   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1604 }\r
1605 \r
1606 int\r
1607 MyGetFullPathName(char *name, char *fullname)\r
1608 {\r
1609   char *dummy;\r
1610   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1611 }\r
1612 \r
1613 int\r
1614 MainWindowUp()\r
1615 { // [HGM] args: allows testing if main window is realized from back-end\r
1616   return hwndMain != NULL;\r
1617 }\r
1618 \r
1619 void\r
1620 PopUpStartupDialog()\r
1621 {\r
1622     FARPROC lpProc;\r
1623     \r
1624     LoadLanguageFile(appData.language);\r
1625     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1626     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1627     FreeProcInstance(lpProc);\r
1628 }\r
1629 \r
1630 /*---------------------------------------------------------------------------*\\r
1631  *\r
1632  * GDI board drawing routines\r
1633  *\r
1634 \*---------------------------------------------------------------------------*/\r
1635 \r
1636 /* [AS] Draw square using background texture */\r
1637 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1638 {\r
1639     XFORM   x;\r
1640 \r
1641     if( mode == 0 ) {\r
1642         return; /* Should never happen! */\r
1643     }\r
1644 \r
1645     SetGraphicsMode( dst, GM_ADVANCED );\r
1646 \r
1647     switch( mode ) {\r
1648     case 1:\r
1649         /* Identity */\r
1650         break;\r
1651     case 2:\r
1652         /* X reflection */\r
1653         x.eM11 = -1.0;\r
1654         x.eM12 = 0;\r
1655         x.eM21 = 0;\r
1656         x.eM22 = 1.0;\r
1657         x.eDx = (FLOAT) dw + dx - 1;\r
1658         x.eDy = 0;\r
1659         dx = 0;\r
1660         SetWorldTransform( dst, &x );\r
1661         break;\r
1662     case 3:\r
1663         /* Y reflection */\r
1664         x.eM11 = 1.0;\r
1665         x.eM12 = 0;\r
1666         x.eM21 = 0;\r
1667         x.eM22 = -1.0;\r
1668         x.eDx = 0;\r
1669         x.eDy = (FLOAT) dh + dy - 1;\r
1670         dy = 0;\r
1671         SetWorldTransform( dst, &x );\r
1672         break;\r
1673     case 4:\r
1674         /* X/Y flip */\r
1675         x.eM11 = 0;\r
1676         x.eM12 = 1.0;\r
1677         x.eM21 = 1.0;\r
1678         x.eM22 = 0;\r
1679         x.eDx = (FLOAT) dx;\r
1680         x.eDy = (FLOAT) dy;\r
1681         dx = 0;\r
1682         dy = 0;\r
1683         SetWorldTransform( dst, &x );\r
1684         break;\r
1685     }\r
1686 \r
1687     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1688 \r
1689     x.eM11 = 1.0;\r
1690     x.eM12 = 0;\r
1691     x.eM21 = 0;\r
1692     x.eM22 = 1.0;\r
1693     x.eDx = 0;\r
1694     x.eDy = 0;\r
1695     SetWorldTransform( dst, &x );\r
1696 \r
1697     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1698 }\r
1699 \r
1700 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1701 enum {\r
1702     PM_WP = (int) WhitePawn, \r
1703     PM_WN = (int) WhiteKnight, \r
1704     PM_WB = (int) WhiteBishop, \r
1705     PM_WR = (int) WhiteRook, \r
1706     PM_WQ = (int) WhiteQueen, \r
1707     PM_WF = (int) WhiteFerz, \r
1708     PM_WW = (int) WhiteWazir, \r
1709     PM_WE = (int) WhiteAlfil, \r
1710     PM_WM = (int) WhiteMan, \r
1711     PM_WO = (int) WhiteCannon, \r
1712     PM_WU = (int) WhiteUnicorn, \r
1713     PM_WH = (int) WhiteNightrider, \r
1714     PM_WA = (int) WhiteAngel, \r
1715     PM_WC = (int) WhiteMarshall, \r
1716     PM_WAB = (int) WhiteCardinal, \r
1717     PM_WD = (int) WhiteDragon, \r
1718     PM_WL = (int) WhiteLance, \r
1719     PM_WS = (int) WhiteCobra, \r
1720     PM_WV = (int) WhiteFalcon, \r
1721     PM_WSG = (int) WhiteSilver, \r
1722     PM_WG = (int) WhiteGrasshopper, \r
1723     PM_WK = (int) WhiteKing,\r
1724     PM_BP = (int) BlackPawn, \r
1725     PM_BN = (int) BlackKnight, \r
1726     PM_BB = (int) BlackBishop, \r
1727     PM_BR = (int) BlackRook, \r
1728     PM_BQ = (int) BlackQueen, \r
1729     PM_BF = (int) BlackFerz, \r
1730     PM_BW = (int) BlackWazir, \r
1731     PM_BE = (int) BlackAlfil, \r
1732     PM_BM = (int) BlackMan,\r
1733     PM_BO = (int) BlackCannon, \r
1734     PM_BU = (int) BlackUnicorn, \r
1735     PM_BH = (int) BlackNightrider, \r
1736     PM_BA = (int) BlackAngel, \r
1737     PM_BC = (int) BlackMarshall, \r
1738     PM_BG = (int) BlackGrasshopper, \r
1739     PM_BAB = (int) BlackCardinal,\r
1740     PM_BD = (int) BlackDragon,\r
1741     PM_BL = (int) BlackLance,\r
1742     PM_BS = (int) BlackCobra,\r
1743     PM_BV = (int) BlackFalcon,\r
1744     PM_BSG = (int) BlackSilver,\r
1745     PM_BK = (int) BlackKing\r
1746 };\r
1747 \r
1748 static HFONT hPieceFont = NULL;\r
1749 static HBITMAP hPieceMask[(int) EmptySquare];\r
1750 static HBITMAP hPieceFace[(int) EmptySquare];\r
1751 static int fontBitmapSquareSize = 0;\r
1752 static char pieceToFontChar[(int) EmptySquare] =\r
1753                               { 'p', 'n', 'b', 'r', 'q', \r
1754                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1755                       'k', 'o', 'm', 'v', 't', 'w', \r
1756                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1757                                                               'l' };\r
1758 \r
1759 extern BOOL SetCharTable( char *table, const char * map );\r
1760 /* [HGM] moved to backend.c */\r
1761 \r
1762 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1763 {\r
1764     HBRUSH hbrush;\r
1765     BYTE r1 = GetRValue( color );\r
1766     BYTE g1 = GetGValue( color );\r
1767     BYTE b1 = GetBValue( color );\r
1768     BYTE r2 = r1 / 2;\r
1769     BYTE g2 = g1 / 2;\r
1770     BYTE b2 = b1 / 2;\r
1771     RECT rc;\r
1772 \r
1773     /* Create a uniform background first */\r
1774     hbrush = CreateSolidBrush( color );\r
1775     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1776     FillRect( hdc, &rc, hbrush );\r
1777     DeleteObject( hbrush );\r
1778     \r
1779     if( mode == 1 ) {\r
1780         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1781         int steps = squareSize / 2;\r
1782         int i;\r
1783 \r
1784         for( i=0; i<steps; i++ ) {\r
1785             BYTE r = r1 - (r1-r2) * i / steps;\r
1786             BYTE g = g1 - (g1-g2) * i / steps;\r
1787             BYTE b = b1 - (b1-b2) * i / steps;\r
1788 \r
1789             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1790             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1791             FillRect( hdc, &rc, hbrush );\r
1792             DeleteObject(hbrush);\r
1793         }\r
1794     }\r
1795     else if( mode == 2 ) {\r
1796         /* Diagonal gradient, good more or less for every piece */\r
1797         POINT triangle[3];\r
1798         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1799         HBRUSH hbrush_old;\r
1800         int steps = squareSize;\r
1801         int i;\r
1802 \r
1803         triangle[0].x = squareSize - steps;\r
1804         triangle[0].y = squareSize;\r
1805         triangle[1].x = squareSize;\r
1806         triangle[1].y = squareSize;\r
1807         triangle[2].x = squareSize;\r
1808         triangle[2].y = squareSize - steps;\r
1809 \r
1810         for( i=0; i<steps; i++ ) {\r
1811             BYTE r = r1 - (r1-r2) * i / steps;\r
1812             BYTE g = g1 - (g1-g2) * i / steps;\r
1813             BYTE b = b1 - (b1-b2) * i / steps;\r
1814 \r
1815             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1816             hbrush_old = SelectObject( hdc, hbrush );\r
1817             Polygon( hdc, triangle, 3 );\r
1818             SelectObject( hdc, hbrush_old );\r
1819             DeleteObject(hbrush);\r
1820             triangle[0].x++;\r
1821             triangle[2].y++;\r
1822         }\r
1823 \r
1824         SelectObject( hdc, hpen );\r
1825     }\r
1826 }\r
1827 \r
1828 /*\r
1829     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1830     seems to work ok. The main problem here is to find the "inside" of a chess\r
1831     piece: follow the steps as explained below.\r
1832 */\r
1833 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1834 {\r
1835     HBITMAP hbm;\r
1836     HBITMAP hbm_old;\r
1837     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1838     RECT rc;\r
1839     SIZE sz;\r
1840 \r
1841 \r
1842     POINT pt;\r
1843     int backColor = whitePieceColor; \r
1844     int foreColor = blackPieceColor;\r
1845     \r
1846     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1847         backColor = appData.fontBackColorWhite;\r
1848         foreColor = appData.fontForeColorWhite;\r
1849     }\r
1850     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1851         backColor = appData.fontBackColorBlack;\r
1852         foreColor = appData.fontForeColorBlack;\r
1853     }\r
1854 \r
1855     /* Mask */\r
1856     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1857 \r
1858     hbm_old = SelectObject( hdc, hbm );\r
1859 \r
1860     rc.left = 0;\r
1861     rc.top = 0;\r
1862     rc.right = squareSize;\r
1863     rc.bottom = squareSize;\r
1864 \r
1865     /* Step 1: background is now black */\r
1866     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1867 \r
1868     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1869 \r
1870     pt.x = (squareSize - sz.cx) / 2;\r
1871     pt.y = (squareSize - sz.cy) / 2;\r
1872 \r
1873     SetBkMode( hdc, TRANSPARENT );\r
1874     SetTextColor( hdc, chroma );\r
1875     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1876     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1877 \r
1878     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1879     /* Step 3: the area outside the piece is filled with white */\r
1880 //    FloodFill( hdc, 0, 0, chroma );\r
1881     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1882     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1883     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1884     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1885     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1886     /* \r
1887         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1888         but if the start point is not inside the piece we're lost!\r
1889         There should be a better way to do this... if we could create a region or path\r
1890         from the fill operation we would be fine for example.\r
1891     */\r
1892 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1893     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1894 \r
1895     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1896         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1897         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1898 \r
1899         SelectObject( dc2, bm2 );\r
1900         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1901         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1902         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1903         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1904         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1905 \r
1906         DeleteDC( dc2 );\r
1907         DeleteObject( bm2 );\r
1908     }\r
1909 \r
1910     SetTextColor( hdc, 0 );\r
1911     /* \r
1912         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1913         draw the piece again in black for safety.\r
1914     */\r
1915     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1916 \r
1917     SelectObject( hdc, hbm_old );\r
1918 \r
1919     if( hPieceMask[index] != NULL ) {\r
1920         DeleteObject( hPieceMask[index] );\r
1921     }\r
1922 \r
1923     hPieceMask[index] = hbm;\r
1924 \r
1925     /* Face */\r
1926     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1927 \r
1928     SelectObject( hdc, hbm );\r
1929 \r
1930     {\r
1931         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1932         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1933         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1934 \r
1935         SelectObject( dc1, hPieceMask[index] );\r
1936         SelectObject( dc2, bm2 );\r
1937         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1938         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1939         \r
1940         /* \r
1941             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1942             the piece background and deletes (makes transparent) the rest.\r
1943             Thanks to that mask, we are free to paint the background with the greates\r
1944             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1945             We use this, to make gradients and give the pieces a "roundish" look.\r
1946         */\r
1947         SetPieceBackground( hdc, backColor, 2 );\r
1948         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1949 \r
1950         DeleteDC( dc2 );\r
1951         DeleteDC( dc1 );\r
1952         DeleteObject( bm2 );\r
1953     }\r
1954 \r
1955     SetTextColor( hdc, foreColor );\r
1956     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1957 \r
1958     SelectObject( hdc, hbm_old );\r
1959 \r
1960     if( hPieceFace[index] != NULL ) {\r
1961         DeleteObject( hPieceFace[index] );\r
1962     }\r
1963 \r
1964     hPieceFace[index] = hbm;\r
1965 }\r
1966 \r
1967 static int TranslatePieceToFontPiece( int piece )\r
1968 {\r
1969     switch( piece ) {\r
1970     case BlackPawn:\r
1971         return PM_BP;\r
1972     case BlackKnight:\r
1973         return PM_BN;\r
1974     case BlackBishop:\r
1975         return PM_BB;\r
1976     case BlackRook:\r
1977         return PM_BR;\r
1978     case BlackQueen:\r
1979         return PM_BQ;\r
1980     case BlackKing:\r
1981         return PM_BK;\r
1982     case WhitePawn:\r
1983         return PM_WP;\r
1984     case WhiteKnight:\r
1985         return PM_WN;\r
1986     case WhiteBishop:\r
1987         return PM_WB;\r
1988     case WhiteRook:\r
1989         return PM_WR;\r
1990     case WhiteQueen:\r
1991         return PM_WQ;\r
1992     case WhiteKing:\r
1993         return PM_WK;\r
1994 \r
1995     case BlackAngel:\r
1996         return PM_BA;\r
1997     case BlackMarshall:\r
1998         return PM_BC;\r
1999     case BlackFerz:\r
2000         return PM_BF;\r
2001     case BlackNightrider:\r
2002         return PM_BH;\r
2003     case BlackAlfil:\r
2004         return PM_BE;\r
2005     case BlackWazir:\r
2006         return PM_BW;\r
2007     case BlackUnicorn:\r
2008         return PM_BU;\r
2009     case BlackCannon:\r
2010         return PM_BO;\r
2011     case BlackGrasshopper:\r
2012         return PM_BG;\r
2013     case BlackMan:\r
2014         return PM_BM;\r
2015     case BlackSilver:\r
2016         return PM_BSG;\r
2017     case BlackLance:\r
2018         return PM_BL;\r
2019     case BlackFalcon:\r
2020         return PM_BV;\r
2021     case BlackCobra:\r
2022         return PM_BS;\r
2023     case BlackCardinal:\r
2024         return PM_BAB;\r
2025     case BlackDragon:\r
2026         return PM_BD;\r
2027 \r
2028     case WhiteAngel:\r
2029         return PM_WA;\r
2030     case WhiteMarshall:\r
2031         return PM_WC;\r
2032     case WhiteFerz:\r
2033         return PM_WF;\r
2034     case WhiteNightrider:\r
2035         return PM_WH;\r
2036     case WhiteAlfil:\r
2037         return PM_WE;\r
2038     case WhiteWazir:\r
2039         return PM_WW;\r
2040     case WhiteUnicorn:\r
2041         return PM_WU;\r
2042     case WhiteCannon:\r
2043         return PM_WO;\r
2044     case WhiteGrasshopper:\r
2045         return PM_WG;\r
2046     case WhiteMan:\r
2047         return PM_WM;\r
2048     case WhiteSilver:\r
2049         return PM_WSG;\r
2050     case WhiteLance:\r
2051         return PM_WL;\r
2052     case WhiteFalcon:\r
2053         return PM_WV;\r
2054     case WhiteCobra:\r
2055         return PM_WS;\r
2056     case WhiteCardinal:\r
2057         return PM_WAB;\r
2058     case WhiteDragon:\r
2059         return PM_WD;\r
2060     }\r
2061 \r
2062     return 0;\r
2063 }\r
2064 \r
2065 void CreatePiecesFromFont()\r
2066 {\r
2067     LOGFONT lf;\r
2068     HDC hdc_window = NULL;\r
2069     HDC hdc = NULL;\r
2070     HFONT hfont_old;\r
2071     int fontHeight;\r
2072     int i;\r
2073 \r
2074     if( fontBitmapSquareSize < 0 ) {\r
2075         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2076         return;\r
2077     }\r
2078 \r
2079     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2080             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2081         fontBitmapSquareSize = -1;\r
2082         return;\r
2083     }\r
2084 \r
2085     if( fontBitmapSquareSize != squareSize ) {\r
2086         hdc_window = GetDC( hwndMain );\r
2087         hdc = CreateCompatibleDC( hdc_window );\r
2088 \r
2089         if( hPieceFont != NULL ) {\r
2090             DeleteObject( hPieceFont );\r
2091         }\r
2092         else {\r
2093             for( i=0; i<=(int)BlackKing; i++ ) {\r
2094                 hPieceMask[i] = NULL;\r
2095                 hPieceFace[i] = NULL;\r
2096             }\r
2097         }\r
2098 \r
2099         fontHeight = 75;\r
2100 \r
2101         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2102             fontHeight = appData.fontPieceSize;\r
2103         }\r
2104 \r
2105         fontHeight = (fontHeight * squareSize) / 100;\r
2106 \r
2107         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2108         lf.lfWidth = 0;\r
2109         lf.lfEscapement = 0;\r
2110         lf.lfOrientation = 0;\r
2111         lf.lfWeight = FW_NORMAL;\r
2112         lf.lfItalic = 0;\r
2113         lf.lfUnderline = 0;\r
2114         lf.lfStrikeOut = 0;\r
2115         lf.lfCharSet = DEFAULT_CHARSET;\r
2116         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2117         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2118         lf.lfQuality = PROOF_QUALITY;\r
2119         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2120         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2121         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2122 \r
2123         hPieceFont = CreateFontIndirect( &lf );\r
2124 \r
2125         if( hPieceFont == NULL ) {\r
2126             fontBitmapSquareSize = -2;\r
2127         }\r
2128         else {\r
2129             /* Setup font-to-piece character table */\r
2130             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2131                 /* No (or wrong) global settings, try to detect the font */\r
2132                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2133                     /* Alpha */\r
2134                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2135                 }\r
2136                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2137                     /* DiagramTT* family */\r
2138                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2139                 }\r
2140                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2141                     /* Fairy symbols */\r
2142                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2143                 }\r
2144                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2145                     /* Good Companion (Some characters get warped as literal :-( */\r
2146                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2147                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2148                     SetCharTable(pieceToFontChar, s);\r
2149                 }\r
2150                 else {\r
2151                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2152                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2153                 }\r
2154             }\r
2155 \r
2156             /* Create bitmaps */\r
2157             hfont_old = SelectObject( hdc, hPieceFont );\r
2158             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2159                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2160                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2161 \r
2162             SelectObject( hdc, hfont_old );\r
2163 \r
2164             fontBitmapSquareSize = squareSize;\r
2165         }\r
2166     }\r
2167 \r
2168     if( hdc != NULL ) {\r
2169         DeleteDC( hdc );\r
2170     }\r
2171 \r
2172     if( hdc_window != NULL ) {\r
2173         ReleaseDC( hwndMain, hdc_window );\r
2174     }\r
2175 }\r
2176 \r
2177 HBITMAP\r
2178 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2179 {\r
2180   char name[128], buf[MSG_SIZ];\r
2181 \r
2182     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2183   if(appData.pieceDirectory[0]) {\r
2184     HBITMAP res;\r
2185     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2186     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2187     if(res) return res;\r
2188   }\r
2189   if (gameInfo.event &&\r
2190       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2191       strcmp(name, "k80s") == 0) {\r
2192     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2193   }\r
2194   return LoadBitmap(hinst, name);\r
2195 }\r
2196 \r
2197 \r
2198 /* Insert a color into the program's logical palette\r
2199    structure.  This code assumes the given color is\r
2200    the result of the RGB or PALETTERGB macro, and it\r
2201    knows how those macros work (which is documented).\r
2202 */\r
2203 VOID\r
2204 InsertInPalette(COLORREF color)\r
2205 {\r
2206   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2207 \r
2208   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2209     DisplayFatalError(_("Too many colors"), 0, 1);\r
2210     pLogPal->palNumEntries--;\r
2211     return;\r
2212   }\r
2213 \r
2214   pe->peFlags = (char) 0;\r
2215   pe->peRed = (char) (0xFF & color);\r
2216   pe->peGreen = (char) (0xFF & (color >> 8));\r
2217   pe->peBlue = (char) (0xFF & (color >> 16));\r
2218   return;\r
2219 }\r
2220 \r
2221 \r
2222 VOID\r
2223 InitDrawingColors()\r
2224 {\r
2225   int i;\r
2226   if (pLogPal == NULL) {\r
2227     /* Allocate enough memory for a logical palette with\r
2228      * PALETTESIZE entries and set the size and version fields\r
2229      * of the logical palette structure.\r
2230      */\r
2231     pLogPal = (NPLOGPALETTE)\r
2232       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2233                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2234     pLogPal->palVersion    = 0x300;\r
2235   }\r
2236   pLogPal->palNumEntries = 0;\r
2237 \r
2238   InsertInPalette(lightSquareColor);\r
2239   InsertInPalette(darkSquareColor);\r
2240   InsertInPalette(whitePieceColor);\r
2241   InsertInPalette(blackPieceColor);\r
2242   InsertInPalette(highlightSquareColor);\r
2243   InsertInPalette(premoveHighlightColor);\r
2244 \r
2245   /*  create a logical color palette according the information\r
2246    *  in the LOGPALETTE structure.\r
2247    */\r
2248   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2249 \r
2250   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2251   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2252   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2253   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2254   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2255   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2256   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2257     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2258 \r
2259    /* [AS] Force rendering of the font-based pieces */\r
2260   if( fontBitmapSquareSize > 0 ) {\r
2261     fontBitmapSquareSize = 0;\r
2262   }\r
2263 }\r
2264 \r
2265 \r
2266 int\r
2267 BoardWidth(int boardSize, int n)\r
2268 { /* [HGM] argument n added to allow different width and height */\r
2269   int lineGap = sizeInfo[boardSize].lineGap;\r
2270 \r
2271   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2272       lineGap = appData.overrideLineGap;\r
2273   }\r
2274 \r
2275   return (n + 1) * lineGap +\r
2276           n * sizeInfo[boardSize].squareSize;\r
2277 }\r
2278 \r
2279 /* Respond to board resize by dragging edge */\r
2280 VOID\r
2281 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2282 {\r
2283   BoardSize newSize = NUM_SIZES - 1;\r
2284   static int recurse = 0;\r
2285   if (IsIconic(hwndMain)) return;\r
2286   if (recurse > 0) return;\r
2287   recurse++;\r
2288   while (newSize > 0) {\r
2289         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2290         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2291            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2292     newSize--;\r
2293   } \r
2294   boardSize = newSize;\r
2295   InitDrawingSizes(boardSize, flags);\r
2296   recurse--;\r
2297 }\r
2298 \r
2299 \r
2300 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2301 \r
2302 VOID\r
2303 InitDrawingSizes(BoardSize boardSize, int flags)\r
2304 {\r
2305   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2306   ChessSquare piece;\r
2307   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2308   HDC hdc;\r
2309   SIZE clockSize, messageSize;\r
2310   HFONT oldFont;\r
2311   char buf[MSG_SIZ];\r
2312   char *str;\r
2313   HMENU hmenu = GetMenu(hwndMain);\r
2314   RECT crect, wrect, oldRect;\r
2315   int offby;\r
2316   LOGBRUSH logbrush;\r
2317   VariantClass v = gameInfo.variant;\r
2318 \r
2319   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2320   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2321 \r
2322   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2323   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2324   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2325   oldBoardSize = boardSize;\r
2326 \r
2327   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2328   { // correct board size to one where built-in pieces exist\r
2329     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2330        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2331 \r
2332       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2333       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2334       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2335       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2336       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2337                                    boardSize = SizeMiddling;\r
2338     }\r
2339   }\r
2340   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2341 \r
2342   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2343   oldRect.top = wpMain.y;\r
2344   oldRect.right = wpMain.x + wpMain.width;\r
2345   oldRect.bottom = wpMain.y + wpMain.height;\r
2346 \r
2347   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2348   smallLayout = sizeInfo[boardSize].smallLayout;\r
2349   squareSize = sizeInfo[boardSize].squareSize;\r
2350   lineGap = sizeInfo[boardSize].lineGap;\r
2351   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2352   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2353 \r
2354   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2355       lineGap = appData.overrideLineGap;\r
2356   }\r
2357 \r
2358   if (tinyLayout != oldTinyLayout) {\r
2359     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2360     if (tinyLayout) {\r
2361       style &= ~WS_SYSMENU;\r
2362       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2363                  "&Minimize\tCtrl+F4");\r
2364     } else {\r
2365       style |= WS_SYSMENU;\r
2366       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2367     }\r
2368     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2369 \r
2370     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2371       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2372         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2373     }\r
2374     DrawMenuBar(hwndMain);\r
2375   }\r
2376 \r
2377   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2378   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2379 \r
2380   /* Get text area sizes */\r
2381   hdc = GetDC(hwndMain);\r
2382   if (appData.clockMode) {\r
2383     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2384   } else {\r
2385     snprintf(buf, MSG_SIZ, _("White"));\r
2386   }\r
2387   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2388   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2389   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2390   str = _("We only care about the height here");\r
2391   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2392   SelectObject(hdc, oldFont);\r
2393   ReleaseDC(hwndMain, hdc);\r
2394 \r
2395   /* Compute where everything goes */\r
2396   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2397         /* [HGM] logo: if either logo is on, reserve space for it */\r
2398         logoHeight =  2*clockSize.cy;\r
2399         leftLogoRect.left   = OUTER_MARGIN;\r
2400         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2401         leftLogoRect.top    = OUTER_MARGIN;\r
2402         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2403 \r
2404         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2405         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2406         rightLogoRect.top    = OUTER_MARGIN;\r
2407         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2408 \r
2409 \r
2410     whiteRect.left = leftLogoRect.right;\r
2411     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2412     whiteRect.top = OUTER_MARGIN;\r
2413     whiteRect.bottom = whiteRect.top + logoHeight;\r
2414 \r
2415     blackRect.right = rightLogoRect.left;\r
2416     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2417     blackRect.top = whiteRect.top;\r
2418     blackRect.bottom = whiteRect.bottom;\r
2419   } else {\r
2420     whiteRect.left = OUTER_MARGIN;\r
2421     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2422     whiteRect.top = OUTER_MARGIN;\r
2423     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2424 \r
2425     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2426     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2427     blackRect.top = whiteRect.top;\r
2428     blackRect.bottom = whiteRect.bottom;\r
2429 \r
2430     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2431   }\r
2432 \r
2433   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2434   if (appData.showButtonBar) {\r
2435     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2436       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2437   } else {\r
2438     messageRect.right = OUTER_MARGIN + boardWidth;\r
2439   }\r
2440   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2441   messageRect.bottom = messageRect.top + messageSize.cy;\r
2442 \r
2443   boardRect.left = OUTER_MARGIN;\r
2444   boardRect.right = boardRect.left + boardWidth;\r
2445   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2446   boardRect.bottom = boardRect.top + boardHeight;\r
2447 \r
2448   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2449   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2450   oldTinyLayout = tinyLayout;\r
2451   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2452   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2453     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2454   winW *= 1 + twoBoards;\r
2455   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2456   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2457   wpMain.height = winH; //       without disturbing window attachments\r
2458   GetWindowRect(hwndMain, &wrect);\r
2459   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2460                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2461 \r
2462   // [HGM] placement: let attached windows follow size change.\r
2463   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2464   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2465   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2466   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2467   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2468 \r
2469   /* compensate if menu bar wrapped */\r
2470   GetClientRect(hwndMain, &crect);\r
2471   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2472   wpMain.height += offby;\r
2473   switch (flags) {\r
2474   case WMSZ_TOPLEFT:\r
2475     SetWindowPos(hwndMain, NULL, \r
2476                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2477                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2478     break;\r
2479 \r
2480   case WMSZ_TOPRIGHT:\r
2481   case WMSZ_TOP:\r
2482     SetWindowPos(hwndMain, NULL, \r
2483                  wrect.left, wrect.bottom - wpMain.height, \r
2484                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2485     break;\r
2486 \r
2487   case WMSZ_BOTTOMLEFT:\r
2488   case WMSZ_LEFT:\r
2489     SetWindowPos(hwndMain, NULL, \r
2490                  wrect.right - wpMain.width, wrect.top, \r
2491                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2492     break;\r
2493 \r
2494   case WMSZ_BOTTOMRIGHT:\r
2495   case WMSZ_BOTTOM:\r
2496   case WMSZ_RIGHT:\r
2497   default:\r
2498     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2499                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2500     break;\r
2501   }\r
2502 \r
2503   hwndPause = NULL;\r
2504   for (i = 0; i < N_BUTTONS; i++) {\r
2505     if (buttonDesc[i].hwnd != NULL) {\r
2506       DestroyWindow(buttonDesc[i].hwnd);\r
2507       buttonDesc[i].hwnd = NULL;\r
2508     }\r
2509     if (appData.showButtonBar) {\r
2510       buttonDesc[i].hwnd =\r
2511         CreateWindow("BUTTON", buttonDesc[i].label,\r
2512                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2513                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2514                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2515                      (HMENU) buttonDesc[i].id,\r
2516                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2517       if (tinyLayout) {\r
2518         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2519                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2520                     MAKELPARAM(FALSE, 0));\r
2521       }\r
2522       if (buttonDesc[i].id == IDM_Pause)\r
2523         hwndPause = buttonDesc[i].hwnd;\r
2524       buttonDesc[i].wndproc = (WNDPROC)\r
2525         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2526     }\r
2527   }\r
2528   if (gridPen != NULL) DeleteObject(gridPen);\r
2529   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2530   if (premovePen != NULL) DeleteObject(premovePen);\r
2531   if (lineGap != 0) {\r
2532     logbrush.lbStyle = BS_SOLID;\r
2533     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2534     gridPen =\r
2535       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2536                    lineGap, &logbrush, 0, NULL);\r
2537     logbrush.lbColor = highlightSquareColor;\r
2538     highlightPen =\r
2539       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2540                    lineGap, &logbrush, 0, NULL);\r
2541 \r
2542     logbrush.lbColor = premoveHighlightColor; \r
2543     premovePen =\r
2544       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2545                    lineGap, &logbrush, 0, NULL);\r
2546 \r
2547     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2548     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2549       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2550       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2551         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2552       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2553         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2554       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2555     }\r
2556     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2557       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2558       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2559         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2560         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2561       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2562         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2563       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2564     }\r
2565   }\r
2566 \r
2567   /* [HGM] Licensing requirement */\r
2568 #ifdef GOTHIC\r
2569   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2570 #endif\r
2571 #ifdef FALCON\r
2572   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2573 #endif\r
2574   GothicPopUp( "", VariantNormal);\r
2575 \r
2576 \r
2577 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2578 \r
2579   /* Load piece bitmaps for this board size */\r
2580   for (i=0; i<=2; i++) {\r
2581     for (piece = WhitePawn;\r
2582          (int) piece < (int) BlackPawn;\r
2583          piece = (ChessSquare) ((int) piece + 1)) {\r
2584       if (pieceBitmap[i][piece] != NULL)\r
2585         DeleteObject(pieceBitmap[i][piece]);\r
2586     }\r
2587   }\r
2588 \r
2589   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2590   // Orthodox Chess pieces\r
2591   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2592   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2593   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2594   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2595   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2596   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2597   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2598   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2599   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2600   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2601   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2602   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2603   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2604   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2605   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2606   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2607     // in Shogi, Hijack the unused Queen for Lance\r
2608     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2609     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2610     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2611   } else {\r
2612     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2613     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2614     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2615   }\r
2616 \r
2617   if(squareSize <= 72 && squareSize >= 33) { \r
2618     /* A & C are available in most sizes now */\r
2619     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2620       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2621       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2622       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2623       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2624       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2625       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2626       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2627       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2628       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2629       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2630       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2631       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2632     } else { // Smirf-like\r
2633       if(gameInfo.variant == VariantSChess) {\r
2634         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2635         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2636         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2637       } else {\r
2638         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2639         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2640         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2641       }\r
2642     }\r
2643     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2644       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2645       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2646       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2647     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2648       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2649       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2650       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2651     } else { // WinBoard standard\r
2652       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2653       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2654       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2655     }\r
2656   }\r
2657 \r
2658 \r
2659   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2660     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2661     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2662     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2663     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2664     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2665     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2666     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2667     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2668     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2669     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2670     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2671     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2672     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2673     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2674     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2675     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2676     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2677     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2678     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2679     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2680     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2681     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2682     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2683     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2684     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2685     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2686     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2687     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2688     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2689     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2690     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2691     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2692     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2693 \r
2694     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
2695       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2696       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2697       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2698       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2699       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2700       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2701       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2702       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2703       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2704       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2705       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2706       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2707     } else {\r
2708       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2709       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2710       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2711       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2712       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2713       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2714       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2715       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2716       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2717       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2718       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2719       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2720     }\r
2721 \r
2722   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2723     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2724     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2725     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2726     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2727     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2728     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2729     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2730     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2731     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2732     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2733     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2734     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2735     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2736     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2737   }\r
2738 \r
2739 \r
2740   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2741   /* special Shogi support in this size */\r
2742   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2743       for (piece = WhitePawn;\r
2744            (int) piece < (int) BlackPawn;\r
2745            piece = (ChessSquare) ((int) piece + 1)) {\r
2746         if (pieceBitmap[i][piece] != NULL)\r
2747           DeleteObject(pieceBitmap[i][piece]);\r
2748       }\r
2749     }\r
2750   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2751   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2752   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2753   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2754   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2755   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2756   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2757   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2758   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2759   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2760   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2761   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2762   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2763   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2764   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2765   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2766   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2767   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2768   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2769   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2770   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2771   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2772   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2773   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2774   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2775   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2776   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2777   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2778   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2779   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2780   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2781   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2782   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2783   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2784   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2785   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2786   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2787   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2788   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2789   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2790   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2791   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2792   minorSize = 0;\r
2793   }\r
2794 }\r
2795 \r
2796 HBITMAP\r
2797 PieceBitmap(ChessSquare p, int kind)\r
2798 {\r
2799   if ((int) p >= (int) BlackPawn)\r
2800     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2801 \r
2802   return pieceBitmap[kind][(int) p];\r
2803 }\r
2804 \r
2805 /***************************************************************/\r
2806 \r
2807 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2808 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2809 /*\r
2810 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2811 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2812 */\r
2813 \r
2814 VOID\r
2815 SquareToPos(int row, int column, int * x, int * y)\r
2816 {\r
2817   if (flipView) {\r
2818     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2819     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2820   } else {\r
2821     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2822     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2823   }\r
2824 }\r
2825 \r
2826 VOID\r
2827 DrawCoordsOnDC(HDC hdc)\r
2828 {\r
2829   static char files[] = "0123456789012345678901221098765432109876543210";\r
2830   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2831   char str[2] = { NULLCHAR, NULLCHAR };\r
2832   int oldMode, oldAlign, x, y, start, i;\r
2833   HFONT oldFont;\r
2834   HBRUSH oldBrush;\r
2835 \r
2836   if (!appData.showCoords)\r
2837     return;\r
2838 \r
2839   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2840 \r
2841   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2842   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2843   oldAlign = GetTextAlign(hdc);\r
2844   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2845 \r
2846   y = boardRect.top + lineGap;\r
2847   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2848 \r
2849   if(border) {\r
2850     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2851     x += border - lineGap - 4; y += squareSize - 6;\r
2852   } else\r
2853   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2854   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2855     str[0] = files[start + i];\r
2856     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2857     y += squareSize + lineGap;\r
2858   }\r
2859 \r
2860   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2861 \r
2862   if(border) {\r
2863     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2864     x += -border + 4; y += border - squareSize + 6;\r
2865   } else\r
2866   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2867   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2868     str[0] = ranks[start + i];\r
2869     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2870     x += squareSize + lineGap;\r
2871   }    \r
2872 \r
2873   SelectObject(hdc, oldBrush);\r
2874   SetBkMode(hdc, oldMode);\r
2875   SetTextAlign(hdc, oldAlign);\r
2876   SelectObject(hdc, oldFont);\r
2877 }\r
2878 \r
2879 VOID\r
2880 DrawGridOnDC(HDC hdc)\r
2881 {\r
2882   HPEN oldPen;\r
2883  \r
2884   if (lineGap != 0) {\r
2885     oldPen = SelectObject(hdc, gridPen);\r
2886     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2887     SelectObject(hdc, oldPen);\r
2888   }\r
2889 }\r
2890 \r
2891 #define HIGHLIGHT_PEN 0\r
2892 #define PREMOVE_PEN   1\r
2893 \r
2894 VOID\r
2895 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2896 {\r
2897   int x1, y1;\r
2898   HPEN oldPen, hPen;\r
2899   if (lineGap == 0) return;\r
2900   if (flipView) {\r
2901     x1 = boardRect.left +\r
2902       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2903     y1 = boardRect.top +\r
2904       lineGap/2 + y * (squareSize + lineGap) + border;\r
2905   } else {\r
2906     x1 = boardRect.left +\r
2907       lineGap/2 + x * (squareSize + lineGap) + border;\r
2908     y1 = boardRect.top +\r
2909       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2910   }\r
2911   hPen = pen ? premovePen : highlightPen;\r
2912   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2913   MoveToEx(hdc, x1, y1, NULL);\r
2914   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2915   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2916   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2917   LineTo(hdc, x1, y1);\r
2918   SelectObject(hdc, oldPen);\r
2919 }\r
2920 \r
2921 VOID\r
2922 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2923 {\r
2924   int i;\r
2925   for (i=0; i<2; i++) {\r
2926     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2927       DrawHighlightOnDC(hdc, TRUE,\r
2928                         h->sq[i].x, h->sq[i].y,\r
2929                         pen);\r
2930   }\r
2931 }\r
2932 \r
2933 /* Note: sqcolor is used only in monoMode */\r
2934 /* Note that this code is largely duplicated in woptions.c,\r
2935    function DrawSampleSquare, so that needs to be updated too */\r
2936 VOID\r
2937 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2938 {\r
2939   HBITMAP oldBitmap;\r
2940   HBRUSH oldBrush;\r
2941   int tmpSize;\r
2942 \r
2943   if (appData.blindfold) return;\r
2944 \r
2945   /* [AS] Use font-based pieces if needed */\r
2946   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2947     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2948     CreatePiecesFromFont();\r
2949 \r
2950     if( fontBitmapSquareSize == squareSize ) {\r
2951         int index = TranslatePieceToFontPiece(piece);\r
2952 \r
2953         SelectObject( tmphdc, hPieceMask[ index ] );\r
2954 \r
2955       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2956         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2957       else\r
2958         BitBlt( hdc,\r
2959             x, y,\r
2960             squareSize, squareSize,\r
2961             tmphdc,\r
2962             0, 0,\r
2963             SRCAND );\r
2964 \r
2965         SelectObject( tmphdc, hPieceFace[ index ] );\r
2966 \r
2967       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2968         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2969       else\r
2970         BitBlt( hdc,\r
2971             x, y,\r
2972             squareSize, squareSize,\r
2973             tmphdc,\r
2974             0, 0,\r
2975             SRCPAINT );\r
2976 \r
2977         return;\r
2978     }\r
2979   }\r
2980 \r
2981   if (appData.monoMode) {\r
2982     SelectObject(tmphdc, PieceBitmap(piece, \r
2983       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2984     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2985            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2986   } else {\r
2987     HBRUSH xBrush = whitePieceBrush;\r
2988     tmpSize = squareSize;\r
2989     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2990     if(minorSize &&\r
2991         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2992          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2993       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2994       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2995       x += (squareSize - minorSize)>>1;\r
2996       y += squareSize - minorSize - 2;\r
2997       tmpSize = minorSize;\r
2998     }\r
2999     if (color || appData.allWhite ) {\r
3000       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3001       if( color )\r
3002               oldBrush = SelectObject(hdc, xBrush);\r
3003       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3004       if(appData.upsideDown && color==flipView)\r
3005         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3006       else\r
3007         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3008       /* Use black for outline of white pieces */\r
3009       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3010       if(appData.upsideDown && color==flipView)\r
3011         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3012       else\r
3013         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3014     } else if(appData.pieceDirectory[0]) {\r
3015       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3016       oldBrush = SelectObject(hdc, xBrush);\r
3017       if(appData.upsideDown && color==flipView)\r
3018         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3019       else\r
3020         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3021       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3022       if(appData.upsideDown && color==flipView)\r
3023         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3024       else\r
3025         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3026     } else {\r
3027       /* Use square color for details of black pieces */\r
3028       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3029       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3030       if(appData.upsideDown && !flipView)\r
3031         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3032       else\r
3033         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3034     }\r
3035     SelectObject(hdc, oldBrush);\r
3036     SelectObject(tmphdc, oldBitmap);\r
3037   }\r
3038 }\r
3039 \r
3040 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3041 int GetBackTextureMode( int algo )\r
3042 {\r
3043     int result = BACK_TEXTURE_MODE_DISABLED;\r
3044 \r
3045     switch( algo ) \r
3046     {\r
3047         case BACK_TEXTURE_MODE_PLAIN:\r
3048             result = 1; /* Always use identity map */\r
3049             break;\r
3050         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3051             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3052             break;\r
3053     }\r
3054 \r
3055     return result;\r
3056 }\r
3057 \r
3058 /* \r
3059     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3060     to handle redraws cleanly (as random numbers would always be different).\r
3061 */\r
3062 VOID RebuildTextureSquareInfo()\r
3063 {\r
3064     BITMAP bi;\r
3065     int lite_w = 0;\r
3066     int lite_h = 0;\r
3067     int dark_w = 0;\r
3068     int dark_h = 0;\r
3069     int row;\r
3070     int col;\r
3071 \r
3072     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3073 \r
3074     if( liteBackTexture != NULL ) {\r
3075         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3076             lite_w = bi.bmWidth;\r
3077             lite_h = bi.bmHeight;\r
3078         }\r
3079     }\r
3080 \r
3081     if( darkBackTexture != NULL ) {\r
3082         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3083             dark_w = bi.bmWidth;\r
3084             dark_h = bi.bmHeight;\r
3085         }\r
3086     }\r
3087 \r
3088     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3089         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3090             if( (col + row) & 1 ) {\r
3091                 /* Lite square */\r
3092                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3093                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3094                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3095                   else\r
3096                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3097                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3098                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3099                   else\r
3100                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3101                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3102                 }\r
3103             }\r
3104             else {\r
3105                 /* Dark square */\r
3106                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3107                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3108                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3109                   else\r
3110                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3111                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3112                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3113                   else\r
3114                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3115                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3116                 }\r
3117             }\r
3118         }\r
3119     }\r
3120 }\r
3121 \r
3122 /* [AS] Arrow highlighting support */\r
3123 \r
3124 static double A_WIDTH = 5; /* Width of arrow body */\r
3125 \r
3126 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3127 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3128 \r
3129 static double Sqr( double x )\r
3130 {\r
3131     return x*x;\r
3132 }\r
3133 \r
3134 static int Round( double x )\r
3135 {\r
3136     return (int) (x + 0.5);\r
3137 }\r
3138 \r
3139 /* Draw an arrow between two points using current settings */\r
3140 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3141 {\r
3142     POINT arrow[7];\r
3143     double dx, dy, j, k, x, y;\r
3144 \r
3145     if( d_x == s_x ) {\r
3146         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3147 \r
3148         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3149         arrow[0].y = s_y;\r
3150 \r
3151         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3152         arrow[1].y = d_y - h;\r
3153 \r
3154         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3155         arrow[2].y = d_y - h;\r
3156 \r
3157         arrow[3].x = d_x;\r
3158         arrow[3].y = d_y;\r
3159 \r
3160         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3161         arrow[5].y = d_y - h;\r
3162 \r
3163         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3164         arrow[4].y = d_y - h;\r
3165 \r
3166         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3167         arrow[6].y = s_y;\r
3168     }\r
3169     else if( d_y == s_y ) {\r
3170         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3171 \r
3172         arrow[0].x = s_x;\r
3173         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3174 \r
3175         arrow[1].x = d_x - w;\r
3176         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3177 \r
3178         arrow[2].x = d_x - w;\r
3179         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3180 \r
3181         arrow[3].x = d_x;\r
3182         arrow[3].y = d_y;\r
3183 \r
3184         arrow[5].x = d_x - w;\r
3185         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3186 \r
3187         arrow[4].x = d_x - w;\r
3188         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3189 \r
3190         arrow[6].x = s_x;\r
3191         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3192     }\r
3193     else {\r
3194         /* [AS] Needed a lot of paper for this! :-) */\r
3195         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3196         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3197   \r
3198         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3199 \r
3200         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3201 \r
3202         x = s_x;\r
3203         y = s_y;\r
3204 \r
3205         arrow[0].x = Round(x - j);\r
3206         arrow[0].y = Round(y + j*dx);\r
3207 \r
3208         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3209         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3210 \r
3211         if( d_x > s_x ) {\r
3212             x = (double) d_x - k;\r
3213             y = (double) d_y - k*dy;\r
3214         }\r
3215         else {\r
3216             x = (double) d_x + k;\r
3217             y = (double) d_y + k*dy;\r
3218         }\r
3219 \r
3220         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3221 \r
3222         arrow[6].x = Round(x - j);\r
3223         arrow[6].y = Round(y + j*dx);\r
3224 \r
3225         arrow[2].x = Round(arrow[6].x + 2*j);\r
3226         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3227 \r
3228         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3229         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3230 \r
3231         arrow[4].x = d_x;\r
3232         arrow[4].y = d_y;\r
3233 \r
3234         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3235         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3236     }\r
3237 \r
3238     Polygon( hdc, arrow, 7 );\r
3239 }\r
3240 \r
3241 /* [AS] Draw an arrow between two squares */\r
3242 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3243 {\r
3244     int s_x, s_y, d_x, d_y;\r
3245     HPEN hpen;\r
3246     HPEN holdpen;\r
3247     HBRUSH hbrush;\r
3248     HBRUSH holdbrush;\r
3249     LOGBRUSH stLB;\r
3250 \r
3251     if( s_col == d_col && s_row == d_row ) {\r
3252         return;\r
3253     }\r
3254 \r
3255     /* Get source and destination points */\r
3256     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3257     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3258 \r
3259     if( d_y > s_y ) {\r
3260         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3261     }\r
3262     else if( d_y < s_y ) {\r
3263         d_y += squareSize / 2 + squareSize / 4;\r
3264     }\r
3265     else {\r
3266         d_y += squareSize / 2;\r
3267     }\r
3268 \r
3269     if( d_x > s_x ) {\r
3270         d_x += squareSize / 2 - squareSize / 4;\r
3271     }\r
3272     else if( d_x < s_x ) {\r
3273         d_x += squareSize / 2 + squareSize / 4;\r
3274     }\r
3275     else {\r
3276         d_x += squareSize / 2;\r
3277     }\r
3278 \r
3279     s_x += squareSize / 2;\r
3280     s_y += squareSize / 2;\r
3281 \r
3282     /* Adjust width */\r
3283     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3284 \r
3285     /* Draw */\r
3286     stLB.lbStyle = BS_SOLID;\r
3287     stLB.lbColor = appData.highlightArrowColor;\r
3288     stLB.lbHatch = 0;\r
3289 \r
3290     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3291     holdpen = SelectObject( hdc, hpen );\r
3292     hbrush = CreateBrushIndirect( &stLB );\r
3293     holdbrush = SelectObject( hdc, hbrush );\r
3294 \r
3295     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3296 \r
3297     SelectObject( hdc, holdpen );\r
3298     SelectObject( hdc, holdbrush );\r
3299     DeleteObject( hpen );\r
3300     DeleteObject( hbrush );\r
3301 }\r
3302 \r
3303 BOOL HasHighlightInfo()\r
3304 {\r
3305     BOOL result = FALSE;\r
3306 \r
3307     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3308         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3309     {\r
3310         result = TRUE;\r
3311     }\r
3312 \r
3313     return result;\r
3314 \r
3315 \r
3316 \r
3317 }\r
3318 \r
3319 BOOL IsDrawArrowEnabled()\r
3320 {\r
3321     BOOL result = FALSE;\r
3322 \r
3323     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3324         result = TRUE;\r
3325     }\r
3326 \r
3327     return result;\r
3328 }\r
3329 \r
3330 VOID DrawArrowHighlight( HDC hdc )\r
3331 {\r
3332     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3333         DrawArrowBetweenSquares( hdc,\r
3334             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3335             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3336     }\r
3337 }\r
3338 \r
3339 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3340 {\r
3341     HRGN result = NULL;\r
3342 \r
3343     if( HasHighlightInfo() ) {\r
3344         int x1, y1, x2, y2;\r
3345         int sx, sy, dx, dy;\r
3346 \r
3347         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3348         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3349 \r
3350         sx = MIN( x1, x2 );\r
3351         sy = MIN( y1, y2 );\r
3352         dx = MAX( x1, x2 ) + squareSize;\r
3353         dy = MAX( y1, y2 ) + squareSize;\r
3354 \r
3355         result = CreateRectRgn( sx, sy, dx, dy );\r
3356     }\r
3357 \r
3358     return result;\r
3359 }\r
3360 \r
3361 /*\r
3362     Warning: this function modifies the behavior of several other functions. \r
3363     \r
3364     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3365     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3366     repaint is scattered all over the place, which is not good for features such as\r
3367     "arrow highlighting" that require a full repaint of the board.\r
3368 \r
3369     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3370     user interaction, when speed is not so important) but especially to avoid errors\r
3371     in the displayed graphics.\r
3372 \r
3373     In such patched places, I always try refer to this function so there is a single\r
3374     place to maintain knowledge.\r
3375     \r
3376     To restore the original behavior, just return FALSE unconditionally.\r
3377 */\r
3378 BOOL IsFullRepaintPreferrable()\r
3379 {\r
3380     BOOL result = FALSE;\r
3381 \r
3382     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3383         /* Arrow may appear on the board */\r
3384         result = TRUE;\r
3385     }\r
3386 \r
3387     return result;\r
3388 }\r
3389 \r
3390 /* \r
3391     This function is called by DrawPosition to know whether a full repaint must\r
3392     be forced or not.\r
3393 \r
3394     Only DrawPosition may directly call this function, which makes use of \r
3395     some state information. Other function should call DrawPosition specifying \r
3396     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3397 */\r
3398 BOOL DrawPositionNeedsFullRepaint()\r
3399 {\r
3400     BOOL result = FALSE;\r
3401 \r
3402     /* \r
3403         Probably a slightly better policy would be to trigger a full repaint\r
3404         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3405         but animation is fast enough that it's difficult to notice.\r
3406     */\r
3407     if( animInfo.piece == EmptySquare ) {\r
3408         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3409             result = TRUE;\r
3410         }\r
3411     }\r
3412 \r
3413     return result;\r
3414 }\r
3415 \r
3416 static HBITMAP borderBitmap;\r
3417 \r
3418 VOID\r
3419 DrawBackgroundOnDC(HDC hdc)\r
3420 {\r
3421   \r
3422   BITMAP bi;\r
3423   HDC tmphdc;\r
3424   HBITMAP hbm;\r
3425   static char oldBorder[MSG_SIZ];\r
3426   int w = 600, h = 600, mode;\r
3427 \r
3428   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3429     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3430     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3431   }\r
3432   if(borderBitmap == NULL) { // loading failed, use white\r
3433     FillRect( hdc, &boardRect, whitePieceBrush );\r
3434     return;\r
3435   }\r
3436   tmphdc = CreateCompatibleDC(hdc);\r
3437   hbm = SelectObject(tmphdc, borderBitmap);\r
3438   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3439             w = bi.bmWidth;\r
3440             h = bi.bmHeight;\r
3441   }\r
3442   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3443   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3444                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3445   SetStretchBltMode(hdc, mode);\r
3446   SelectObject(tmphdc, hbm);\r
3447   DeleteDC(tmphdc);\r
3448 }\r
3449 \r
3450 VOID\r
3451 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3452 {\r
3453   int row, column, x, y, square_color, piece_color;\r
3454   ChessSquare piece;\r
3455   HBRUSH oldBrush;\r
3456   HDC texture_hdc = NULL;\r
3457 \r
3458   /* [AS] Initialize background textures if needed */\r
3459   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3460       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3461       if( backTextureSquareSize != squareSize \r
3462        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3463           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3464           backTextureSquareSize = squareSize;\r
3465           RebuildTextureSquareInfo();\r
3466       }\r
3467 \r
3468       texture_hdc = CreateCompatibleDC( hdc );\r
3469   }\r
3470 \r
3471   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3472     for (column = 0; column < BOARD_WIDTH; column++) {\r
3473   \r
3474       SquareToPos(row, column, &x, &y);\r
3475 \r
3476       piece = board[row][column];\r
3477 \r
3478       square_color = ((column + row) % 2) == 1;\r
3479       if( gameInfo.variant == VariantXiangqi ) {\r
3480           square_color = !InPalace(row, column);\r
3481           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3482           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3483       }\r
3484       piece_color = (int) piece < (int) BlackPawn;\r
3485 \r
3486 \r
3487       /* [HGM] holdings file: light square or black */\r
3488       if(column == BOARD_LEFT-2) {\r
3489             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3490                 square_color = 1;\r
3491             else {\r
3492                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3493                 continue;\r
3494             }\r
3495       } else\r
3496       if(column == BOARD_RGHT + 1 ) {\r
3497             if( row < gameInfo.holdingsSize )\r
3498                 square_color = 1;\r
3499             else {\r
3500                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3501                 continue;\r
3502             }\r
3503       }\r
3504       if(column == BOARD_LEFT-1 ) /* left align */\r
3505             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3506       else if( column == BOARD_RGHT) /* right align */\r
3507             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3508       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3509       else\r
3510       if (appData.monoMode) {\r
3511         if (piece == EmptySquare) {\r
3512           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3513                  square_color ? WHITENESS : BLACKNESS);\r
3514         } else {\r
3515           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3516         }\r
3517       } \r
3518       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3519           /* [AS] Draw the square using a texture bitmap */\r
3520           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3521           int r = row, c = column; // [HGM] do not flip board in flipView\r
3522           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3523 \r
3524           DrawTile( x, y, \r
3525               squareSize, squareSize, \r
3526               hdc, \r
3527               texture_hdc,\r
3528               backTextureSquareInfo[r][c].mode,\r
3529               backTextureSquareInfo[r][c].x,\r
3530               backTextureSquareInfo[r][c].y );\r
3531 \r
3532           SelectObject( texture_hdc, hbm );\r
3533 \r
3534           if (piece != EmptySquare) {\r
3535               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3536           }\r
3537       }\r
3538       else {\r
3539         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3540 \r
3541         oldBrush = SelectObject(hdc, brush );\r
3542         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3543         SelectObject(hdc, oldBrush);\r
3544         if (piece != EmptySquare)\r
3545           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3546       }\r
3547     }\r
3548   }\r
3549 \r
3550   if( texture_hdc != NULL ) {\r
3551     DeleteDC( texture_hdc );\r
3552   }\r
3553 }\r
3554 \r
3555 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3556 void fputDW(FILE *f, int x)\r
3557 {\r
3558         fputc(x     & 255, f);\r
3559         fputc(x>>8  & 255, f);\r
3560         fputc(x>>16 & 255, f);\r
3561         fputc(x>>24 & 255, f);\r
3562 }\r
3563 \r
3564 #define MAX_CLIPS 200   /* more than enough */\r
3565 \r
3566 VOID\r
3567 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3568 {\r
3569 //  HBITMAP bufferBitmap;\r
3570   BITMAP bi;\r
3571 //  RECT Rect;\r
3572   HDC tmphdc;\r
3573   HBITMAP hbm;\r
3574   int w = 100, h = 50;\r
3575 \r
3576   if(logo == NULL) {\r
3577     if(!logoHeight) return;\r
3578     FillRect( hdc, &logoRect, whitePieceBrush );\r
3579   }\r
3580 //  GetClientRect(hwndMain, &Rect);\r
3581 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3582 //                                      Rect.bottom-Rect.top+1);\r
3583   tmphdc = CreateCompatibleDC(hdc);\r
3584   hbm = SelectObject(tmphdc, logo);\r
3585   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3586             w = bi.bmWidth;\r
3587             h = bi.bmHeight;\r
3588   }\r
3589   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3590                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3591   SelectObject(tmphdc, hbm);\r
3592   DeleteDC(tmphdc);\r
3593 }\r
3594 \r
3595 VOID\r
3596 DisplayLogos()\r
3597 {\r
3598   if(logoHeight) {\r
3599         HDC hdc = GetDC(hwndMain);\r
3600         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3601         if(appData.autoLogo) {\r
3602           \r
3603           switch(gameMode) { // pick logos based on game mode\r
3604             case IcsObserving:\r
3605                 whiteLogo = second.programLogo; // ICS logo\r
3606                 blackLogo = second.programLogo;\r
3607             default:\r
3608                 break;\r
3609             case IcsPlayingWhite:\r
3610                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3611                 blackLogo = second.programLogo; // ICS logo\r
3612                 break;\r
3613             case IcsPlayingBlack:\r
3614                 whiteLogo = second.programLogo; // ICS logo\r
3615                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3616                 break;\r
3617             case TwoMachinesPlay:\r
3618                 if(first.twoMachinesColor[0] == 'b') {\r
3619                     whiteLogo = second.programLogo;\r
3620                     blackLogo = first.programLogo;\r
3621                 }\r
3622                 break;\r
3623             case MachinePlaysWhite:\r
3624                 blackLogo = userLogo;\r
3625                 break;\r
3626             case MachinePlaysBlack:\r
3627                 whiteLogo = userLogo;\r
3628                 blackLogo = first.programLogo;\r
3629           }\r
3630         }\r
3631         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3632         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3633         ReleaseDC(hwndMain, hdc);\r
3634   }\r
3635 }\r
3636 \r
3637 void\r
3638 UpdateLogos(int display)\r
3639 { // called after loading new engine(s), in tourney or from menu\r
3640   LoadLogo(&first, 0, FALSE);\r
3641   LoadLogo(&second, 1, appData.icsActive);\r
3642   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3643   if(display) DisplayLogos();\r
3644 }\r
3645 \r
3646 static HDC hdcSeek;\r
3647 \r
3648 // [HGM] seekgraph\r
3649 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3650 {\r
3651     POINT stPt;\r
3652     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3653     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3654     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3655     SelectObject( hdcSeek, hp );\r
3656 }\r
3657 \r
3658 // front-end wrapper for drawing functions to do rectangles\r
3659 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3660 {\r
3661     HPEN hp;\r
3662     RECT rc;\r
3663 \r
3664     if (hdcSeek == NULL) {\r
3665     hdcSeek = GetDC(hwndMain);\r
3666       if (!appData.monoMode) {\r
3667         SelectPalette(hdcSeek, hPal, FALSE);\r
3668         RealizePalette(hdcSeek);\r
3669       }\r
3670     }\r
3671     hp = SelectObject( hdcSeek, gridPen );\r
3672     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3673     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3674     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3675     SelectObject( hdcSeek, hp );\r
3676 }\r
3677 \r
3678 // front-end wrapper for putting text in graph\r
3679 void DrawSeekText(char *buf, int x, int y)\r
3680 {\r
3681         SIZE stSize;\r
3682         SetBkMode( hdcSeek, TRANSPARENT );\r
3683         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3684         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3685 }\r
3686 \r
3687 void DrawSeekDot(int x, int y, int color)\r
3688 {\r
3689         int square = color & 0x80;\r
3690         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3691                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3692         color &= 0x7F;\r
3693         if(square)\r
3694             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3695                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3696         else\r
3697             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3698                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3699             SelectObject(hdcSeek, oldBrush);\r
3700 }\r
3701 \r
3702 void DrawSeekOpen()\r
3703 {\r
3704 }\r
3705 \r
3706 void DrawSeekClose()\r
3707 {\r
3708 }\r
3709 \r
3710 VOID\r
3711 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3712 {\r
3713   static Board lastReq[2], lastDrawn[2];\r
3714   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3715   static int lastDrawnFlipView = 0;\r
3716   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3717   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3718   HDC tmphdc;\r
3719   HDC hdcmem;\r
3720   HBITMAP bufferBitmap;\r
3721   HBITMAP oldBitmap;\r
3722   RECT Rect;\r
3723   HRGN clips[MAX_CLIPS];\r
3724   ChessSquare dragged_piece = EmptySquare;\r
3725   int nr = twoBoards*partnerUp;\r
3726 \r
3727   /* I'm undecided on this - this function figures out whether a full\r
3728    * repaint is necessary on its own, so there's no real reason to have the\r
3729    * caller tell it that.  I think this can safely be set to FALSE - but\r
3730    * if we trust the callers not to request full repaints unnessesarily, then\r
3731    * we could skip some clipping work.  In other words, only request a full\r
3732    * redraw when the majority of pieces have changed positions (ie. flip, \r
3733    * gamestart and similar)  --Hawk\r
3734    */\r
3735   Boolean fullrepaint = repaint;\r
3736 \r
3737   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3738 \r
3739   if( DrawPositionNeedsFullRepaint() ) {\r
3740       fullrepaint = TRUE;\r
3741   }\r
3742 \r
3743   if (board == NULL) {\r
3744     if (!lastReqValid[nr]) {\r
3745       return;\r
3746     }\r
3747     board = lastReq[nr];\r
3748   } else {\r
3749     CopyBoard(lastReq[nr], board);\r
3750     lastReqValid[nr] = 1;\r
3751   }\r
3752 \r
3753   if (doingSizing) {\r
3754     return;\r
3755   }\r
3756 \r
3757   if (IsIconic(hwndMain)) {\r
3758     return;\r
3759   }\r
3760 \r
3761   if (hdc == NULL) {\r
3762     hdc = GetDC(hwndMain);\r
3763     if (!appData.monoMode) {\r
3764       SelectPalette(hdc, hPal, FALSE);\r
3765       RealizePalette(hdc);\r
3766     }\r
3767     releaseDC = TRUE;\r
3768   } else {\r
3769     releaseDC = FALSE;\r
3770   }\r
3771 \r
3772   /* Create some work-DCs */\r
3773   hdcmem = CreateCompatibleDC(hdc);\r
3774   tmphdc = CreateCompatibleDC(hdc);\r
3775 \r
3776   /* If dragging is in progress, we temporarely remove the piece */\r
3777   /* [HGM] or temporarily decrease count if stacked              */\r
3778   /*       !! Moved to before board compare !!                   */\r
3779   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3780     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3781     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3782             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3783         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3784     } else \r
3785     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3786             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3787         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3788     } else \r
3789         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3790   }\r
3791 \r
3792   /* Figure out which squares need updating by comparing the \r
3793    * newest board with the last drawn board and checking if\r
3794    * flipping has changed.\r
3795    */\r
3796   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3797     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3798       for (column = 0; column < BOARD_WIDTH; column++) {\r
3799         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3800           SquareToPos(row, column, &x, &y);\r
3801           clips[num_clips++] =\r
3802             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3803         }\r
3804       }\r
3805     }\r
3806    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3807     for (i=0; i<2; i++) {\r
3808       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3809           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3810         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3811             lastDrawnHighlight.sq[i].y >= 0) {\r
3812           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3813                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3814           clips[num_clips++] =\r
3815             CreateRectRgn(x - lineGap, y - lineGap, \r
3816                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3817         }\r
3818         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3819           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3820           clips[num_clips++] =\r
3821             CreateRectRgn(x - lineGap, y - lineGap, \r
3822                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3823         }\r
3824       }\r
3825     }\r
3826     for (i=0; i<2; i++) {\r
3827       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3828           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3829         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3830             lastDrawnPremove.sq[i].y >= 0) {\r
3831           SquareToPos(lastDrawnPremove.sq[i].y,\r
3832                       lastDrawnPremove.sq[i].x, &x, &y);\r
3833           clips[num_clips++] =\r
3834             CreateRectRgn(x - lineGap, y - lineGap, \r
3835                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3836         }\r
3837         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3838             premoveHighlightInfo.sq[i].y >= 0) {\r
3839           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3840                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3841           clips[num_clips++] =\r
3842             CreateRectRgn(x - lineGap, y - lineGap, \r
3843                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3844         }\r
3845       }\r
3846     }\r
3847    } else { // nr == 1\r
3848         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3849         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3850         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3851         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3852       for (i=0; i<2; i++) {\r
3853         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3854             partnerHighlightInfo.sq[i].y >= 0) {\r
3855           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3856                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3857           clips[num_clips++] =\r
3858             CreateRectRgn(x - lineGap, y - lineGap, \r
3859                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3860         }\r
3861         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3862             oldPartnerHighlight.sq[i].y >= 0) {\r
3863           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3864                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3865           clips[num_clips++] =\r
3866             CreateRectRgn(x - lineGap, y - lineGap, \r
3867                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3868         }\r
3869       }\r
3870    }\r
3871   } else {\r
3872     fullrepaint = TRUE;\r
3873   }\r
3874 \r
3875   /* Create a buffer bitmap - this is the actual bitmap\r
3876    * being written to.  When all the work is done, we can\r
3877    * copy it to the real DC (the screen).  This avoids\r
3878    * the problems with flickering.\r
3879    */\r
3880   GetClientRect(hwndMain, &Rect);\r
3881   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3882                                         Rect.bottom-Rect.top+1);\r
3883   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3884   if (!appData.monoMode) {\r
3885     SelectPalette(hdcmem, hPal, FALSE);\r
3886   }\r
3887 \r
3888   /* Create clips for dragging */\r
3889   if (!fullrepaint) {\r
3890     if (dragInfo.from.x >= 0) {\r
3891       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3892       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3893     }\r
3894     if (dragInfo.start.x >= 0) {\r
3895       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3896       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3897     }\r
3898     if (dragInfo.pos.x >= 0) {\r
3899       x = dragInfo.pos.x - squareSize / 2;\r
3900       y = dragInfo.pos.y - squareSize / 2;\r
3901       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3902     }\r
3903     if (dragInfo.lastpos.x >= 0) {\r
3904       x = dragInfo.lastpos.x - squareSize / 2;\r
3905       y = dragInfo.lastpos.y - squareSize / 2;\r
3906       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3907     }\r
3908   }\r
3909 \r
3910   /* Are we animating a move?  \r
3911    * If so, \r
3912    *   - remove the piece from the board (temporarely)\r
3913    *   - calculate the clipping region\r
3914    */\r
3915   if (!fullrepaint) {\r
3916     if (animInfo.piece != EmptySquare) {\r
3917       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3918       x = boardRect.left + animInfo.lastpos.x;\r
3919       y = boardRect.top + animInfo.lastpos.y;\r
3920       x2 = boardRect.left + animInfo.pos.x;\r
3921       y2 = boardRect.top + animInfo.pos.y;\r
3922       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3923       /* Slight kludge.  The real problem is that after AnimateMove is\r
3924          done, the position on the screen does not match lastDrawn.\r
3925          This currently causes trouble only on e.p. captures in\r
3926          atomic, where the piece moves to an empty square and then\r
3927          explodes.  The old and new positions both had an empty square\r
3928          at the destination, but animation has drawn a piece there and\r
3929          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3930       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3931     }\r
3932   }\r
3933 \r
3934   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3935   if (num_clips == 0)\r
3936     fullrepaint = TRUE;\r
3937 \r
3938   /* Set clipping on the memory DC */\r
3939   if (!fullrepaint) {\r
3940     SelectClipRgn(hdcmem, clips[0]);\r
3941     for (x = 1; x < num_clips; x++) {\r
3942       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3943         abort();  // this should never ever happen!\r
3944     }\r
3945   }\r
3946 \r
3947   /* Do all the drawing to the memory DC */\r
3948   if(explodeInfo.radius) { // [HGM] atomic\r
3949         HBRUSH oldBrush;\r
3950         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3951         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3952         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3953         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3954         x += squareSize/2;\r
3955         y += squareSize/2;\r
3956         if(!fullrepaint) {\r
3957           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3958           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3959         }\r
3960         DrawGridOnDC(hdcmem);\r
3961         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3962         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3963         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3964         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3965         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3966         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3967         SelectObject(hdcmem, oldBrush);\r
3968   } else {\r
3969     if(border) DrawBackgroundOnDC(hdcmem);\r
3970     DrawGridOnDC(hdcmem);\r
3971     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3972         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3973         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3974     } else {\r
3975         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3976         oldPartnerHighlight = partnerHighlightInfo;\r
3977     }\r
3978     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3979   }\r
3980   if(nr == 0) // [HGM] dual: markers only on left board\r
3981   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3982     for (column = 0; column < BOARD_WIDTH; column++) {\r
3983         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3984             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3985             SquareToPos(row, column, &x, &y);\r
3986             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3987                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3988             SelectObject(hdcmem, oldBrush);\r
3989         }\r
3990     }\r
3991   }\r
3992 \r
3993   if( appData.highlightMoveWithArrow ) {\r
3994     DrawArrowHighlight(hdcmem);\r
3995   }\r
3996 \r
3997   DrawCoordsOnDC(hdcmem);\r
3998 \r
3999   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4000                  /* to make sure lastDrawn contains what is actually drawn */\r
4001 \r
4002   /* Put the dragged piece back into place and draw it (out of place!) */\r
4003     if (dragged_piece != EmptySquare) {\r
4004     /* [HGM] or restack */\r
4005     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4006                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4007     else\r
4008     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4009                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4010 \r
4011     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4012     x = dragInfo.pos.x - squareSize / 2;\r
4013     y = dragInfo.pos.y - squareSize / 2;\r
4014     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4015                   ((int) dragInfo.piece < (int) BlackPawn), \r
4016                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4017   }   \r
4018   \r
4019   /* Put the animated piece back into place and draw it */\r
4020   if (animInfo.piece != EmptySquare) {\r
4021     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4022     x = boardRect.left + animInfo.pos.x;\r
4023     y = boardRect.top + animInfo.pos.y;\r
4024     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4025                   ((int) animInfo.piece < (int) BlackPawn),\r
4026                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4027   }\r
4028 \r
4029   /* Release the bufferBitmap by selecting in the old bitmap \r
4030    * and delete the memory DC\r
4031    */\r
4032   SelectObject(hdcmem, oldBitmap);\r
4033   DeleteDC(hdcmem);\r
4034 \r
4035   /* Set clipping on the target DC */\r
4036   if (!fullrepaint) {\r
4037     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4038         RECT rect;\r
4039         GetRgnBox(clips[x], &rect);\r
4040         DeleteObject(clips[x]);\r
4041         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4042                           rect.right + wpMain.width/2, rect.bottom);\r
4043     }\r
4044     SelectClipRgn(hdc, clips[0]);\r
4045     for (x = 1; x < num_clips; x++) {\r
4046       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4047         abort();   // this should never ever happen!\r
4048     } \r
4049   }\r
4050 \r
4051   /* Copy the new bitmap onto the screen in one go.\r
4052    * This way we avoid any flickering\r
4053    */\r
4054   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4055   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4056          boardRect.right - boardRect.left,\r
4057          boardRect.bottom - boardRect.top,\r
4058          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4059   if(saveDiagFlag) { \r
4060     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4061     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4062 \r
4063     GetObject(bufferBitmap, sizeof(b), &b);\r
4064     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4065         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4066         bih.biWidth = b.bmWidth;\r
4067         bih.biHeight = b.bmHeight;\r
4068         bih.biPlanes = 1;\r
4069         bih.biBitCount = b.bmBitsPixel;\r
4070         bih.biCompression = 0;\r
4071         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4072         bih.biXPelsPerMeter = 0;\r
4073         bih.biYPelsPerMeter = 0;\r
4074         bih.biClrUsed = 0;\r
4075         bih.biClrImportant = 0;\r
4076 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4077 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4078         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4079 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4080 \r
4081         wb = b.bmWidthBytes;\r
4082         // count colors\r
4083         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4084                 int k = ((int*) pData)[i];\r
4085                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4086                 if(j >= 16) break;\r
4087                 color[j] = k;\r
4088                 if(j >= nrColors) nrColors = j+1;\r
4089         }\r
4090         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4091                 INT p = 0;\r
4092                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4093                     for(w=0; w<(wb>>2); w+=2) {\r
4094                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4095                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4096                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4097                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4098                         pData[p++] = m | j<<4;\r
4099                     }\r
4100                     while(p&3) pData[p++] = 0;\r
4101                 }\r
4102                 fac = 3;\r
4103                 wb = ((wb+31)>>5)<<2;\r
4104         }\r
4105         // write BITMAPFILEHEADER\r
4106         fprintf(diagFile, "BM");\r
4107         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4108         fputDW(diagFile, 0);\r
4109         fputDW(diagFile, 0x36 + (fac?64:0));\r
4110         // write BITMAPINFOHEADER\r
4111         fputDW(diagFile, 40);\r
4112         fputDW(diagFile, b.bmWidth);\r
4113         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4114         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4115         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4116         fputDW(diagFile, 0);\r
4117         fputDW(diagFile, 0);\r
4118         fputDW(diagFile, 0);\r
4119         fputDW(diagFile, 0);\r
4120         fputDW(diagFile, 0);\r
4121         fputDW(diagFile, 0);\r
4122         // write color table\r
4123         if(fac)\r
4124         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4125         // write bitmap data\r
4126         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4127                 fputc(pData[i], diagFile);\r
4128         free(pData);\r
4129      }\r
4130   }\r
4131 \r
4132   SelectObject(tmphdc, oldBitmap);\r
4133 \r
4134   /* Massive cleanup */\r
4135   for (x = 0; x < num_clips; x++)\r
4136     DeleteObject(clips[x]);\r
4137 \r
4138   DeleteDC(tmphdc);\r
4139   DeleteObject(bufferBitmap);\r
4140 \r
4141   if (releaseDC) \r
4142     ReleaseDC(hwndMain, hdc);\r
4143   \r
4144   if (lastDrawnFlipView != flipView && nr == 0) {\r
4145     if (flipView)\r
4146       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4147     else\r
4148       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4149   }\r
4150 \r
4151 /*  CopyBoard(lastDrawn, board);*/\r
4152   lastDrawnHighlight = highlightInfo;\r
4153   lastDrawnPremove   = premoveHighlightInfo;\r
4154   lastDrawnFlipView = flipView;\r
4155   lastDrawnValid[nr] = 1;\r
4156 }\r
4157 \r
4158 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4159 int\r
4160 SaveDiagram(f)\r
4161      FILE *f;\r
4162 {\r
4163     saveDiagFlag = 1; diagFile = f;\r
4164     HDCDrawPosition(NULL, TRUE, NULL);\r
4165     saveDiagFlag = 0;\r
4166 \r
4167     fclose(f);\r
4168     return TRUE;\r
4169 }\r
4170 \r
4171 \r
4172 /*---------------------------------------------------------------------------*\\r
4173 | CLIENT PAINT PROCEDURE\r
4174 |   This is the main event-handler for the WM_PAINT message.\r
4175 |\r
4176 \*---------------------------------------------------------------------------*/\r
4177 VOID\r
4178 PaintProc(HWND hwnd)\r
4179 {\r
4180   HDC         hdc;\r
4181   PAINTSTRUCT ps;\r
4182   HFONT       oldFont;\r
4183 \r
4184   if((hdc = BeginPaint(hwnd, &ps))) {\r
4185     if (IsIconic(hwnd)) {\r
4186       DrawIcon(hdc, 2, 2, iconCurrent);\r
4187     } else {\r
4188       if (!appData.monoMode) {\r
4189         SelectPalette(hdc, hPal, FALSE);\r
4190         RealizePalette(hdc);\r
4191       }\r
4192       HDCDrawPosition(hdc, 1, NULL);\r
4193       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4194         flipView = !flipView; partnerUp = !partnerUp;\r
4195         HDCDrawPosition(hdc, 1, NULL);\r
4196         flipView = !flipView; partnerUp = !partnerUp;\r
4197       }\r
4198       oldFont =\r
4199         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4200       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4201                  ETO_CLIPPED|ETO_OPAQUE,\r
4202                  &messageRect, messageText, strlen(messageText), NULL);\r
4203       SelectObject(hdc, oldFont);\r
4204       DisplayBothClocks();\r
4205       DisplayLogos();\r
4206     }\r
4207     EndPaint(hwnd,&ps);\r
4208   }\r
4209 \r
4210   return;\r
4211 }\r
4212 \r
4213 \r
4214 /*\r
4215  * If the user selects on a border boundary, return -1; if off the board,\r
4216  *   return -2.  Otherwise map the event coordinate to the square.\r
4217  * The offset boardRect.left or boardRect.top must already have been\r
4218  *   subtracted from x.\r
4219  */\r
4220 int EventToSquare(x, limit)\r
4221      int x, limit;\r
4222 {\r
4223   if (x <= border)\r
4224     return -2;\r
4225   if (x < lineGap + border)\r
4226     return -1;\r
4227   x -= lineGap + border;\r
4228   if ((x % (squareSize + lineGap)) >= squareSize)\r
4229     return -1;\r
4230   x /= (squareSize + lineGap);\r
4231     if (x >= limit)\r
4232     return -2;\r
4233   return x;\r
4234 }\r
4235 \r
4236 typedef struct {\r
4237   char piece;\r
4238   int command;\r
4239   char* name;\r
4240 } DropEnable;\r
4241 \r
4242 DropEnable dropEnables[] = {\r
4243   { 'P', DP_Pawn, N_("Pawn") },\r
4244   { 'N', DP_Knight, N_("Knight") },\r
4245   { 'B', DP_Bishop, N_("Bishop") },\r
4246   { 'R', DP_Rook, N_("Rook") },\r
4247   { 'Q', DP_Queen, N_("Queen") },\r
4248 };\r
4249 \r
4250 VOID\r
4251 SetupDropMenu(HMENU hmenu)\r
4252 {\r
4253   int i, count, enable;\r
4254   char *p;\r
4255   extern char white_holding[], black_holding[];\r
4256   char item[MSG_SIZ];\r
4257 \r
4258   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4259     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4260                dropEnables[i].piece);\r
4261     count = 0;\r
4262     while (p && *p++ == dropEnables[i].piece) count++;\r
4263       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4264     enable = count > 0 || !appData.testLegality\r
4265       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4266                       && !appData.icsActive);\r
4267     ModifyMenu(hmenu, dropEnables[i].command,\r
4268                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4269                dropEnables[i].command, item);\r
4270   }\r
4271 }\r
4272 \r
4273 void DragPieceBegin(int x, int y, Boolean instantly)\r
4274 {\r
4275       dragInfo.lastpos.x = boardRect.left + x;\r
4276       dragInfo.lastpos.y = boardRect.top + y;\r
4277       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4278       dragInfo.from.x = fromX;\r
4279       dragInfo.from.y = fromY;\r
4280       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4281       dragInfo.start = dragInfo.from;\r
4282       SetCapture(hwndMain);\r
4283 }\r
4284 \r
4285 void DragPieceEnd(int x, int y)\r
4286 {\r
4287     ReleaseCapture();\r
4288     dragInfo.start.x = dragInfo.start.y = -1;\r
4289     dragInfo.from = dragInfo.start;\r
4290     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4291 }\r
4292 \r
4293 void ChangeDragPiece(ChessSquare piece)\r
4294 {\r
4295     dragInfo.piece = piece;\r
4296 }\r
4297 \r
4298 /* Event handler for mouse messages */\r
4299 VOID\r
4300 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4301 {\r
4302   int x, y, menuNr;\r
4303   POINT pt;\r
4304   static int recursive = 0;\r
4305   HMENU hmenu;\r
4306   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4307 \r
4308   if (recursive) {\r
4309     if (message == WM_MBUTTONUP) {\r
4310       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4311          to the middle button: we simulate pressing the left button too!\r
4312          */\r
4313       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4314       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4315     }\r
4316     return;\r
4317   }\r
4318   recursive++;\r
4319   \r
4320   pt.x = LOWORD(lParam);\r
4321   pt.y = HIWORD(lParam);\r
4322   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4323   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4324   if (!flipView && y >= 0) {\r
4325     y = BOARD_HEIGHT - 1 - y;\r
4326   }\r
4327   if (flipView && x >= 0) {\r
4328     x = BOARD_WIDTH - 1 - x;\r
4329   }\r
4330 \r
4331   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4332   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4333 \r
4334   switch (message) {\r
4335   case WM_LBUTTONDOWN:\r
4336       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4337         ClockClick(flipClock); break;\r
4338       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4339         ClockClick(!flipClock); break;\r
4340       }\r
4341     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4342       dragInfo.start.x = dragInfo.start.y = -1;\r
4343       dragInfo.from = dragInfo.start;\r
4344     }\r
4345     if(fromX == -1 && frozen) { // not sure where this is for\r
4346                 fromX = fromY = -1; \r
4347       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4348       break;\r
4349     }\r
4350       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4351       DrawPosition(TRUE, NULL);\r
4352     break;\r
4353 \r
4354   case WM_LBUTTONUP:\r
4355       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4356       DrawPosition(TRUE, NULL);\r
4357     break;\r
4358 \r
4359   case WM_MOUSEMOVE:\r
4360     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4361     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4362     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4363     if ((appData.animateDragging || appData.highlightDragging)\r
4364         && (wParam & MK_LBUTTON || dragging == 2)\r
4365         && dragInfo.from.x >= 0) \r
4366     {\r
4367       BOOL full_repaint = FALSE;\r
4368 \r
4369       if (appData.animateDragging) {\r
4370         dragInfo.pos = pt;\r
4371       }\r
4372       if (appData.highlightDragging) {\r
4373         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4374         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4375             full_repaint = TRUE;\r
4376         }\r
4377       }\r
4378       \r
4379       DrawPosition( full_repaint, NULL);\r
4380       \r
4381       dragInfo.lastpos = dragInfo.pos;\r
4382     }\r
4383     break;\r
4384 \r
4385   case WM_MOUSEWHEEL: // [DM]\r
4386     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4387        /* Mouse Wheel is being rolled forward\r
4388         * Play moves forward\r
4389         */\r
4390        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4391                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4392        /* Mouse Wheel is being rolled backward\r
4393         * Play moves backward\r
4394         */\r
4395        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4396                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4397     }\r
4398     break;\r
4399 \r
4400   case WM_MBUTTONUP:\r
4401   case WM_RBUTTONUP:\r
4402     ReleaseCapture();\r
4403     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4404     break;\r
4405  \r
4406   case WM_MBUTTONDOWN:\r
4407   case WM_RBUTTONDOWN:\r
4408     ErrorPopDown();\r
4409     ReleaseCapture();\r
4410     fromX = fromY = -1;\r
4411     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4412     dragInfo.start.x = dragInfo.start.y = -1;\r
4413     dragInfo.from = dragInfo.start;\r
4414     dragInfo.lastpos = dragInfo.pos;\r
4415     if (appData.highlightDragging) {\r
4416       ClearHighlights();\r
4417     }\r
4418     if(y == -2) {\r
4419       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4420       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4421           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4422       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4423           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4424       }\r
4425       break;\r
4426     }\r
4427     DrawPosition(TRUE, NULL);\r
4428 \r
4429     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4430     switch (menuNr) {\r
4431     case 0:\r
4432       if (message == WM_MBUTTONDOWN) {\r
4433         buttonCount = 3;  /* even if system didn't think so */\r
4434         if (wParam & MK_SHIFT) \r
4435           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4436         else\r
4437           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4438       } else { /* message == WM_RBUTTONDOWN */\r
4439         /* Just have one menu, on the right button.  Windows users don't\r
4440            think to try the middle one, and sometimes other software steals\r
4441            it, or it doesn't really exist. */\r
4442         if(gameInfo.variant != VariantShogi)\r
4443             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4444         else\r
4445             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4446       }\r
4447       break;\r
4448     case 2:\r
4449       SetCapture(hwndMain);\r
4450       break;\r
4451     case 1:\r
4452       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4453       SetupDropMenu(hmenu);\r
4454       MenuPopup(hwnd, pt, hmenu, -1);\r
4455     default:\r
4456       break;\r
4457     }\r
4458     break;\r
4459   }\r
4460 \r
4461   recursive--;\r
4462 }\r
4463 \r
4464 /* Preprocess messages for buttons in main window */\r
4465 LRESULT CALLBACK\r
4466 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4467 {\r
4468   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4469   int i, dir;\r
4470 \r
4471   for (i=0; i<N_BUTTONS; i++) {\r
4472     if (buttonDesc[i].id == id) break;\r
4473   }\r
4474   if (i == N_BUTTONS) return 0;\r
4475   switch (message) {\r
4476   case WM_KEYDOWN:\r
4477     switch (wParam) {\r
4478     case VK_LEFT:\r
4479     case VK_RIGHT:\r
4480       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4481       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4482       return TRUE;\r
4483     }\r
4484     break;\r
4485   case WM_CHAR:\r
4486     switch (wParam) {\r
4487     case '\r':\r
4488       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4489       return TRUE;\r
4490     default:\r
4491       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4492         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4493         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4494         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4495         SetFocus(h);\r
4496         SendMessage(h, WM_CHAR, wParam, lParam);\r
4497         return TRUE;\r
4498       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4499         TypeInEvent((char)wParam);\r
4500       }\r
4501       break;\r
4502     }\r
4503     break;\r
4504   }\r
4505   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4506 }\r
4507 \r
4508 static int promoStyle;\r
4509 \r
4510 /* Process messages for Promotion dialog box */\r
4511 LRESULT CALLBACK\r
4512 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4513 {\r
4514   char promoChar;\r
4515 \r
4516   switch (message) {\r
4517 \r
4518   case WM_INITDIALOG: /* message: initialize dialog box */\r
4519     /* Center the dialog over the application window */\r
4520     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4521     Translate(hDlg, DLG_PromotionKing);\r
4522     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4523       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4524        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4525        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4526                SW_SHOW : SW_HIDE);\r
4527     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4528     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4529        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4530          PieceToChar(WhiteAngel) != '~') ||\r
4531         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4532          PieceToChar(BlackAngel) != '~')   ) ?\r
4533                SW_SHOW : SW_HIDE);\r
4534     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4535        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4536          PieceToChar(WhiteMarshall) != '~') ||\r
4537         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4538          PieceToChar(BlackMarshall) != '~')   ) ?\r
4539                SW_SHOW : SW_HIDE);\r
4540     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4541     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4542     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4543     if(promoStyle) {\r
4544         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4545         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4546         SetWindowText(hDlg, "Promote?");\r
4547     }\r
4548     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4549        gameInfo.variant == VariantSuper ?\r
4550                SW_SHOW : SW_HIDE);\r
4551     return TRUE;\r
4552 \r
4553   case WM_COMMAND: /* message: received a command */\r
4554     switch (LOWORD(wParam)) {\r
4555     case IDCANCEL:\r
4556       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4557       ClearHighlights();\r
4558       DrawPosition(FALSE, NULL);\r
4559       return TRUE;\r
4560     case PB_King:\r
4561       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4562       break;\r
4563     case PB_Queen:\r
4564       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4565       break;\r
4566     case PB_Rook:\r
4567       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4568       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4569       break;\r
4570     case PB_Bishop:\r
4571       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4572       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4573       break;\r
4574     case PB_Chancellor:\r
4575       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4576       break;\r
4577     case PB_Archbishop:\r
4578       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4579       break;\r
4580     case PB_Knight:\r
4581       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4582                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4583       break;\r
4584     default:\r
4585       return FALSE;\r
4586     }\r
4587     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4588     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4589     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4590     fromX = fromY = -1;\r
4591     if (!appData.highlightLastMove) {\r
4592       ClearHighlights();\r
4593       DrawPosition(FALSE, NULL);\r
4594     }\r
4595     return TRUE;\r
4596   }\r
4597   return FALSE;\r
4598 }\r
4599 \r
4600 /* Pop up promotion dialog */\r
4601 VOID\r
4602 PromotionPopup(HWND hwnd)\r
4603 {\r
4604   FARPROC lpProc;\r
4605 \r
4606   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4607   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4608     hwnd, (DLGPROC)lpProc);\r
4609   FreeProcInstance(lpProc);\r
4610 }\r
4611 \r
4612 void\r
4613 PromotionPopUp(char choice)\r
4614 {\r
4615   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4616   DrawPosition(TRUE, NULL);\r
4617   PromotionPopup(hwndMain);\r
4618 }\r
4619 \r
4620 VOID\r
4621 LoadGameDialog(HWND hwnd, char* title)\r
4622 {\r
4623   UINT number = 0;\r
4624   FILE *f;\r
4625   char fileTitle[MSG_SIZ];\r
4626   f = OpenFileDialog(hwnd, "rb", "",\r
4627                      appData.oldSaveStyle ? "gam" : "pgn",\r
4628                      GAME_FILT,\r
4629                      title, &number, fileTitle, NULL);\r
4630   if (f != NULL) {\r
4631     cmailMsgLoaded = FALSE;\r
4632     if (number == 0) {\r
4633       int error = GameListBuild(f);\r
4634       if (error) {\r
4635         DisplayError(_("Cannot build game list"), error);\r
4636       } else if (!ListEmpty(&gameList) &&\r
4637                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4638         GameListPopUp(f, fileTitle);\r
4639         return;\r
4640       }\r
4641       GameListDestroy();\r
4642       number = 1;\r
4643     }\r
4644     LoadGame(f, number, fileTitle, FALSE);\r
4645   }\r
4646 }\r
4647 \r
4648 int get_term_width()\r
4649 {\r
4650     HDC hdc;\r
4651     TEXTMETRIC tm;\r
4652     RECT rc;\r
4653     HFONT hfont, hold_font;\r
4654     LOGFONT lf;\r
4655     HWND hText;\r
4656 \r
4657     if (hwndConsole)\r
4658         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4659     else\r
4660         return 79;\r
4661 \r
4662     // get the text metrics\r
4663     hdc = GetDC(hText);\r
4664     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4665     if (consoleCF.dwEffects & CFE_BOLD)\r
4666         lf.lfWeight = FW_BOLD;\r
4667     if (consoleCF.dwEffects & CFE_ITALIC)\r
4668         lf.lfItalic = TRUE;\r
4669     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4670         lf.lfStrikeOut = TRUE;\r
4671     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4672         lf.lfUnderline = TRUE;\r
4673     hfont = CreateFontIndirect(&lf);\r
4674     hold_font = SelectObject(hdc, hfont);\r
4675     GetTextMetrics(hdc, &tm);\r
4676     SelectObject(hdc, hold_font);\r
4677     DeleteObject(hfont);\r
4678     ReleaseDC(hText, hdc);\r
4679 \r
4680     // get the rectangle\r
4681     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4682 \r
4683     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4684 }\r
4685 \r
4686 void UpdateICSWidth(HWND hText)\r
4687 {\r
4688     LONG old_width, new_width;\r
4689 \r
4690     new_width = get_term_width(hText, FALSE);\r
4691     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4692     if (new_width != old_width)\r
4693     {\r
4694         ics_update_width(new_width);\r
4695         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4696     }\r
4697 }\r
4698 \r
4699 VOID\r
4700 ChangedConsoleFont()\r
4701 {\r
4702   CHARFORMAT cfmt;\r
4703   CHARRANGE tmpsel, sel;\r
4704   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4705   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4706   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4707   PARAFORMAT paraf;\r
4708 \r
4709   cfmt.cbSize = sizeof(CHARFORMAT);\r
4710   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4711     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4712                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4713   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4714    * size.  This was undocumented in the version of MSVC++ that I had\r
4715    * when I wrote the code, but is apparently documented now.\r
4716    */\r
4717   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4718   cfmt.bCharSet = f->lf.lfCharSet;\r
4719   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4720   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4721   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4722   /* Why are the following seemingly needed too? */\r
4723   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4724   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4725   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4726   tmpsel.cpMin = 0;\r
4727   tmpsel.cpMax = -1; /*999999?*/\r
4728   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4729   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4730   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4731    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4732    */\r
4733   paraf.cbSize = sizeof(paraf);\r
4734   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4735   paraf.dxStartIndent = 0;\r
4736   paraf.dxOffset = WRAP_INDENT;\r
4737   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4738   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4739   UpdateICSWidth(hText);\r
4740 }\r
4741 \r
4742 /*---------------------------------------------------------------------------*\\r
4743  *\r
4744  * Window Proc for main window\r
4745  *\r
4746 \*---------------------------------------------------------------------------*/\r
4747 \r
4748 /* Process messages for main window, etc. */\r
4749 LRESULT CALLBACK\r
4750 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4751 {\r
4752   FARPROC lpProc;\r
4753   int wmId;\r
4754   char *defName;\r
4755   FILE *f;\r
4756   UINT number;\r
4757   char fileTitle[MSG_SIZ];\r
4758   static SnapData sd;\r
4759   static int peek=0;\r
4760 \r
4761   switch (message) {\r
4762 \r
4763   case WM_PAINT: /* message: repaint portion of window */\r
4764     PaintProc(hwnd);\r
4765     break;\r
4766 \r
4767   case WM_ERASEBKGND:\r
4768     if (IsIconic(hwnd)) {\r
4769       /* Cheat; change the message */\r
4770       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4771     } else {\r
4772       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4773     }\r
4774     break;\r
4775 \r
4776   case WM_LBUTTONDOWN:\r
4777   case WM_MBUTTONDOWN:\r
4778   case WM_RBUTTONDOWN:\r
4779   case WM_LBUTTONUP:\r
4780   case WM_MBUTTONUP:\r
4781   case WM_RBUTTONUP:\r
4782   case WM_MOUSEMOVE:\r
4783   case WM_MOUSEWHEEL:\r
4784     MouseEvent(hwnd, message, wParam, lParam);\r
4785     break;\r
4786 \r
4787   case WM_KEYUP:\r
4788     if((char)wParam == '\b') {\r
4789       ForwardEvent(); peek = 0;\r
4790     }\r
4791 \r
4792     JAWS_KBUP_NAVIGATION\r
4793 \r
4794     break;\r
4795 \r
4796   case WM_KEYDOWN:\r
4797     if((char)wParam == '\b') {\r
4798       if(!peek) BackwardEvent(), peek = 1;\r
4799     }\r
4800 \r
4801     JAWS_KBDOWN_NAVIGATION\r
4802 \r
4803     break;\r
4804 \r
4805   case WM_CHAR:\r
4806     \r
4807     JAWS_ALT_INTERCEPT\r
4808 \r
4809     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4810         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4811         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4812         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4813         SetFocus(h);\r
4814         SendMessage(h, message, wParam, lParam);\r
4815     } else if(lParam != KF_REPEAT) {\r
4816         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4817                 TypeInEvent((char)wParam);\r
4818         } else if((char)wParam == 003) CopyGameToClipboard();\r
4819          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4820     }\r
4821 \r
4822     break;\r
4823 \r
4824   case WM_PALETTECHANGED:\r
4825     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4826       int nnew;\r
4827       HDC hdc = GetDC(hwndMain);\r
4828       SelectPalette(hdc, hPal, TRUE);\r
4829       nnew = RealizePalette(hdc);\r
4830       if (nnew > 0) {\r
4831         paletteChanged = TRUE;\r
4832 \r
4833         InvalidateRect(hwnd, &boardRect, FALSE);\r
4834       }\r
4835       ReleaseDC(hwnd, hdc);\r
4836     }\r
4837     break;\r
4838 \r
4839   case WM_QUERYNEWPALETTE:\r
4840     if (!appData.monoMode /*&& paletteChanged*/) {\r
4841       int nnew;\r
4842       HDC hdc = GetDC(hwndMain);\r
4843       paletteChanged = FALSE;\r
4844       SelectPalette(hdc, hPal, FALSE);\r
4845       nnew = RealizePalette(hdc);\r
4846       if (nnew > 0) {\r
4847         InvalidateRect(hwnd, &boardRect, FALSE);\r
4848       }\r
4849       ReleaseDC(hwnd, hdc);\r
4850       return TRUE;\r
4851     }\r
4852     return FALSE;\r
4853 \r
4854   case WM_COMMAND: /* message: command from application menu */\r
4855     wmId    = LOWORD(wParam);\r
4856 \r
4857     switch (wmId) {\r
4858     case IDM_NewGame:\r
4859       ResetGameEvent();\r
4860       SAY("new game enter a move to play against the computer with white");\r
4861       break;\r
4862 \r
4863     case IDM_NewGameFRC:\r
4864       if( NewGameFRC() == 0 ) {\r
4865         ResetGameEvent();\r
4866       }\r
4867       break;\r
4868 \r
4869     case IDM_NewVariant:\r
4870       NewVariantPopup(hwnd);\r
4871       break;\r
4872 \r
4873     case IDM_LoadGame:\r
4874       LoadGameDialog(hwnd, _("Load Game from File"));\r
4875       break;\r
4876 \r
4877     case IDM_LoadNextGame:\r
4878       ReloadGame(1);\r
4879       break;\r
4880 \r
4881     case IDM_LoadPrevGame:\r
4882       ReloadGame(-1);\r
4883       break;\r
4884 \r
4885     case IDM_ReloadGame:\r
4886       ReloadGame(0);\r
4887       break;\r
4888 \r
4889     case IDM_LoadPosition:\r
4890       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4891         Reset(FALSE, TRUE);\r
4892       }\r
4893       number = 1;\r
4894       f = OpenFileDialog(hwnd, "rb", "",\r
4895                          appData.oldSaveStyle ? "pos" : "fen",\r
4896                          POSITION_FILT,\r
4897                          _("Load Position from File"), &number, fileTitle, NULL);\r
4898       if (f != NULL) {\r
4899         LoadPosition(f, number, fileTitle);\r
4900       }\r
4901       break;\r
4902 \r
4903     case IDM_LoadNextPosition:\r
4904       ReloadPosition(1);\r
4905       break;\r
4906 \r
4907     case IDM_LoadPrevPosition:\r
4908       ReloadPosition(-1);\r
4909       break;\r
4910 \r
4911     case IDM_ReloadPosition:\r
4912       ReloadPosition(0);\r
4913       break;\r
4914 \r
4915     case IDM_SaveGame:\r
4916       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4917       f = OpenFileDialog(hwnd, "a", defName,\r
4918                          appData.oldSaveStyle ? "gam" : "pgn",\r
4919                          GAME_FILT,\r
4920                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4921       if (f != NULL) {\r
4922         SaveGame(f, 0, "");\r
4923       }\r
4924       break;\r
4925 \r
4926     case IDM_SavePosition:\r
4927       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4928       f = OpenFileDialog(hwnd, "a", defName,\r
4929                          appData.oldSaveStyle ? "pos" : "fen",\r
4930                          POSITION_FILT,\r
4931                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4932       if (f != NULL) {\r
4933         SavePosition(f, 0, "");\r
4934       }\r
4935       break;\r
4936 \r
4937     case IDM_SaveDiagram:\r
4938       defName = "diagram";\r
4939       f = OpenFileDialog(hwnd, "wb", defName,\r
4940                          "bmp",\r
4941                          DIAGRAM_FILT,\r
4942                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4943       if (f != NULL) {\r
4944         SaveDiagram(f);\r
4945       }\r
4946       break;\r
4947 \r
4948     case IDM_SaveSelected:\r
4949       f = OpenFileDialog(hwnd, "a", "",\r
4950                          "pgn",\r
4951                          GAME_FILT,\r
4952                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4953       if (f != NULL) {\r
4954         SaveSelected(f, 0, "");\r
4955       }\r
4956       break;\r
4957 \r
4958     case IDM_CreateBook:\r
4959       CreateBookEvent();\r
4960       break;\r
4961 \r
4962     case IDM_CopyGame:\r
4963       CopyGameToClipboard();\r
4964       break;\r
4965 \r
4966     case IDM_PasteGame:\r
4967       PasteGameFromClipboard();\r
4968       break;\r
4969 \r
4970     case IDM_CopyGameListToClipboard:\r
4971       CopyGameListToClipboard();\r
4972       break;\r
4973 \r
4974     /* [AS] Autodetect FEN or PGN data */\r
4975     case IDM_PasteAny:\r
4976       PasteGameOrFENFromClipboard();\r
4977       break;\r
4978 \r
4979     /* [AS] Move history */\r
4980     case IDM_ShowMoveHistory:\r
4981         if( MoveHistoryIsUp() ) {\r
4982             MoveHistoryPopDown();\r
4983         }\r
4984         else {\r
4985             MoveHistoryPopUp();\r
4986         }\r
4987         break;\r
4988 \r
4989     /* [AS] Eval graph */\r
4990     case IDM_ShowEvalGraph:\r
4991         if( EvalGraphIsUp() ) {\r
4992             EvalGraphPopDown();\r
4993         }\r
4994         else {\r
4995             EvalGraphPopUp();\r
4996             SetFocus(hwndMain);\r
4997         }\r
4998         break;\r
4999 \r
5000     /* [AS] Engine output */\r
5001     case IDM_ShowEngineOutput:\r
5002         if( EngineOutputIsUp() ) {\r
5003             EngineOutputPopDown();\r
5004         }\r
5005         else {\r
5006             EngineOutputPopUp();\r
5007         }\r
5008         break;\r
5009 \r
5010     /* [AS] User adjudication */\r
5011     case IDM_UserAdjudication_White:\r
5012         UserAdjudicationEvent( +1 );\r
5013         break;\r
5014 \r
5015     case IDM_UserAdjudication_Black:\r
5016         UserAdjudicationEvent( -1 );\r
5017         break;\r
5018 \r
5019     case IDM_UserAdjudication_Draw:\r
5020         UserAdjudicationEvent( 0 );\r
5021         break;\r
5022 \r
5023     /* [AS] Game list options dialog */\r
5024     case IDM_GameListOptions:\r
5025       GameListOptions();\r
5026       break;\r
5027 \r
5028     case IDM_NewChat:\r
5029       ChatPopUp(NULL);\r
5030       break;\r
5031 \r
5032     case IDM_CopyPosition:\r
5033       CopyFENToClipboard();\r
5034       break;\r
5035 \r
5036     case IDM_PastePosition:\r
5037       PasteFENFromClipboard();\r
5038       break;\r
5039 \r
5040     case IDM_MailMove:\r
5041       MailMoveEvent();\r
5042       break;\r
5043 \r
5044     case IDM_ReloadCMailMsg:\r
5045       Reset(TRUE, TRUE);\r
5046       ReloadCmailMsgEvent(FALSE);\r
5047       break;\r
5048 \r
5049     case IDM_Minimize:\r
5050       ShowWindow(hwnd, SW_MINIMIZE);\r
5051       break;\r
5052 \r
5053     case IDM_Exit:\r
5054       ExitEvent(0);\r
5055       break;\r
5056 \r
5057     case IDM_MachineWhite:\r
5058       MachineWhiteEvent();\r
5059       /*\r
5060        * refresh the tags dialog only if it's visible\r
5061        */\r
5062       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5063           char *tags;\r
5064           tags = PGNTags(&gameInfo);\r
5065           TagsPopUp(tags, CmailMsg());\r
5066           free(tags);\r
5067       }\r
5068       SAY("computer starts playing white");\r
5069       break;\r
5070 \r
5071     case IDM_MachineBlack:\r
5072       MachineBlackEvent();\r
5073       /*\r
5074        * refresh the tags dialog only if it's visible\r
5075        */\r
5076       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5077           char *tags;\r
5078           tags = PGNTags(&gameInfo);\r
5079           TagsPopUp(tags, CmailMsg());\r
5080           free(tags);\r
5081       }\r
5082       SAY("computer starts playing black");\r
5083       break;\r
5084 \r
5085     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5086       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5087       break;\r
5088 \r
5089     case IDM_TwoMachines:\r
5090       TwoMachinesEvent();\r
5091       /*\r
5092 \r
5093        * refresh the tags dialog only if it's visible\r
5094        */\r
5095       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5096           char *tags;\r
5097           tags = PGNTags(&gameInfo);\r
5098           TagsPopUp(tags, CmailMsg());\r
5099           free(tags);\r
5100       }\r
5101       SAY("computer starts playing both sides");\r
5102       break;\r
5103 \r
5104     case IDM_AnalysisMode:\r
5105       if(AnalyzeModeEvent()) {\r
5106         SAY("analyzing current position");\r
5107       }\r
5108       break;\r
5109 \r
5110     case IDM_AnalyzeFile:\r
5111       AnalyzeFileEvent();\r
5112       break;\r
5113 \r
5114     case IDM_IcsClient:\r
5115       IcsClientEvent();\r
5116       break;\r
5117 \r
5118     case IDM_EditGame:\r
5119     case IDM_EditGame2:\r
5120       EditGameEvent();\r
5121       SAY("edit game");\r
5122       break;\r
5123 \r
5124     case IDM_EditPosition:\r
5125     case IDM_EditPosition2:\r
5126       EditPositionEvent();\r
5127       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5128       break;\r
5129 \r
5130     case IDM_Training:\r
5131       TrainingEvent();\r
5132       break;\r
5133 \r
5134     case IDM_ShowGameList:\r
5135       ShowGameListProc();\r
5136       break;\r
5137 \r
5138     case IDM_EditProgs1:\r
5139       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5140       break;\r
5141 \r
5142     case IDM_LoadProg1:\r
5143      LoadEnginePopUp(hwndMain, 0);\r
5144       break;\r
5145 \r
5146     case IDM_LoadProg2:\r
5147      LoadEnginePopUp(hwndMain, 1);\r
5148       break;\r
5149 \r
5150     case IDM_EditServers:\r
5151       EditTagsPopUp(icsNames, &icsNames);\r
5152       break;\r
5153 \r
5154     case IDM_EditTags:\r
5155     case IDM_Tags:\r
5156       EditTagsProc();\r
5157       break;\r
5158 \r
5159     case IDM_EditBook:\r
5160       EditBookEvent();\r
5161       break;\r
5162 \r
5163     case IDM_EditComment:\r
5164     case IDM_Comment:\r
5165       if (commentUp && editComment) {\r
5166         CommentPopDown();\r
5167       } else {\r
5168         EditCommentEvent();\r
5169       }\r
5170       break;\r
5171 \r
5172     case IDM_Pause:\r
5173       PauseEvent();\r
5174       break;\r
5175 \r
5176     case IDM_Accept:\r
5177       AcceptEvent();\r
5178       break;\r
5179 \r
5180     case IDM_Decline:\r
5181       DeclineEvent();\r
5182       break;\r
5183 \r
5184     case IDM_Rematch:\r
5185 \r
5186       RematchEvent();\r
5187       break;\r
5188 \r
5189     case IDM_CallFlag:\r
5190       CallFlagEvent();\r
5191       break;\r
5192 \r
5193     case IDM_Draw:\r
5194       DrawEvent();\r
5195       break;\r
5196 \r
5197     case IDM_Adjourn:\r
5198       AdjournEvent();\r
5199       break;\r
5200 \r
5201     case IDM_Abort:\r
5202       AbortEvent();\r
5203       break;\r
5204 \r
5205     case IDM_Resign:\r
5206       ResignEvent();\r
5207       break;\r
5208 \r
5209     case IDM_StopObserving:\r
5210       StopObservingEvent();\r
5211       break;\r
5212 \r
5213     case IDM_StopExamining:\r
5214       StopExaminingEvent();\r
5215       break;\r
5216 \r
5217     case IDM_Upload:\r
5218       UploadGameEvent();\r
5219       break;\r
5220 \r
5221     case IDM_TypeInMove:\r
5222       TypeInEvent('\000');\r
5223       break;\r
5224 \r
5225     case IDM_TypeInName:\r
5226       PopUpNameDialog('\000');\r
5227       break;\r
5228 \r
5229     case IDM_Backward:\r
5230       BackwardEvent();\r
5231       SetFocus(hwndMain);\r
5232       break;\r
5233 \r
5234     JAWS_MENU_ITEMS\r
5235 \r
5236     case IDM_Forward:\r
5237       ForwardEvent();\r
5238       SetFocus(hwndMain);\r
5239       break;\r
5240 \r
5241     case IDM_ToStart:\r
5242       ToStartEvent();\r
5243       SetFocus(hwndMain);\r
5244       break;\r
5245 \r
5246     case IDM_ToEnd:\r
5247       ToEndEvent();\r
5248       SetFocus(hwndMain);\r
5249       break;\r
5250 \r
5251     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5252     case OPT_GameListPrev:\r
5253       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5254       break;\r
5255 \r
5256     case IDM_Revert:\r
5257       RevertEvent(FALSE);\r
5258       break;\r
5259 \r
5260     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5261       RevertEvent(TRUE);\r
5262       break;\r
5263 \r
5264     case IDM_TruncateGame:\r
5265       TruncateGameEvent();\r
5266       break;\r
5267 \r
5268     case IDM_MoveNow:\r
5269       MoveNowEvent();\r
5270       break;\r
5271 \r
5272     case IDM_RetractMove:\r
5273       RetractMoveEvent();\r
5274       break;\r
5275 \r
5276     case IDM_FlipView:\r
5277       flipView = !flipView;\r
5278       DrawPosition(FALSE, NULL);\r
5279       break;\r
5280 \r
5281     case IDM_FlipClock:\r
5282       flipClock = !flipClock;\r
5283       DisplayBothClocks();\r
5284       DisplayLogos();\r
5285       break;\r
5286 \r
5287     case IDM_MuteSounds:\r
5288       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5289       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5290                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5291       break;\r
5292 \r
5293     case IDM_GeneralOptions:\r
5294       GeneralOptionsPopup(hwnd);\r
5295       DrawPosition(TRUE, NULL);\r
5296       break;\r
5297 \r
5298     case IDM_BoardOptions:\r
5299       BoardOptionsPopup(hwnd);\r
5300       break;\r
5301 \r
5302     case IDM_ThemeOptions:\r
5303       ThemeOptionsPopup(hwnd);\r
5304       break;\r
5305 \r
5306     case IDM_EnginePlayOptions:\r
5307       EnginePlayOptionsPopup(hwnd);\r
5308       break;\r
5309 \r
5310     case IDM_Engine1Options:\r
5311       EngineOptionsPopup(hwnd, &first);\r
5312       break;\r
5313 \r
5314     case IDM_Engine2Options:\r
5315       savedHwnd = hwnd;\r
5316       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5317       EngineOptionsPopup(hwnd, &second);\r
5318       break;\r
5319 \r
5320     case IDM_OptionsUCI:\r
5321       UciOptionsPopup(hwnd);\r
5322       break;\r
5323 \r
5324     case IDM_Tourney:\r
5325       TourneyPopup(hwnd);\r
5326       break;\r
5327 \r
5328     case IDM_IcsOptions:\r
5329       IcsOptionsPopup(hwnd);\r
5330       break;\r
5331 \r
5332     case IDM_Fonts:\r
5333       FontsOptionsPopup(hwnd);\r
5334       break;\r
5335 \r
5336     case IDM_Sounds:\r
5337       SoundOptionsPopup(hwnd);\r
5338       break;\r
5339 \r
5340     case IDM_CommPort:\r
5341       CommPortOptionsPopup(hwnd);\r
5342       break;\r
5343 \r
5344     case IDM_LoadOptions:\r
5345       LoadOptionsPopup(hwnd);\r
5346       break;\r
5347 \r
5348     case IDM_SaveOptions:\r
5349       SaveOptionsPopup(hwnd);\r
5350       break;\r
5351 \r
5352     case IDM_TimeControl:\r
5353       TimeControlOptionsPopup(hwnd);\r
5354       break;\r
5355 \r
5356     case IDM_SaveSettings:\r
5357       SaveSettings(settingsFileName);\r
5358       break;\r
5359 \r
5360     case IDM_SaveSettingsOnExit:\r
5361       saveSettingsOnExit = !saveSettingsOnExit;\r
5362       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5363                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5364                                          MF_CHECKED : MF_UNCHECKED));\r
5365       break;\r
5366 \r
5367     case IDM_Hint:\r
5368       HintEvent();\r
5369       break;\r
5370 \r
5371     case IDM_Book:\r
5372       BookEvent();\r
5373       break;\r
5374 \r
5375     case IDM_AboutGame:\r
5376       AboutGameEvent();\r
5377       break;\r
5378 \r
5379     case IDM_Debug:\r
5380       appData.debugMode = !appData.debugMode;\r
5381       if (appData.debugMode) {\r
5382         char dir[MSG_SIZ];\r
5383         GetCurrentDirectory(MSG_SIZ, dir);\r
5384         SetCurrentDirectory(installDir);\r
5385         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5386         SetCurrentDirectory(dir);\r
5387         setbuf(debugFP, NULL);\r
5388       } else {\r
5389         fclose(debugFP);\r
5390         debugFP = NULL;\r
5391       }\r
5392       break;\r
5393 \r
5394     case IDM_HELPCONTENTS:\r
5395       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5396           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5397           MessageBox (GetFocus(),\r
5398                     _("Unable to activate help"),\r
5399                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5400       }\r
5401       break;\r
5402 \r
5403     case IDM_HELPSEARCH:\r
5404         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5405             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5406         MessageBox (GetFocus(),\r
5407                     _("Unable to activate help"),\r
5408                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5409       }\r
5410       break;\r
5411 \r
5412     case IDM_HELPHELP:\r
5413       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5414         MessageBox (GetFocus(),\r
5415                     _("Unable to activate help"),\r
5416                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5417       }\r
5418       break;\r
5419 \r
5420     case IDM_ABOUT:\r
5421       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5422       DialogBox(hInst, \r
5423         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5424         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5425       FreeProcInstance(lpProc);\r
5426       break;\r
5427 \r
5428     case IDM_DirectCommand1:\r
5429       AskQuestionEvent(_("Direct Command"),\r
5430                        _("Send to chess program:"), "", "1");\r
5431       break;\r
5432     case IDM_DirectCommand2:\r
5433       AskQuestionEvent(_("Direct Command"),\r
5434                        _("Send to second chess program:"), "", "2");\r
5435       break;\r
5436 \r
5437     case EP_WhitePawn:\r
5438       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5439       fromX = fromY = -1;\r
5440       break;\r
5441 \r
5442     case EP_WhiteKnight:\r
5443       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5444       fromX = fromY = -1;\r
5445       break;\r
5446 \r
5447     case EP_WhiteBishop:\r
5448       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5449       fromX = fromY = -1;\r
5450       break;\r
5451 \r
5452     case EP_WhiteRook:\r
5453       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5454       fromX = fromY = -1;\r
5455       break;\r
5456 \r
5457     case EP_WhiteQueen:\r
5458       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5459       fromX = fromY = -1;\r
5460       break;\r
5461 \r
5462     case EP_WhiteFerz:\r
5463       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5464       fromX = fromY = -1;\r
5465       break;\r
5466 \r
5467     case EP_WhiteWazir:\r
5468       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5469       fromX = fromY = -1;\r
5470       break;\r
5471 \r
5472     case EP_WhiteAlfil:\r
5473       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5474       fromX = fromY = -1;\r
5475       break;\r
5476 \r
5477     case EP_WhiteCannon:\r
5478       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5479       fromX = fromY = -1;\r
5480       break;\r
5481 \r
5482     case EP_WhiteCardinal:\r
5483       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5484       fromX = fromY = -1;\r
5485       break;\r
5486 \r
5487     case EP_WhiteMarshall:\r
5488       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5489       fromX = fromY = -1;\r
5490       break;\r
5491 \r
5492     case EP_WhiteKing:\r
5493       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5494       fromX = fromY = -1;\r
5495       break;\r
5496 \r
5497     case EP_BlackPawn:\r
5498       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5499       fromX = fromY = -1;\r
5500       break;\r
5501 \r
5502     case EP_BlackKnight:\r
5503       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5504       fromX = fromY = -1;\r
5505       break;\r
5506 \r
5507     case EP_BlackBishop:\r
5508       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5509       fromX = fromY = -1;\r
5510       break;\r
5511 \r
5512     case EP_BlackRook:\r
5513       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5514       fromX = fromY = -1;\r
5515       break;\r
5516 \r
5517     case EP_BlackQueen:\r
5518       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5519       fromX = fromY = -1;\r
5520       break;\r
5521 \r
5522     case EP_BlackFerz:\r
5523       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5524       fromX = fromY = -1;\r
5525       break;\r
5526 \r
5527     case EP_BlackWazir:\r
5528       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5529       fromX = fromY = -1;\r
5530       break;\r
5531 \r
5532     case EP_BlackAlfil:\r
5533       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5534       fromX = fromY = -1;\r
5535       break;\r
5536 \r
5537     case EP_BlackCannon:\r
5538       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5539       fromX = fromY = -1;\r
5540       break;\r
5541 \r
5542     case EP_BlackCardinal:\r
5543       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5544       fromX = fromY = -1;\r
5545       break;\r
5546 \r
5547     case EP_BlackMarshall:\r
5548       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5549       fromX = fromY = -1;\r
5550       break;\r
5551 \r
5552     case EP_BlackKing:\r
5553       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5554       fromX = fromY = -1;\r
5555       break;\r
5556 \r
5557     case EP_EmptySquare:\r
5558       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5559       fromX = fromY = -1;\r
5560       break;\r
5561 \r
5562     case EP_ClearBoard:\r
5563       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5564       fromX = fromY = -1;\r
5565       break;\r
5566 \r
5567     case EP_White:\r
5568       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5569       fromX = fromY = -1;\r
5570       break;\r
5571 \r
5572     case EP_Black:\r
5573       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5574       fromX = fromY = -1;\r
5575       break;\r
5576 \r
5577     case EP_Promote:\r
5578       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5579       fromX = fromY = -1;\r
5580       break;\r
5581 \r
5582     case EP_Demote:\r
5583       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5584       fromX = fromY = -1;\r
5585       break;\r
5586 \r
5587     case DP_Pawn:\r
5588       DropMenuEvent(WhitePawn, fromX, fromY);\r
5589       fromX = fromY = -1;\r
5590       break;\r
5591 \r
5592     case DP_Knight:\r
5593       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5594       fromX = fromY = -1;\r
5595       break;\r
5596 \r
5597     case DP_Bishop:\r
5598       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5599       fromX = fromY = -1;\r
5600       break;\r
5601 \r
5602     case DP_Rook:\r
5603       DropMenuEvent(WhiteRook, fromX, fromY);\r
5604       fromX = fromY = -1;\r
5605       break;\r
5606 \r
5607     case DP_Queen:\r
5608       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5609       fromX = fromY = -1;\r
5610       break;\r
5611 \r
5612     case IDM_English:\r
5613       barbaric = 0; appData.language = "";\r
5614       TranslateMenus(0);\r
5615       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5616       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5617       lastChecked = wmId;\r
5618       break;\r
5619 \r
5620     default:\r
5621       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5622           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5623       else\r
5624       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5625           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5626           TranslateMenus(0);\r
5627           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5628           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5629           lastChecked = wmId;\r
5630           break;\r
5631       }\r
5632       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5633     }\r
5634     break;\r
5635 \r
5636   case WM_TIMER:\r
5637     switch (wParam) {\r
5638     case CLOCK_TIMER_ID:\r
5639       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5640       clockTimerEvent = 0;\r
5641       DecrementClocks(); /* call into back end */\r
5642       break;\r
5643     case LOAD_GAME_TIMER_ID:\r
5644       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5645       loadGameTimerEvent = 0;\r
5646       AutoPlayGameLoop(); /* call into back end */\r
5647       break;\r
5648     case ANALYSIS_TIMER_ID:\r
5649       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5650                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5651         AnalysisPeriodicEvent(0);\r
5652       } else {\r
5653         KillTimer(hwnd, analysisTimerEvent);\r
5654         analysisTimerEvent = 0;\r
5655       }\r
5656       break;\r
5657     case DELAYED_TIMER_ID:\r
5658       KillTimer(hwnd, delayedTimerEvent);\r
5659       delayedTimerEvent = 0;\r
5660       delayedTimerCallback();\r
5661       break;\r
5662     }\r
5663     break;\r
5664 \r
5665   case WM_USER_Input:\r
5666     InputEvent(hwnd, message, wParam, lParam);\r
5667     break;\r
5668 \r
5669   /* [AS] Also move "attached" child windows */\r
5670   case WM_WINDOWPOSCHANGING:\r
5671 \r
5672     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5673         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5674 \r
5675         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5676             /* Window is moving */\r
5677             RECT rcMain;\r
5678 \r
5679 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5680             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5681             rcMain.right  = wpMain.x + wpMain.width;\r
5682             rcMain.top    = wpMain.y;\r
5683             rcMain.bottom = wpMain.y + wpMain.height;\r
5684             \r
5685             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5686             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5687             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5688             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5689             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5690             wpMain.x = lpwp->x;\r
5691             wpMain.y = lpwp->y;\r
5692         }\r
5693     }\r
5694     break;\r
5695 \r
5696   /* [AS] Snapping */\r
5697   case WM_ENTERSIZEMOVE:\r
5698     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5699     if (hwnd == hwndMain) {\r
5700       doingSizing = TRUE;\r
5701       lastSizing = 0;\r
5702     }\r
5703     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5704     break;\r
5705 \r
5706   case WM_SIZING:\r
5707     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5708     if (hwnd == hwndMain) {\r
5709       lastSizing = wParam;\r
5710     }\r
5711     break;\r
5712 \r
5713   case WM_MOVING:\r
5714     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5715       return OnMoving( &sd, hwnd, wParam, lParam );\r
5716 \r
5717   case WM_EXITSIZEMOVE:\r
5718     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5719     if (hwnd == hwndMain) {\r
5720       RECT client;\r
5721       doingSizing = FALSE;\r
5722       InvalidateRect(hwnd, &boardRect, FALSE);\r
5723       GetClientRect(hwnd, &client);\r
5724       ResizeBoard(client.right, client.bottom, lastSizing);\r
5725       lastSizing = 0;\r
5726       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5727     }\r
5728     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5729     break;\r
5730 \r
5731   case WM_DESTROY: /* message: window being destroyed */\r
5732     PostQuitMessage(0);\r
5733     break;\r
5734 \r
5735   case WM_CLOSE:\r
5736     if (hwnd == hwndMain) {\r
5737       ExitEvent(0);\r
5738     }\r
5739     break;\r
5740 \r
5741   default:      /* Passes it on if unprocessed */\r
5742     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5743   }\r
5744 \r
5745 \r
5746   return 0;\r
5747 }\r
5748 \r
5749 /*---------------------------------------------------------------------------*\\r
5750  *\r
5751  * Misc utility routines\r
5752  *\r
5753 \*---------------------------------------------------------------------------*/\r
5754 \r
5755 /*\r
5756  * Decent random number generator, at least not as bad as Windows\r
5757  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5758  */\r
5759 unsigned int randstate;\r
5760 \r
5761 int\r
5762 myrandom(void)\r
5763 {\r
5764   randstate = randstate * 1664525 + 1013904223;\r
5765   return (int) randstate & 0x7fffffff;\r
5766 }\r
5767 \r
5768 void\r
5769 mysrandom(unsigned int seed)\r
5770 {\r
5771   randstate = seed;\r
5772 }\r
5773 \r
5774 \r
5775 /* \r
5776  * returns TRUE if user selects a different color, FALSE otherwise \r
5777  */\r
5778 \r
5779 BOOL\r
5780 ChangeColor(HWND hwnd, COLORREF *which)\r
5781 {\r
5782   static BOOL firstTime = TRUE;\r
5783   static DWORD customColors[16];\r
5784   CHOOSECOLOR cc;\r
5785   COLORREF newcolor;\r
5786   int i;\r
5787   ColorClass ccl;\r
5788 \r
5789   if (firstTime) {\r
5790     /* Make initial colors in use available as custom colors */\r
5791     /* Should we put the compiled-in defaults here instead? */\r
5792     i = 0;\r
5793     customColors[i++] = lightSquareColor & 0xffffff;\r
5794     customColors[i++] = darkSquareColor & 0xffffff;\r
5795     customColors[i++] = whitePieceColor & 0xffffff;\r
5796     customColors[i++] = blackPieceColor & 0xffffff;\r
5797     customColors[i++] = highlightSquareColor & 0xffffff;\r
5798     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5799 \r
5800     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5801       customColors[i++] = textAttribs[ccl].color;\r
5802     }\r
5803     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5804     firstTime = FALSE;\r
5805   }\r
5806 \r
5807   cc.lStructSize = sizeof(cc);\r
5808   cc.hwndOwner = hwnd;\r
5809   cc.hInstance = NULL;\r
5810   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5811   cc.lpCustColors = (LPDWORD) customColors;\r
5812   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5813 \r
5814   if (!ChooseColor(&cc)) return FALSE;\r
5815 \r
5816   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5817   if (newcolor == *which) return FALSE;\r
5818   *which = newcolor;\r
5819   return TRUE;\r
5820 \r
5821   /*\r
5822   InitDrawingColors();\r
5823   InvalidateRect(hwnd, &boardRect, FALSE);\r
5824   */\r
5825 }\r
5826 \r
5827 BOOLEAN\r
5828 MyLoadSound(MySound *ms)\r
5829 {\r
5830   BOOL ok = FALSE;\r
5831   struct stat st;\r
5832   FILE *f;\r
5833 \r
5834   if (ms->data && ms->flag) free(ms->data);\r
5835   ms->data = NULL;\r
5836 \r
5837   switch (ms->name[0]) {\r
5838   case NULLCHAR:\r
5839     /* Silence */\r
5840     ok = TRUE;\r
5841     break;\r
5842   case '$':\r
5843     /* System sound from Control Panel.  Don't preload here. */\r
5844     ok = TRUE;\r
5845     break;\r
5846   case '!':\r
5847     if (ms->name[1] == NULLCHAR) {\r
5848       /* "!" alone = silence */\r
5849       ok = TRUE;\r
5850     } else {\r
5851       /* Builtin wave resource.  Error if not found. */\r
5852       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5853       if (h == NULL) break;\r
5854       ms->data = (void *)LoadResource(hInst, h);\r
5855       ms->flag = 0; // not maloced, so cannot be freed!\r
5856       if (h == NULL) break;\r
5857       ok = TRUE;\r
5858     }\r
5859     break;\r
5860   default:\r
5861     /* .wav file.  Error if not found. */\r
5862     f = fopen(ms->name, "rb");\r
5863     if (f == NULL) break;\r
5864     if (fstat(fileno(f), &st) < 0) break;\r
5865     ms->data = malloc(st.st_size);\r
5866     ms->flag = 1;\r
5867     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5868     fclose(f);\r
5869     ok = TRUE;\r
5870     break;\r
5871   }\r
5872   if (!ok) {\r
5873     char buf[MSG_SIZ];\r
5874       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5875     DisplayError(buf, GetLastError());\r
5876   }\r
5877   return ok;\r
5878 }\r
5879 \r
5880 BOOLEAN\r
5881 MyPlaySound(MySound *ms)\r
5882 {\r
5883   BOOLEAN ok = FALSE;\r
5884 \r
5885   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5886   switch (ms->name[0]) {\r
5887   case NULLCHAR:\r
5888         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5889     /* Silence */\r
5890     ok = TRUE;\r
5891     break;\r
5892   case '$':\r
5893     /* System sound from Control Panel (deprecated feature).\r
5894        "$" alone or an unset sound name gets default beep (still in use). */\r
5895     if (ms->name[1]) {\r
5896       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5897     }\r
5898     if (!ok) ok = MessageBeep(MB_OK);\r
5899     break; \r
5900   case '!':\r
5901     /* Builtin wave resource, or "!" alone for silence */\r
5902     if (ms->name[1]) {\r
5903       if (ms->data == NULL) return FALSE;\r
5904       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5905     } else {\r
5906       ok = TRUE;\r
5907     }\r
5908     break;\r
5909   default:\r
5910     /* .wav file.  Error if not found. */\r
5911     if (ms->data == NULL) return FALSE;\r
5912     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5913     break;\r
5914   }\r
5915   /* Don't print an error: this can happen innocently if the sound driver\r
5916      is busy; for instance, if another instance of WinBoard is playing\r
5917      a sound at about the same time. */\r
5918   return ok;\r
5919 }\r
5920 \r
5921 \r
5922 LRESULT CALLBACK\r
5923 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5924 {\r
5925   BOOL ok;\r
5926   OPENFILENAME *ofn;\r
5927   static UINT *number; /* gross that this is static */\r
5928 \r
5929   switch (message) {\r
5930   case WM_INITDIALOG: /* message: initialize dialog box */\r
5931     /* Center the dialog over the application window */\r
5932     ofn = (OPENFILENAME *) lParam;\r
5933     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5934       number = (UINT *) ofn->lCustData;\r
5935       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5936     } else {\r
5937       number = NULL;\r
5938     }\r
5939     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5940     Translate(hDlg, 1536);\r
5941     return FALSE;  /* Allow for further processing */\r
5942 \r
5943   case WM_COMMAND:\r
5944     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5945       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5946     }\r
5947     return FALSE;  /* Allow for further processing */\r
5948   }\r
5949   return FALSE;\r
5950 }\r
5951 \r
5952 UINT APIENTRY\r
5953 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5954 {\r
5955   static UINT *number;\r
5956   OPENFILENAME *ofname;\r
5957   OFNOTIFY *ofnot;\r
5958   switch (uiMsg) {\r
5959   case WM_INITDIALOG:\r
5960     Translate(hdlg, DLG_IndexNumber);\r
5961     ofname = (OPENFILENAME *)lParam;\r
5962     number = (UINT *)(ofname->lCustData);\r
5963     break;\r
5964   case WM_NOTIFY:\r
5965     ofnot = (OFNOTIFY *)lParam;\r
5966     if (ofnot->hdr.code == CDN_FILEOK) {\r
5967       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5968     }\r
5969     break;\r
5970   }\r
5971   return 0;\r
5972 }\r
5973 \r
5974 \r
5975 FILE *\r
5976 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5977                char *nameFilt, char *dlgTitle, UINT *number,\r
5978                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5979 {\r
5980   OPENFILENAME openFileName;\r
5981   char buf1[MSG_SIZ];\r
5982   FILE *f;\r
5983 \r
5984   if (fileName == NULL) fileName = buf1;\r
5985   if (defName == NULL) {\r
5986     safeStrCpy(fileName, "*.", 3 );\r
5987     strcat(fileName, defExt);\r
5988   } else {\r
5989     safeStrCpy(fileName, defName, MSG_SIZ );\r
5990   }\r
5991     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5992   if (number) *number = 0;\r
5993 \r
5994   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5995   openFileName.hwndOwner         = hwnd;\r
5996   openFileName.hInstance         = (HANDLE) hInst;\r
5997   openFileName.lpstrFilter       = nameFilt;\r
5998   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5999   openFileName.nMaxCustFilter    = 0L;\r
6000   openFileName.nFilterIndex      = 1L;\r
6001   openFileName.lpstrFile         = fileName;\r
6002   openFileName.nMaxFile          = MSG_SIZ;\r
6003   openFileName.lpstrFileTitle    = fileTitle;\r
6004   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6005   openFileName.lpstrInitialDir   = NULL;\r
6006   openFileName.lpstrTitle        = dlgTitle;\r
6007   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6008     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6009     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6010     | (oldDialog ? 0 : OFN_EXPLORER);\r
6011   openFileName.nFileOffset       = 0;\r
6012   openFileName.nFileExtension    = 0;\r
6013   openFileName.lpstrDefExt       = defExt;\r
6014   openFileName.lCustData         = (LONG) number;\r
6015   openFileName.lpfnHook          = oldDialog ?\r
6016     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6017   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6018 \r
6019   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6020                         GetOpenFileName(&openFileName)) {\r
6021     /* open the file */\r
6022     f = fopen(openFileName.lpstrFile, write);\r
6023     if (f == NULL) {\r
6024       MessageBox(hwnd, _("File open failed"), NULL,\r
6025                  MB_OK|MB_ICONEXCLAMATION);\r
6026       return NULL;\r
6027     }\r
6028   } else {\r
6029     int err = CommDlgExtendedError();\r
6030     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6031     return FALSE;\r
6032   }\r
6033   return f;\r
6034 }\r
6035 \r
6036 \r
6037 \r
6038 VOID APIENTRY\r
6039 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6040 {\r
6041   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6042 \r
6043   /*\r
6044    * Get the first pop-up menu in the menu template. This is the\r
6045    * menu that TrackPopupMenu displays.\r
6046    */\r
6047   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6048   TranslateOneMenu(10, hmenuTrackPopup);\r
6049 \r
6050   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6051 \r
6052   /*\r
6053    * TrackPopup uses screen coordinates, so convert the\r
6054    * coordinates of the mouse click to screen coordinates.\r
6055    */\r
6056   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6057 \r
6058   /* Draw and track the floating pop-up menu. */\r
6059   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6060                  pt.x, pt.y, 0, hwnd, NULL);\r
6061 \r
6062   /* Destroy the menu.*/\r
6063   DestroyMenu(hmenu);\r
6064 }\r
6065    \r
6066 typedef struct {\r
6067   HWND hDlg, hText;\r
6068   int sizeX, sizeY, newSizeX, newSizeY;\r
6069   HDWP hdwp;\r
6070 } ResizeEditPlusButtonsClosure;\r
6071 \r
6072 BOOL CALLBACK\r
6073 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6074 {\r
6075   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6076   RECT rect;\r
6077   POINT pt;\r
6078 \r
6079   if (hChild == cl->hText) return TRUE;\r
6080   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6081   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6082   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6083   ScreenToClient(cl->hDlg, &pt);\r
6084   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6085     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6086   return TRUE;\r
6087 }\r
6088 \r
6089 /* Resize a dialog that has a (rich) edit field filling most of\r
6090    the top, with a row of buttons below */\r
6091 VOID\r
6092 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6093 {\r
6094   RECT rectText;\r
6095   int newTextHeight, newTextWidth;\r
6096   ResizeEditPlusButtonsClosure cl;\r
6097   \r
6098   /*if (IsIconic(hDlg)) return;*/\r
6099   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6100   \r
6101   cl.hdwp = BeginDeferWindowPos(8);\r
6102 \r
6103   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6104   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6105   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6106   if (newTextHeight < 0) {\r
6107     newSizeY += -newTextHeight;\r
6108     newTextHeight = 0;\r
6109   }\r
6110   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6111     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6112 \r
6113   cl.hDlg = hDlg;\r
6114   cl.hText = hText;\r
6115   cl.sizeX = sizeX;\r
6116   cl.sizeY = sizeY;\r
6117   cl.newSizeX = newSizeX;\r
6118   cl.newSizeY = newSizeY;\r
6119   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6120 \r
6121   EndDeferWindowPos(cl.hdwp);\r
6122 }\r
6123 \r
6124 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6125 {\r
6126     RECT    rChild, rParent;\r
6127     int     wChild, hChild, wParent, hParent;\r
6128     int     wScreen, hScreen, xNew, yNew;\r
6129     HDC     hdc;\r
6130 \r
6131     /* Get the Height and Width of the child window */\r
6132     GetWindowRect (hwndChild, &rChild);\r
6133     wChild = rChild.right - rChild.left;\r
6134     hChild = rChild.bottom - rChild.top;\r
6135 \r
6136     /* Get the Height and Width of the parent window */\r
6137     GetWindowRect (hwndParent, &rParent);\r
6138     wParent = rParent.right - rParent.left;\r
6139     hParent = rParent.bottom - rParent.top;\r
6140 \r
6141     /* Get the display limits */\r
6142     hdc = GetDC (hwndChild);\r
6143     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6144     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6145     ReleaseDC(hwndChild, hdc);\r
6146 \r
6147     /* Calculate new X position, then adjust for screen */\r
6148     xNew = rParent.left + ((wParent - wChild) /2);\r
6149     if (xNew < 0) {\r
6150         xNew = 0;\r
6151     } else if ((xNew+wChild) > wScreen) {\r
6152         xNew = wScreen - wChild;\r
6153     }\r
6154 \r
6155     /* Calculate new Y position, then adjust for screen */\r
6156     if( mode == 0 ) {\r
6157         yNew = rParent.top  + ((hParent - hChild) /2);\r
6158     }\r
6159     else {\r
6160         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6161     }\r
6162 \r
6163     if (yNew < 0) {\r
6164         yNew = 0;\r
6165     } else if ((yNew+hChild) > hScreen) {\r
6166         yNew = hScreen - hChild;\r
6167     }\r
6168 \r
6169     /* Set it, and return */\r
6170     return SetWindowPos (hwndChild, NULL,\r
6171                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6172 }\r
6173 \r
6174 /* Center one window over another */\r
6175 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6176 {\r
6177     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6178 }\r
6179 \r
6180 /*---------------------------------------------------------------------------*\\r
6181  *\r
6182  * Startup Dialog functions\r
6183  *\r
6184 \*---------------------------------------------------------------------------*/\r
6185 void\r
6186 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6187 {\r
6188   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6189 \r
6190   while (*cd != NULL) {\r
6191     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6192     cd++;\r
6193   }\r
6194 }\r
6195 \r
6196 void\r
6197 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6198 {\r
6199   char buf1[MAX_ARG_LEN];\r
6200   int len;\r
6201 \r
6202   if (str[0] == '@') {\r
6203     FILE* f = fopen(str + 1, "r");\r
6204     if (f == NULL) {\r
6205       DisplayFatalError(str + 1, errno, 2);\r
6206       return;\r
6207     }\r
6208     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6209     fclose(f);\r
6210     buf1[len] = NULLCHAR;\r
6211     str = buf1;\r
6212   }\r
6213 \r
6214   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6215 \r
6216   for (;;) {\r
6217     char buf[MSG_SIZ];\r
6218     char *end = strchr(str, '\n');\r
6219     if (end == NULL) return;\r
6220     memcpy(buf, str, end - str);\r
6221     buf[end - str] = NULLCHAR;\r
6222     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6223     str = end + 1;\r
6224   }\r
6225 }\r
6226 \r
6227 void\r
6228 SetStartupDialogEnables(HWND hDlg)\r
6229 {\r
6230   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6231     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6232     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6233   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6234     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6235   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6236     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6237   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6238     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6239   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6240     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6241     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6242     IsDlgButtonChecked(hDlg, OPT_View));\r
6243 }\r
6244 \r
6245 char *\r
6246 QuoteForFilename(char *filename)\r
6247 {\r
6248   int dquote, space;\r
6249   dquote = strchr(filename, '"') != NULL;\r
6250   space = strchr(filename, ' ') != NULL;\r
6251   if (dquote || space) {\r
6252     if (dquote) {\r
6253       return "'";\r
6254     } else {\r
6255       return "\"";\r
6256     }\r
6257   } else {\r
6258     return "";\r
6259   }\r
6260 }\r
6261 \r
6262 VOID\r
6263 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6264 {\r
6265   char buf[MSG_SIZ];\r
6266   char *q;\r
6267 \r
6268   InitComboStringsFromOption(hwndCombo, nthnames);\r
6269   q = QuoteForFilename(nthcp);\r
6270     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6271   if (*nthdir != NULLCHAR) {\r
6272     q = QuoteForFilename(nthdir);\r
6273       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6274   }\r
6275   if (*nthcp == NULLCHAR) {\r
6276     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6277   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6278     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6279     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6280   }\r
6281 }\r
6282 \r
6283 LRESULT CALLBACK\r
6284 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6285 {\r
6286   char buf[MSG_SIZ];\r
6287   HANDLE hwndCombo;\r
6288   char *p;\r
6289 \r
6290   switch (message) {\r
6291   case WM_INITDIALOG:\r
6292     /* Center the dialog */\r
6293     CenterWindow (hDlg, GetDesktopWindow());\r
6294     Translate(hDlg, DLG_Startup);\r
6295     /* Initialize the dialog items */\r
6296     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6297                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6298                   firstChessProgramNames);\r
6299     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6300                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6301                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6302     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6303     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6304       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6305     if (*appData.icsHelper != NULLCHAR) {\r
6306       char *q = QuoteForFilename(appData.icsHelper);\r
6307       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6308     }\r
6309     if (*appData.icsHost == NULLCHAR) {\r
6310       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6311       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6312     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6313       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6314       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6315     }\r
6316 \r
6317     if (appData.icsActive) {\r
6318       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6319     }\r
6320     else if (appData.noChessProgram) {\r
6321       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6322     }\r
6323     else {\r
6324       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6325     }\r
6326 \r
6327     SetStartupDialogEnables(hDlg);\r
6328     return TRUE;\r
6329 \r
6330   case WM_COMMAND:\r
6331     switch (LOWORD(wParam)) {\r
6332     case IDOK:\r
6333       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6334         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6335         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6336         p = buf;\r
6337         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6338         ParseArgs(StringGet, &p);\r
6339         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6340         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6341         p = buf;\r
6342         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6343         ParseArgs(StringGet, &p);\r
6344         SwapEngines(singleList); // ... and then make it 'second'\r
6345 \r
6346         appData.noChessProgram = FALSE;\r
6347         appData.icsActive = FALSE;\r
6348       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6349         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6350         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6351         p = buf;\r
6352         ParseArgs(StringGet, &p);\r
6353         if (appData.zippyPlay) {\r
6354           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6355           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6356           p = buf;\r
6357           ParseArgs(StringGet, &p);\r
6358         }\r
6359       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6360         appData.noChessProgram = TRUE;\r
6361         appData.icsActive = FALSE;\r
6362       } else {\r
6363         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6364                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6365         return TRUE;\r
6366       }\r
6367       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6368         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6369         p = buf;\r
6370         ParseArgs(StringGet, &p);\r
6371       }\r
6372       EndDialog(hDlg, TRUE);\r
6373       return TRUE;\r
6374 \r
6375     case IDCANCEL:\r
6376       ExitEvent(0);\r
6377       return TRUE;\r
6378 \r
6379     case IDM_HELPCONTENTS:\r
6380       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6381         MessageBox (GetFocus(),\r
6382                     _("Unable to activate help"),\r
6383                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6384       }\r
6385       break;\r
6386 \r
6387     default:\r
6388       SetStartupDialogEnables(hDlg);\r
6389       break;\r
6390     }\r
6391     break;\r
6392   }\r
6393   return FALSE;\r
6394 }\r
6395 \r
6396 /*---------------------------------------------------------------------------*\\r
6397  *\r
6398  * About box dialog functions\r
6399  *\r
6400 \*---------------------------------------------------------------------------*/\r
6401 \r
6402 /* Process messages for "About" dialog box */\r
6403 LRESULT CALLBACK\r
6404 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6405 {\r
6406   switch (message) {\r
6407   case WM_INITDIALOG: /* message: initialize dialog box */\r
6408     /* Center the dialog over the application window */\r
6409     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6410     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6411     Translate(hDlg, ABOUTBOX);\r
6412     JAWS_COPYRIGHT\r
6413     return (TRUE);\r
6414 \r
6415   case WM_COMMAND: /* message: received a command */\r
6416     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6417         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6418       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6419       return (TRUE);\r
6420     }\r
6421     break;\r
6422   }\r
6423   return (FALSE);\r
6424 }\r
6425 \r
6426 /*---------------------------------------------------------------------------*\\r
6427  *\r
6428  * Comment Dialog functions\r
6429  *\r
6430 \*---------------------------------------------------------------------------*/\r
6431 \r
6432 LRESULT CALLBACK\r
6433 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6434 {\r
6435   static HANDLE hwndText = NULL;\r
6436   int len, newSizeX, newSizeY;\r
6437   static int sizeX, sizeY;\r
6438   char *str;\r
6439   RECT rect;\r
6440   MINMAXINFO *mmi;\r
6441 \r
6442   switch (message) {\r
6443   case WM_INITDIALOG: /* message: initialize dialog box */\r
6444     /* Initialize the dialog items */\r
6445     Translate(hDlg, DLG_EditComment);\r
6446     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6447     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6448     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6449     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6450     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6451     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6452     SetWindowText(hDlg, commentTitle);\r
6453     if (editComment) {\r
6454       SetFocus(hwndText);\r
6455     } else {\r
6456       SetFocus(GetDlgItem(hDlg, IDOK));\r
6457     }\r
6458     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6459                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6460                 MAKELPARAM(FALSE, 0));\r
6461     /* Size and position the dialog */\r
6462     if (!commentDialog) {\r
6463       commentDialog = hDlg;\r
6464       GetClientRect(hDlg, &rect);\r
6465       sizeX = rect.right;\r
6466       sizeY = rect.bottom;\r
6467       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6468           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6469         WINDOWPLACEMENT wp;\r
6470         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6471         wp.length = sizeof(WINDOWPLACEMENT);\r
6472         wp.flags = 0;\r
6473         wp.showCmd = SW_SHOW;\r
6474         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6475         wp.rcNormalPosition.left = wpComment.x;\r
6476         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6477         wp.rcNormalPosition.top = wpComment.y;\r
6478         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6479         SetWindowPlacement(hDlg, &wp);\r
6480 \r
6481         GetClientRect(hDlg, &rect);\r
6482         newSizeX = rect.right;\r
6483         newSizeY = rect.bottom;\r
6484         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6485                               newSizeX, newSizeY);\r
6486         sizeX = newSizeX;\r
6487         sizeY = newSizeY;\r
6488       }\r
6489     }\r
6490     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6491     return FALSE;\r
6492 \r
6493   case WM_COMMAND: /* message: received a command */\r
6494     switch (LOWORD(wParam)) {\r
6495     case IDOK:\r
6496       if (editComment) {\r
6497         char *p, *q;\r
6498         /* Read changed options from the dialog box */\r
6499         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6500         len = GetWindowTextLength(hwndText);\r
6501         str = (char *) malloc(len + 1);\r
6502         GetWindowText(hwndText, str, len + 1);\r
6503         p = q = str;\r
6504         while (*q) {\r
6505           if (*q == '\r')\r
6506             q++;\r
6507           else\r
6508             *p++ = *q++;\r
6509         }\r
6510         *p = NULLCHAR;\r
6511         ReplaceComment(commentIndex, str);\r
6512         free(str);\r
6513       }\r
6514       CommentPopDown();\r
6515       return TRUE;\r
6516 \r
6517     case IDCANCEL:\r
6518     case OPT_CancelComment:\r
6519       CommentPopDown();\r
6520       return TRUE;\r
6521 \r
6522     case OPT_ClearComment:\r
6523       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6524       break;\r
6525 \r
6526     case OPT_EditComment:\r
6527       EditCommentEvent();\r
6528       return TRUE;\r
6529 \r
6530     default:\r
6531       break;\r
6532     }\r
6533     break;\r
6534 \r
6535   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6536         if( wParam == OPT_CommentText ) {\r
6537             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6538 \r
6539             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6540                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6541                 POINTL pt;\r
6542                 LRESULT index;\r
6543 \r
6544                 pt.x = LOWORD( lpMF->lParam );\r
6545                 pt.y = HIWORD( lpMF->lParam );\r
6546 \r
6547                 if(lpMF->msg == WM_CHAR) {\r
6548                         CHARRANGE sel;\r
6549                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6550                         index = sel.cpMin;\r
6551                 } else\r
6552                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6553 \r
6554                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6555                 len = GetWindowTextLength(hwndText);\r
6556                 str = (char *) malloc(len + 1);\r
6557                 GetWindowText(hwndText, str, len + 1);\r
6558                 ReplaceComment(commentIndex, str);\r
6559                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6560                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6561                 free(str);\r
6562 \r
6563                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6564                 lpMF->msg = WM_USER;\r
6565 \r
6566                 return TRUE;\r
6567             }\r
6568         }\r
6569         break;\r
6570 \r
6571   case WM_SIZE:\r
6572     newSizeX = LOWORD(lParam);\r
6573     newSizeY = HIWORD(lParam);\r
6574     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6575     sizeX = newSizeX;\r
6576     sizeY = newSizeY;\r
6577     break;\r
6578 \r
6579   case WM_GETMINMAXINFO:\r
6580     /* Prevent resizing window too small */\r
6581     mmi = (MINMAXINFO *) lParam;\r
6582     mmi->ptMinTrackSize.x = 100;\r
6583     mmi->ptMinTrackSize.y = 100;\r
6584     break;\r
6585   }\r
6586   return FALSE;\r
6587 }\r
6588 \r
6589 VOID\r
6590 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6591 {\r
6592   FARPROC lpProc;\r
6593   char *p, *q;\r
6594 \r
6595   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6596 \r
6597   if (str == NULL) str = "";\r
6598   p = (char *) malloc(2 * strlen(str) + 2);\r
6599   q = p;\r
6600   while (*str) {\r
6601     if (*str == '\n') *q++ = '\r';\r
6602     *q++ = *str++;\r
6603   }\r
6604   *q = NULLCHAR;\r
6605   if (commentText != NULL) free(commentText);\r
6606 \r
6607   commentIndex = index;\r
6608   commentTitle = title;\r
6609   commentText = p;\r
6610   editComment = edit;\r
6611 \r
6612   if (commentDialog) {\r
6613     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6614     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6615   } else {\r
6616     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6617     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6618                  hwndMain, (DLGPROC)lpProc);\r
6619     FreeProcInstance(lpProc);\r
6620   }\r
6621   commentUp = TRUE;\r
6622 }\r
6623 \r
6624 \r
6625 /*---------------------------------------------------------------------------*\\r
6626  *\r
6627  * Type-in move dialog functions\r
6628  * \r
6629 \*---------------------------------------------------------------------------*/\r
6630 \r
6631 LRESULT CALLBACK\r
6632 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6633 {\r
6634   char move[MSG_SIZ];\r
6635   HWND hInput;\r
6636 \r
6637   switch (message) {\r
6638   case WM_INITDIALOG:\r
6639     move[0] = (char) lParam;\r
6640     move[1] = NULLCHAR;\r
6641     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6642     Translate(hDlg, DLG_TypeInMove);\r
6643     hInput = GetDlgItem(hDlg, OPT_Move);\r
6644     SetWindowText(hInput, move);\r
6645     SetFocus(hInput);\r
6646     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6647     return FALSE;\r
6648 \r
6649   case WM_COMMAND:\r
6650     switch (LOWORD(wParam)) {\r
6651     case IDOK:\r
6652 \r
6653       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6654       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6655       TypeInDoneEvent(move);\r
6656       EndDialog(hDlg, TRUE);\r
6657       return TRUE;\r
6658     case IDCANCEL:\r
6659       EndDialog(hDlg, FALSE);\r
6660       return TRUE;\r
6661     default:\r
6662       break;\r
6663     }\r
6664     break;\r
6665   }\r
6666   return FALSE;\r
6667 }\r
6668 \r
6669 VOID\r
6670 PopUpMoveDialog(char firstchar)\r
6671 {\r
6672     FARPROC lpProc;\r
6673 \r
6674       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6675       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6676         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6677       FreeProcInstance(lpProc);\r
6678 }\r
6679 \r
6680 /*---------------------------------------------------------------------------*\\r
6681  *\r
6682  * Type-in name dialog functions\r
6683  * \r
6684 \*---------------------------------------------------------------------------*/\r
6685 \r
6686 LRESULT CALLBACK\r
6687 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6688 {\r
6689   char move[MSG_SIZ];\r
6690   HWND hInput;\r
6691 \r
6692   switch (message) {\r
6693   case WM_INITDIALOG:\r
6694     move[0] = (char) lParam;\r
6695     move[1] = NULLCHAR;\r
6696     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6697     Translate(hDlg, DLG_TypeInName);\r
6698     hInput = GetDlgItem(hDlg, OPT_Name);\r
6699     SetWindowText(hInput, move);\r
6700     SetFocus(hInput);\r
6701     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6702     return FALSE;\r
6703 \r
6704   case WM_COMMAND:\r
6705     switch (LOWORD(wParam)) {\r
6706     case IDOK:\r
6707       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6708       appData.userName = strdup(move);\r
6709       SetUserLogo();\r
6710       SetGameInfo();\r
6711       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6712         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6713         DisplayTitle(move);\r
6714       }\r
6715 \r
6716 \r
6717       EndDialog(hDlg, TRUE);\r
6718       return TRUE;\r
6719     case IDCANCEL:\r
6720       EndDialog(hDlg, FALSE);\r
6721       return TRUE;\r
6722     default:\r
6723       break;\r
6724     }\r
6725     break;\r
6726   }\r
6727   return FALSE;\r
6728 }\r
6729 \r
6730 VOID\r
6731 PopUpNameDialog(char firstchar)\r
6732 {\r
6733     FARPROC lpProc;\r
6734     \r
6735       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6736       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6737         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6738       FreeProcInstance(lpProc);\r
6739 }\r
6740 \r
6741 /*---------------------------------------------------------------------------*\\r
6742  *\r
6743  *  Error dialogs\r
6744  * \r
6745 \*---------------------------------------------------------------------------*/\r
6746 \r
6747 /* Nonmodal error box */\r
6748 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6749                              WPARAM wParam, LPARAM lParam);\r
6750 \r
6751 VOID\r
6752 ErrorPopUp(char *title, char *content)\r
6753 {\r
6754   FARPROC lpProc;\r
6755   char *p, *q;\r
6756   BOOLEAN modal = hwndMain == NULL;\r
6757 \r
6758   p = content;\r
6759   q = errorMessage;\r
6760   while (*p) {\r
6761     if (*p == '\n') {\r
6762       if (modal) {\r
6763         *q++ = ' ';\r
6764         p++;\r
6765       } else {\r
6766         *q++ = '\r';\r
6767         *q++ = *p++;\r
6768       }\r
6769     } else {\r
6770       *q++ = *p++;\r
6771     }\r
6772   }\r
6773   *q = NULLCHAR;\r
6774   strncpy(errorTitle, title, sizeof(errorTitle));\r
6775   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6776   \r
6777   if (modal) {\r
6778     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6779   } else {\r
6780     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6781     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6782                  hwndMain, (DLGPROC)lpProc);\r
6783     FreeProcInstance(lpProc);\r
6784   }\r
6785 }\r
6786 \r
6787 VOID\r
6788 ErrorPopDown()\r
6789 {\r
6790   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6791   if (errorDialog == NULL) return;\r
6792   DestroyWindow(errorDialog);\r
6793   errorDialog = NULL;\r
6794   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6795 }\r
6796 \r
6797 LRESULT CALLBACK\r
6798 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6799 {\r
6800   RECT rChild;\r
6801 \r
6802   switch (message) {\r
6803   case WM_INITDIALOG:\r
6804     GetWindowRect(hDlg, &rChild);\r
6805 \r
6806     /*\r
6807     SetWindowPos(hDlg, NULL, rChild.left,\r
6808       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6809       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6810     */\r
6811 \r
6812     /* \r
6813         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6814         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6815         and it doesn't work when you resize the dialog.\r
6816         For now, just give it a default position.\r
6817     */\r
6818     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6819     Translate(hDlg, DLG_Error);\r
6820 \r
6821     errorDialog = hDlg;\r
6822     SetWindowText(hDlg, errorTitle);\r
6823     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6824     return FALSE;\r
6825 \r
6826   case WM_COMMAND:\r
6827     switch (LOWORD(wParam)) {\r
6828     case IDOK:\r
6829     case IDCANCEL:\r
6830       if (errorDialog == hDlg) errorDialog = NULL;\r
6831       DestroyWindow(hDlg);\r
6832       return TRUE;\r
6833 \r
6834     default:\r
6835       break;\r
6836     }\r
6837     break;\r
6838   }\r
6839   return FALSE;\r
6840 }\r
6841 \r
6842 #ifdef GOTHIC\r
6843 HWND gothicDialog = NULL;\r
6844 \r
6845 LRESULT CALLBACK\r
6846 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6847 {\r
6848   RECT rChild;\r
6849   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6850 \r
6851   switch (message) {\r
6852   case WM_INITDIALOG:\r
6853     GetWindowRect(hDlg, &rChild);\r
6854 \r
6855     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6856                                                              SWP_NOZORDER);\r
6857 \r
6858     /* \r
6859         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6860         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6861         and it doesn't work when you resize the dialog.\r
6862         For now, just give it a default position.\r
6863     */\r
6864     gothicDialog = hDlg;\r
6865     SetWindowText(hDlg, errorTitle);\r
6866     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6867     return FALSE;\r
6868 \r
6869   case WM_COMMAND:\r
6870     switch (LOWORD(wParam)) {\r
6871     case IDOK:\r
6872     case IDCANCEL:\r
6873       if (errorDialog == hDlg) errorDialog = NULL;\r
6874       DestroyWindow(hDlg);\r
6875       return TRUE;\r
6876 \r
6877     default:\r
6878       break;\r
6879     }\r
6880     break;\r
6881   }\r
6882   return FALSE;\r
6883 }\r
6884 \r
6885 VOID\r
6886 GothicPopUp(char *title, VariantClass variant)\r
6887 {\r
6888   FARPROC lpProc;\r
6889   static char *lastTitle;\r
6890 \r
6891   strncpy(errorTitle, title, sizeof(errorTitle));\r
6892   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6893 \r
6894   if(lastTitle != title && gothicDialog != NULL) {\r
6895     DestroyWindow(gothicDialog);\r
6896     gothicDialog = NULL;\r
6897   }\r
6898   if(variant != VariantNormal && gothicDialog == NULL) {\r
6899     title = lastTitle;\r
6900     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6901     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6902                  hwndMain, (DLGPROC)lpProc);\r
6903     FreeProcInstance(lpProc);\r
6904   }\r
6905 }\r
6906 #endif\r
6907 \r
6908 /*---------------------------------------------------------------------------*\\r
6909  *\r
6910  *  Ics Interaction console functions\r
6911  *\r
6912 \*---------------------------------------------------------------------------*/\r
6913 \r
6914 #define HISTORY_SIZE 64\r
6915 static char *history[HISTORY_SIZE];\r
6916 int histIn = 0, histP = 0;\r
6917 \r
6918 \r
6919 VOID\r
6920 SaveInHistory(char *cmd)\r
6921 {\r
6922   if (history[histIn] != NULL) {\r
6923     free(history[histIn]);\r
6924     history[histIn] = NULL;\r
6925   }\r
6926   if (*cmd == NULLCHAR) return;\r
6927   history[histIn] = StrSave(cmd);\r
6928   histIn = (histIn + 1) % HISTORY_SIZE;\r
6929   if (history[histIn] != NULL) {\r
6930     free(history[histIn]);\r
6931 \r
6932     history[histIn] = NULL;\r
6933   }\r
6934   histP = histIn;\r
6935 }\r
6936 \r
6937 char *\r
6938 PrevInHistory(char *cmd)\r
6939 {\r
6940   int newhp;\r
6941   if (histP == histIn) {\r
6942     if (history[histIn] != NULL) free(history[histIn]);\r
6943     history[histIn] = StrSave(cmd);\r
6944   }\r
6945   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6946   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6947   histP = newhp;\r
6948   return history[histP];\r
6949 }\r
6950 \r
6951 char *\r
6952 NextInHistory()\r
6953 {\r
6954   if (histP == histIn) return NULL;\r
6955   histP = (histP + 1) % HISTORY_SIZE;\r
6956   return history[histP];   \r
6957 }\r
6958 \r
6959 HMENU\r
6960 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6961 {\r
6962   HMENU hmenu, h;\r
6963   int i = 0;\r
6964   hmenu = LoadMenu(hInst, "TextMenu");\r
6965   h = GetSubMenu(hmenu, 0);\r
6966   while (e->item) {\r
6967     if (strcmp(e->item, "-") == 0) {\r
6968       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6969     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6970       int flags = MF_STRING, j = 0;\r
6971       if (e->item[0] == '|') {\r
6972         flags |= MF_MENUBARBREAK;\r
6973         j++;\r
6974       }\r
6975       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6976       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6977     }\r
6978     e++;\r
6979     i++;\r
6980   } \r
6981   return hmenu;\r
6982 }\r
6983 \r
6984 WNDPROC consoleTextWindowProc;\r
6985 \r
6986 void\r
6987 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6988 {\r
6989   char buf[MSG_SIZ], name[MSG_SIZ];\r
6990   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6991   CHARRANGE sel;\r
6992 \r
6993   if (!getname) {\r
6994     SetWindowText(hInput, command);\r
6995     if (immediate) {\r
6996       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6997     } else {\r
6998       sel.cpMin = 999999;\r
6999       sel.cpMax = 999999;\r
7000       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7001       SetFocus(hInput);\r
7002     }\r
7003     return;\r
7004   }    \r
7005   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7006   if (sel.cpMin == sel.cpMax) {\r
7007     /* Expand to surrounding word */\r
7008     TEXTRANGE tr;\r
7009     do {\r
7010       tr.chrg.cpMax = sel.cpMin;\r
7011       tr.chrg.cpMin = --sel.cpMin;\r
7012       if (sel.cpMin < 0) break;\r
7013       tr.lpstrText = name;\r
7014       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7015     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7016     sel.cpMin++;\r
7017 \r
7018     do {\r
7019       tr.chrg.cpMin = sel.cpMax;\r
7020       tr.chrg.cpMax = ++sel.cpMax;\r
7021       tr.lpstrText = name;\r
7022       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7023     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7024     sel.cpMax--;\r
7025 \r
7026     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7027       MessageBeep(MB_ICONEXCLAMATION);\r
7028       return;\r
7029     }\r
7030     tr.chrg = sel;\r
7031     tr.lpstrText = name;\r
7032     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7033   } else {\r
7034     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7035       MessageBeep(MB_ICONEXCLAMATION);\r
7036       return;\r
7037     }\r
7038     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7039   }\r
7040   if (immediate) {\r
7041     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7042     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7043     SetWindowText(hInput, buf);\r
7044     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7045   } else {\r
7046     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7047       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7048     SetWindowText(hInput, buf);\r
7049     sel.cpMin = 999999;\r
7050     sel.cpMax = 999999;\r
7051     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7052     SetFocus(hInput);\r
7053   }\r
7054 }\r
7055 \r
7056 LRESULT CALLBACK \r
7057 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7058 {\r
7059   HWND hInput;\r
7060   CHARRANGE sel;\r
7061 \r
7062   switch (message) {\r
7063   case WM_KEYDOWN:\r
7064     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7065     if(wParam=='R') return 0;\r
7066     switch (wParam) {\r
7067     case VK_PRIOR:\r
7068       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7069       return 0;\r
7070     case VK_NEXT:\r
7071       sel.cpMin = 999999;\r
7072       sel.cpMax = 999999;\r
7073       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7074       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7075       return 0;\r
7076     }\r
7077     break;\r
7078   case WM_CHAR:\r
7079    if(wParam != '\022') {\r
7080     if (wParam == '\t') {\r
7081       if (GetKeyState(VK_SHIFT) < 0) {\r
7082         /* shifted */\r
7083         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7084         if (buttonDesc[0].hwnd) {\r
7085           SetFocus(buttonDesc[0].hwnd);\r
7086         } else {\r
7087           SetFocus(hwndMain);\r
7088         }\r
7089       } else {\r
7090         /* unshifted */\r
7091         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7092       }\r
7093     } else {\r
7094       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7095       JAWS_DELETE( SetFocus(hInput); )\r
7096       SendMessage(hInput, message, wParam, lParam);\r
7097     }\r
7098     return 0;\r
7099    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7100    lParam = -1;\r
7101   case WM_RBUTTONDOWN:\r
7102     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7103       /* Move selection here if it was empty */\r
7104       POINT pt;\r
7105       pt.x = LOWORD(lParam);\r
7106       pt.y = HIWORD(lParam);\r
7107       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7108       if (sel.cpMin == sel.cpMax) {\r
7109         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7110         sel.cpMax = sel.cpMin;\r
7111         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7112       }\r
7113       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7114 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7115       POINT pt;\r
7116       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7117       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7118       if (sel.cpMin == sel.cpMax) {\r
7119         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7120         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7121       }\r
7122       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7123         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7124       }\r
7125       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7126       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7127       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7128       MenuPopup(hwnd, pt, hmenu, -1);\r
7129 }\r
7130     }\r
7131     return 0;\r
7132   case WM_RBUTTONUP:\r
7133     if (GetKeyState(VK_SHIFT) & ~1) {\r
7134       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7135         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7136     }\r
7137     return 0;\r
7138   case WM_PASTE:\r
7139     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7140     SetFocus(hInput);\r
7141     return SendMessage(hInput, message, wParam, lParam);\r
7142   case WM_MBUTTONDOWN:\r
7143     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7144   case WM_COMMAND:\r
7145     switch (LOWORD(wParam)) {\r
7146     case IDM_QuickPaste:\r
7147       {\r
7148         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7149         if (sel.cpMin == sel.cpMax) {\r
7150           MessageBeep(MB_ICONEXCLAMATION);\r
7151           return 0;\r
7152         }\r
7153         SendMessage(hwnd, WM_COPY, 0, 0);\r
7154         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7155         SendMessage(hInput, WM_PASTE, 0, 0);\r
7156         SetFocus(hInput);\r
7157         return 0;\r
7158       }\r
7159     case IDM_Cut:\r
7160       SendMessage(hwnd, WM_CUT, 0, 0);\r
7161       return 0;\r
7162     case IDM_Paste:\r
7163       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7164       return 0;\r
7165     case IDM_Copy:\r
7166       SendMessage(hwnd, WM_COPY, 0, 0);\r
7167       return 0;\r
7168     default:\r
7169       {\r
7170         int i = LOWORD(wParam) - IDM_CommandX;\r
7171         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7172             icsTextMenuEntry[i].command != NULL) {\r
7173           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7174                    icsTextMenuEntry[i].getname,\r
7175                    icsTextMenuEntry[i].immediate);\r
7176           return 0;\r
7177         }\r
7178       }\r
7179       break;\r
7180     }\r
7181     break;\r
7182   }\r
7183   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7184 }\r
7185 \r
7186 WNDPROC consoleInputWindowProc;\r
7187 \r
7188 LRESULT CALLBACK\r
7189 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7190 {\r
7191   char buf[MSG_SIZ];\r
7192   char *p;\r
7193   static BOOL sendNextChar = FALSE;\r
7194   static BOOL quoteNextChar = FALSE;\r
7195   InputSource *is = consoleInputSource;\r
7196   CHARFORMAT cf;\r
7197   CHARRANGE sel;\r
7198 \r
7199   switch (message) {\r
7200   case WM_CHAR:\r
7201     if (!appData.localLineEditing || sendNextChar) {\r
7202       is->buf[0] = (CHAR) wParam;\r
7203       is->count = 1;\r
7204       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7205       sendNextChar = FALSE;\r
7206       return 0;\r
7207     }\r
7208     if (quoteNextChar) {\r
7209       buf[0] = (char) wParam;\r
7210       buf[1] = NULLCHAR;\r
7211       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7212       quoteNextChar = FALSE;\r
7213       return 0;\r
7214     }\r
7215     switch (wParam) {\r
7216     case '\r':   /* Enter key */\r
7217       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7218       if (consoleEcho) SaveInHistory(is->buf);\r
7219       is->buf[is->count++] = '\n';\r
7220       is->buf[is->count] = NULLCHAR;\r
7221       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7222       if (consoleEcho) {\r
7223         ConsoleOutput(is->buf, is->count, TRUE);\r
7224       } else if (appData.localLineEditing) {\r
7225         ConsoleOutput("\n", 1, TRUE);\r
7226       }\r
7227       /* fall thru */\r
7228     case '\033': /* Escape key */\r
7229       SetWindowText(hwnd, "");\r
7230       cf.cbSize = sizeof(CHARFORMAT);\r
7231       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7232       if (consoleEcho) {\r
7233         cf.crTextColor = textAttribs[ColorNormal].color;\r
7234       } else {\r
7235         cf.crTextColor = COLOR_ECHOOFF;\r
7236       }\r
7237       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7238       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7239       return 0;\r
7240     case '\t':   /* Tab key */\r
7241       if (GetKeyState(VK_SHIFT) < 0) {\r
7242         /* shifted */\r
7243         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7244       } else {\r
7245         /* unshifted */\r
7246         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7247         if (buttonDesc[0].hwnd) {\r
7248           SetFocus(buttonDesc[0].hwnd);\r
7249         } else {\r
7250           SetFocus(hwndMain);\r
7251         }\r
7252       }\r
7253       return 0;\r
7254     case '\023': /* Ctrl+S */\r
7255       sendNextChar = TRUE;\r
7256       return 0;\r
7257     case '\021': /* Ctrl+Q */\r
7258       quoteNextChar = TRUE;\r
7259       return 0;\r
7260     JAWS_REPLAY\r
7261     default:\r
7262       break;\r
7263     }\r
7264     break;\r
7265   case WM_KEYDOWN:\r
7266     switch (wParam) {\r
7267     case VK_UP:\r
7268       GetWindowText(hwnd, buf, MSG_SIZ);\r
7269       p = PrevInHistory(buf);\r
7270       if (p != NULL) {\r
7271         SetWindowText(hwnd, p);\r
7272         sel.cpMin = 999999;\r
7273         sel.cpMax = 999999;\r
7274         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7275         return 0;\r
7276       }\r
7277       break;\r
7278     case VK_DOWN:\r
7279       p = NextInHistory();\r
7280       if (p != NULL) {\r
7281         SetWindowText(hwnd, p);\r
7282         sel.cpMin = 999999;\r
7283         sel.cpMax = 999999;\r
7284         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7285         return 0;\r
7286       }\r
7287       break;\r
7288     case VK_HOME:\r
7289     case VK_END:\r
7290       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7291       /* fall thru */\r
7292     case VK_PRIOR:\r
7293     case VK_NEXT:\r
7294       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7295       return 0;\r
7296     }\r
7297     break;\r
7298   case WM_MBUTTONDOWN:\r
7299     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7300       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7301     break;\r
7302   case WM_RBUTTONUP:\r
7303     if (GetKeyState(VK_SHIFT) & ~1) {\r
7304       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7305         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7306     } else {\r
7307       POINT pt;\r
7308       HMENU hmenu;\r
7309       hmenu = LoadMenu(hInst, "InputMenu");\r
7310       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7311       if (sel.cpMin == sel.cpMax) {\r
7312         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7313         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7314       }\r
7315       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7316         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7317       }\r
7318       pt.x = LOWORD(lParam);\r
7319       pt.y = HIWORD(lParam);\r
7320       MenuPopup(hwnd, pt, hmenu, -1);\r
7321     }\r
7322     return 0;\r
7323   case WM_COMMAND:\r
7324     switch (LOWORD(wParam)) { \r
7325     case IDM_Undo:\r
7326       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7327       return 0;\r
7328     case IDM_SelectAll:\r
7329       sel.cpMin = 0;\r
7330       sel.cpMax = -1; /*999999?*/\r
7331       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7332       return 0;\r
7333     case IDM_Cut:\r
7334       SendMessage(hwnd, WM_CUT, 0, 0);\r
7335       return 0;\r
7336     case IDM_Paste:\r
7337       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7338       return 0;\r
7339     case IDM_Copy:\r
7340       SendMessage(hwnd, WM_COPY, 0, 0);\r
7341       return 0;\r
7342     }\r
7343     break;\r
7344   }\r
7345   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7346 }\r
7347 \r
7348 #define CO_MAX  100000\r
7349 #define CO_TRIM   1000\r
7350 \r
7351 LRESULT CALLBACK\r
7352 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7353 {\r
7354   static SnapData sd;\r
7355   HWND hText, hInput;\r
7356   RECT rect;\r
7357   static int sizeX, sizeY;\r
7358   int newSizeX, newSizeY;\r
7359   MINMAXINFO *mmi;\r
7360   WORD wMask;\r
7361 \r
7362   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7363   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7364 \r
7365   switch (message) {\r
7366   case WM_NOTIFY:\r
7367     if (((NMHDR*)lParam)->code == EN_LINK)\r
7368     {\r
7369       ENLINK *pLink = (ENLINK*)lParam;\r
7370       if (pLink->msg == WM_LBUTTONUP)\r
7371       {\r
7372         TEXTRANGE tr;\r
7373 \r
7374         tr.chrg = pLink->chrg;\r
7375         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7376         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7377         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7378         free(tr.lpstrText);\r
7379       }\r
7380     }\r
7381     break;\r
7382   case WM_INITDIALOG: /* message: initialize dialog box */\r
7383     hwndConsole = hDlg;\r
7384     SetFocus(hInput);\r
7385     consoleTextWindowProc = (WNDPROC)\r
7386       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7387     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7388     consoleInputWindowProc = (WNDPROC)\r
7389       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7390     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7391     Colorize(ColorNormal, TRUE);\r
7392     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7393     ChangedConsoleFont();\r
7394     GetClientRect(hDlg, &rect);\r
7395     sizeX = rect.right;\r
7396     sizeY = rect.bottom;\r
7397     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7398         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7399       WINDOWPLACEMENT wp;\r
7400       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7401       wp.length = sizeof(WINDOWPLACEMENT);\r
7402       wp.flags = 0;\r
7403       wp.showCmd = SW_SHOW;\r
7404       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7405       wp.rcNormalPosition.left = wpConsole.x;\r
7406       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7407       wp.rcNormalPosition.top = wpConsole.y;\r
7408       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7409       SetWindowPlacement(hDlg, &wp);\r
7410     }\r
7411 \r
7412    // [HGM] Chessknight's change 2004-07-13\r
7413    else { /* Determine Defaults */\r
7414        WINDOWPLACEMENT wp;\r
7415        wpConsole.x = wpMain.width + 1;\r
7416        wpConsole.y = wpMain.y;\r
7417        wpConsole.width = screenWidth -  wpMain.width;\r
7418        wpConsole.height = wpMain.height;\r
7419        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7420        wp.length = sizeof(WINDOWPLACEMENT);\r
7421        wp.flags = 0;\r
7422        wp.showCmd = SW_SHOW;\r
7423        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7424        wp.rcNormalPosition.left = wpConsole.x;\r
7425        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7426        wp.rcNormalPosition.top = wpConsole.y;\r
7427        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7428        SetWindowPlacement(hDlg, &wp);\r
7429     }\r
7430 \r
7431    // Allow hText to highlight URLs and send notifications on them\r
7432    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7433    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7434    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7435    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7436 \r
7437     return FALSE;\r
7438 \r
7439   case WM_SETFOCUS:\r
7440     SetFocus(hInput);\r
7441     return 0;\r
7442 \r
7443   case WM_CLOSE:\r
7444     ExitEvent(0);\r
7445     /* not reached */\r
7446     break;\r
7447 \r
7448   case WM_SIZE:\r
7449     if (IsIconic(hDlg)) break;\r
7450     newSizeX = LOWORD(lParam);\r
7451     newSizeY = HIWORD(lParam);\r
7452     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7453       RECT rectText, rectInput;\r
7454       POINT pt;\r
7455       int newTextHeight, newTextWidth;\r
7456       GetWindowRect(hText, &rectText);\r
7457       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7458       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7459       if (newTextHeight < 0) {\r
7460         newSizeY += -newTextHeight;\r
7461         newTextHeight = 0;\r
7462       }\r
7463       SetWindowPos(hText, NULL, 0, 0,\r
7464         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7465       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7466       pt.x = rectInput.left;\r
7467       pt.y = rectInput.top + newSizeY - sizeY;\r
7468       ScreenToClient(hDlg, &pt);\r
7469       SetWindowPos(hInput, NULL, \r
7470         pt.x, pt.y, /* needs client coords */   \r
7471         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7472         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7473     }\r
7474     sizeX = newSizeX;\r
7475     sizeY = newSizeY;\r
7476     break;\r
7477 \r
7478   case WM_GETMINMAXINFO:\r
7479     /* Prevent resizing window too small */\r
7480     mmi = (MINMAXINFO *) lParam;\r
7481     mmi->ptMinTrackSize.x = 100;\r
7482     mmi->ptMinTrackSize.y = 100;\r
7483     break;\r
7484 \r
7485   /* [AS] Snapping */\r
7486   case WM_ENTERSIZEMOVE:\r
7487     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7488 \r
7489   case WM_SIZING:\r
7490     return OnSizing( &sd, hDlg, wParam, lParam );\r
7491 \r
7492   case WM_MOVING:\r
7493     return OnMoving( &sd, hDlg, wParam, lParam );\r
7494 \r
7495   case WM_EXITSIZEMOVE:\r
7496         UpdateICSWidth(hText);\r
7497     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7498   }\r
7499 \r
7500   return DefWindowProc(hDlg, message, wParam, lParam);\r
7501 }\r
7502 \r
7503 \r
7504 VOID\r
7505 ConsoleCreate()\r
7506 {\r
7507   HWND hCons;\r
7508   if (hwndConsole) return;\r
7509   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7510   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7511 }\r
7512 \r
7513 \r
7514 VOID\r
7515 ConsoleOutput(char* data, int length, int forceVisible)\r
7516 {\r
7517   HWND hText;\r
7518   int trim, exlen;\r
7519   char *p, *q;\r
7520   char buf[CO_MAX+1];\r
7521   POINT pEnd;\r
7522   RECT rect;\r
7523   static int delayLF = 0;\r
7524   CHARRANGE savesel, sel;\r
7525 \r
7526   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7527   p = data;\r
7528   q = buf;\r
7529   if (delayLF) {\r
7530     *q++ = '\r';\r
7531     *q++ = '\n';\r
7532     delayLF = 0;\r
7533   }\r
7534   while (length--) {\r
7535     if (*p == '\n') {\r
7536       if (*++p) {\r
7537         *q++ = '\r';\r
7538         *q++ = '\n';\r
7539       } else {\r
7540         delayLF = 1;\r
7541       }\r
7542     } else if (*p == '\007') {\r
7543        MyPlaySound(&sounds[(int)SoundBell]);\r
7544        p++;\r
7545     } else {\r
7546       *q++ = *p++;\r
7547     }\r
7548   }\r
7549   *q = NULLCHAR;\r
7550   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7551   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7552   /* Save current selection */\r
7553   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7554   exlen = GetWindowTextLength(hText);\r
7555   /* Find out whether current end of text is visible */\r
7556   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7557   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7558   /* Trim existing text if it's too long */\r
7559   if (exlen + (q - buf) > CO_MAX) {\r
7560     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7561     sel.cpMin = 0;\r
7562     sel.cpMax = trim;\r
7563     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7564     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7565     exlen -= trim;\r
7566     savesel.cpMin -= trim;\r
7567     savesel.cpMax -= trim;\r
7568     if (exlen < 0) exlen = 0;\r
7569     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7570     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7571   }\r
7572   /* Append the new text */\r
7573   sel.cpMin = exlen;\r
7574   sel.cpMax = exlen;\r
7575   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7576   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7577   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7578   if (forceVisible || exlen == 0 ||\r
7579       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7580        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7581     /* Scroll to make new end of text visible if old end of text\r
7582        was visible or new text is an echo of user typein */\r
7583     sel.cpMin = 9999999;\r
7584     sel.cpMax = 9999999;\r
7585     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7586     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7587     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7588     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7589   }\r
7590   if (savesel.cpMax == exlen || forceVisible) {\r
7591     /* Move insert point to new end of text if it was at the old\r
7592        end of text or if the new text is an echo of user typein */\r
7593     sel.cpMin = 9999999;\r
7594     sel.cpMax = 9999999;\r
7595     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7596   } else {\r
7597     /* Restore previous selection */\r
7598     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7599   }\r
7600   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7601 }\r
7602 \r
7603 /*---------*/\r
7604 \r
7605 \r
7606 void\r
7607 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7608 {\r
7609   char buf[100];\r
7610   char *str;\r
7611   COLORREF oldFg, oldBg;\r
7612   HFONT oldFont;\r
7613   RECT rect;\r
7614 \r
7615   if(copyNumber > 1)\r
7616     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7617 \r
7618   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7619   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7620   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7621 \r
7622   rect.left = x;\r
7623   rect.right = x + squareSize;\r
7624   rect.top  = y;\r
7625   rect.bottom = y + squareSize;\r
7626   str = buf;\r
7627 \r
7628   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7629                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7630              y, ETO_CLIPPED|ETO_OPAQUE,\r
7631              &rect, str, strlen(str), NULL);\r
7632 \r
7633   (void) SetTextColor(hdc, oldFg);\r
7634   (void) SetBkColor(hdc, oldBg);\r
7635   (void) SelectObject(hdc, oldFont);\r
7636 }\r
7637 \r
7638 void\r
7639 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7640               RECT *rect, char *color, char *flagFell)\r
7641 {\r
7642   char buf[100];\r
7643   char *str;\r
7644   COLORREF oldFg, oldBg;\r
7645   HFONT oldFont;\r
7646 \r
7647   if (twoBoards && partnerUp) return;\r
7648   if (appData.clockMode) {\r
7649     if (tinyLayout)\r
7650       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7651     else\r
7652       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7653     str = buf;\r
7654   } else {\r
7655     str = color;\r
7656   }\r
7657 \r
7658   if (highlight) {\r
7659     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7660     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7661   } else {\r
7662     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7663     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7664   }\r
7665   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7666 \r
7667   JAWS_SILENCE\r
7668 \r
7669   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7670              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7671              rect, str, strlen(str), NULL);\r
7672   if(logoHeight > 0 && appData.clockMode) {\r
7673       RECT r;\r
7674       str += strlen(color)+2;\r
7675       r.top = rect->top + logoHeight/2;\r
7676       r.left = rect->left;\r
7677       r.right = rect->right;\r
7678       r.bottom = rect->bottom;\r
7679       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7680                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7681                  &r, str, strlen(str), NULL);\r
7682   }\r
7683   (void) SetTextColor(hdc, oldFg);\r
7684   (void) SetBkColor(hdc, oldBg);\r
7685   (void) SelectObject(hdc, oldFont);\r
7686 }\r
7687 \r
7688 \r
7689 int\r
7690 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7691            OVERLAPPED *ovl)\r
7692 {\r
7693   int ok, err;\r
7694 \r
7695   /* [AS]  */\r
7696   if( count <= 0 ) {\r
7697     if (appData.debugMode) {\r
7698       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7699     }\r
7700 \r
7701     return ERROR_INVALID_USER_BUFFER;\r
7702   }\r
7703 \r
7704   ResetEvent(ovl->hEvent);\r
7705   ovl->Offset = ovl->OffsetHigh = 0;\r
7706   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7707   if (ok) {\r
7708     err = NO_ERROR;\r
7709   } else {\r
7710     err = GetLastError();\r
7711     if (err == ERROR_IO_PENDING) {\r
7712       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7713       if (ok)\r
7714         err = NO_ERROR;\r
7715       else\r
7716         err = GetLastError();\r
7717     }\r
7718   }\r
7719   return err;\r
7720 }\r
7721 \r
7722 int\r
7723 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7724             OVERLAPPED *ovl)\r
7725 {\r
7726   int ok, err;\r
7727 \r
7728   ResetEvent(ovl->hEvent);\r
7729   ovl->Offset = ovl->OffsetHigh = 0;\r
7730   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7731   if (ok) {\r
7732     err = NO_ERROR;\r
7733   } else {\r
7734     err = GetLastError();\r
7735     if (err == ERROR_IO_PENDING) {\r
7736       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7737       if (ok)\r
7738         err = NO_ERROR;\r
7739       else\r
7740         err = GetLastError();\r
7741     }\r
7742 \r
7743   }\r
7744   return err;\r
7745 }\r
7746 \r
7747 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7748 void CheckForInputBufferFull( InputSource * is )\r
7749 {\r
7750     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7751         /* Look for end of line */\r
7752         char * p = is->buf;\r
7753         \r
7754         while( p < is->next && *p != '\n' ) {\r
7755             p++;\r
7756         }\r
7757 \r
7758         if( p >= is->next ) {\r
7759             if (appData.debugMode) {\r
7760                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7761             }\r
7762 \r
7763             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7764             is->count = (DWORD) -1;\r
7765             is->next = is->buf;\r
7766         }\r
7767     }\r
7768 }\r
7769 \r
7770 DWORD\r
7771 InputThread(LPVOID arg)\r
7772 {\r
7773   InputSource *is;\r
7774   OVERLAPPED ovl;\r
7775 \r
7776   is = (InputSource *) arg;\r
7777   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7778   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7779   while (is->hThread != NULL) {\r
7780     is->error = DoReadFile(is->hFile, is->next,\r
7781                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7782                            &is->count, &ovl);\r
7783     if (is->error == NO_ERROR) {\r
7784       is->next += is->count;\r
7785     } else {\r
7786       if (is->error == ERROR_BROKEN_PIPE) {\r
7787         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7788         is->count = 0;\r
7789       } else {\r
7790         is->count = (DWORD) -1;\r
7791         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7792         break; \r
7793       }\r
7794     }\r
7795 \r
7796     CheckForInputBufferFull( is );\r
7797 \r
7798     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7799 \r
7800     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7801 \r
7802     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7803   }\r
7804 \r
7805   CloseHandle(ovl.hEvent);\r
7806   CloseHandle(is->hFile);\r
7807 \r
7808   if (appData.debugMode) {\r
7809     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7810   }\r
7811 \r
7812   return 0;\r
7813 }\r
7814 \r
7815 \r
7816 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7817 DWORD\r
7818 NonOvlInputThread(LPVOID arg)\r
7819 {\r
7820   InputSource *is;\r
7821   char *p, *q;\r
7822   int i;\r
7823   char prev;\r
7824 \r
7825   is = (InputSource *) arg;\r
7826   while (is->hThread != NULL) {\r
7827     is->error = ReadFile(is->hFile, is->next,\r
7828                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7829                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7830     if (is->error == NO_ERROR) {\r
7831       /* Change CRLF to LF */\r
7832       if (is->next > is->buf) {\r
7833         p = is->next - 1;\r
7834         i = is->count + 1;\r
7835       } else {\r
7836         p = is->next;\r
7837         i = is->count;\r
7838       }\r
7839       q = p;\r
7840       prev = NULLCHAR;\r
7841       while (i > 0) {\r
7842         if (prev == '\r' && *p == '\n') {\r
7843           *(q-1) = '\n';\r
7844           is->count--;\r
7845         } else { \r
7846           *q++ = *p;\r
7847         }\r
7848         prev = *p++;\r
7849         i--;\r
7850       }\r
7851       *q = NULLCHAR;\r
7852       is->next = q;\r
7853     } else {\r
7854       if (is->error == ERROR_BROKEN_PIPE) {\r
7855         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7856         is->count = 0; \r
7857       } else {\r
7858         is->count = (DWORD) -1;\r
7859       }\r
7860     }\r
7861 \r
7862     CheckForInputBufferFull( is );\r
7863 \r
7864     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7865 \r
7866     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7867 \r
7868     if (is->count < 0) break;  /* Quit on error */\r
7869   }\r
7870   CloseHandle(is->hFile);\r
7871   return 0;\r
7872 }\r
7873 \r
7874 DWORD\r
7875 SocketInputThread(LPVOID arg)\r
7876 {\r
7877   InputSource *is;\r
7878 \r
7879   is = (InputSource *) arg;\r
7880   while (is->hThread != NULL) {\r
7881     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7882     if ((int)is->count == SOCKET_ERROR) {\r
7883       is->count = (DWORD) -1;\r
7884       is->error = WSAGetLastError();\r
7885     } else {\r
7886       is->error = NO_ERROR;\r
7887       is->next += is->count;\r
7888       if (is->count == 0 && is->second == is) {\r
7889         /* End of file on stderr; quit with no message */\r
7890         break;\r
7891       }\r
7892     }\r
7893     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7894 \r
7895     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7896 \r
7897     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7898   }\r
7899   return 0;\r
7900 }\r
7901 \r
7902 VOID\r
7903 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7904 {\r
7905   InputSource *is;\r
7906 \r
7907   is = (InputSource *) lParam;\r
7908   if (is->lineByLine) {\r
7909     /* Feed in lines one by one */\r
7910     char *p = is->buf;\r
7911     char *q = p;\r
7912     while (q < is->next) {\r
7913       if (*q++ == '\n') {\r
7914         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7915         p = q;\r
7916       }\r
7917     }\r
7918     \r
7919     /* Move any partial line to the start of the buffer */\r
7920     q = is->buf;\r
7921     while (p < is->next) {\r
7922       *q++ = *p++;\r
7923     }\r
7924     is->next = q;\r
7925 \r
7926     if (is->error != NO_ERROR || is->count == 0) {\r
7927       /* Notify backend of the error.  Note: If there was a partial\r
7928          line at the end, it is not flushed through. */\r
7929       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7930     }\r
7931   } else {\r
7932     /* Feed in the whole chunk of input at once */\r
7933     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7934     is->next = is->buf;\r
7935   }\r
7936 }\r
7937 \r
7938 /*---------------------------------------------------------------------------*\\r
7939  *\r
7940  *  Menu enables. Used when setting various modes.\r
7941  *\r
7942 \*---------------------------------------------------------------------------*/\r
7943 \r
7944 typedef struct {\r
7945   int item;\r
7946   int flags;\r
7947 } Enables;\r
7948 \r
7949 VOID\r
7950 GreyRevert(Boolean grey)\r
7951 { // [HGM] vari: for retracting variations in local mode\r
7952   HMENU hmenu = GetMenu(hwndMain);\r
7953   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7954   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7955 }\r
7956 \r
7957 VOID\r
7958 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7959 {\r
7960   while (enab->item > 0) {\r
7961     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7962     enab++;\r
7963   }\r
7964 }\r
7965 \r
7966 Enables gnuEnables[] = {\r
7967   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7980 \r
7981   // Needed to switch from ncp to GNU mode on Engine Load\r
7982   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7983   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7984   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7989   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7990   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7991   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7992   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7993   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7994   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7995   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7996   { -1, -1 }\r
7997 };\r
7998 \r
7999 Enables icsEnables[] = {\r
8000   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8001   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8002   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8003   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8004   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8005   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8008   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8016   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8017   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8018   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8019   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8020   { -1, -1 }\r
8021 };\r
8022 \r
8023 #if ZIPPY\r
8024 Enables zippyEnables[] = {\r
8025   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8026   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8027   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8028   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8029   { -1, -1 }\r
8030 };\r
8031 #endif\r
8032 \r
8033 Enables ncpEnables[] = {\r
8034   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8042   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8043   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8044   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8045   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8046   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8047   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8048   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8050   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8051   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8052   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8053   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8054   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8055   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8056   { -1, -1 }\r
8057 };\r
8058 \r
8059 Enables trainingOnEnables[] = {\r
8060   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8065   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8066   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8068   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8069   { -1, -1 }\r
8070 };\r
8071 \r
8072 Enables trainingOffEnables[] = {\r
8073   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8074   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8075   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8076   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8077   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8078   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8079   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8080   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8081   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8082   { -1, -1 }\r
8083 };\r
8084 \r
8085 /* These modify either ncpEnables or gnuEnables */\r
8086 Enables cmailEnables[] = {\r
8087   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8088   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8089   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8090   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8091   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8092   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8093   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8094   { -1, -1 }\r
8095 };\r
8096 \r
8097 Enables machineThinkingEnables[] = {\r
8098   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8104   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8105   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8107   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8108   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8109   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8110   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8111 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8112   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8113   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8114   { -1, -1 }\r
8115 };\r
8116 \r
8117 Enables userThinkingEnables[] = {\r
8118   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8119   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8120   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8121   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8122   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8123   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8124   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8125   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8126   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8127   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8128   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8129   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8130   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8131 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8132   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8133   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8134   { -1, -1 }\r
8135 };\r
8136 \r
8137 /*---------------------------------------------------------------------------*\\r
8138  *\r
8139  *  Front-end interface functions exported by XBoard.\r
8140  *  Functions appear in same order as prototypes in frontend.h.\r
8141  * \r
8142 \*---------------------------------------------------------------------------*/\r
8143 VOID\r
8144 CheckMark(UINT item, int state)\r
8145 {\r
8146     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8147 }\r
8148 \r
8149 VOID\r
8150 ModeHighlight()\r
8151 {\r
8152   static UINT prevChecked = 0;\r
8153   static int prevPausing = 0;\r
8154   UINT nowChecked;\r
8155 \r
8156   if (pausing != prevPausing) {\r
8157     prevPausing = pausing;\r
8158     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8159                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8160     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8161   }\r
8162 \r
8163   switch (gameMode) {\r
8164   case BeginningOfGame:\r
8165     if (appData.icsActive)\r
8166       nowChecked = IDM_IcsClient;\r
8167     else if (appData.noChessProgram)\r
8168       nowChecked = IDM_EditGame;\r
8169     else\r
8170       nowChecked = IDM_MachineBlack;\r
8171     break;\r
8172   case MachinePlaysBlack:\r
8173     nowChecked = IDM_MachineBlack;\r
8174     break;\r
8175   case MachinePlaysWhite:\r
8176     nowChecked = IDM_MachineWhite;\r
8177     break;\r
8178   case TwoMachinesPlay:\r
8179     nowChecked = IDM_TwoMachines;\r
8180     break;\r
8181   case AnalyzeMode:\r
8182     nowChecked = IDM_AnalysisMode;\r
8183     break;\r
8184   case AnalyzeFile:\r
8185     nowChecked = IDM_AnalyzeFile;\r
8186     break;\r
8187   case EditGame:\r
8188     nowChecked = IDM_EditGame;\r
8189     break;\r
8190   case PlayFromGameFile:\r
8191     nowChecked = IDM_LoadGame;\r
8192     break;\r
8193   case EditPosition:\r
8194     nowChecked = IDM_EditPosition;\r
8195     break;\r
8196   case Training:\r
8197     nowChecked = IDM_Training;\r
8198     break;\r
8199   case IcsPlayingWhite:\r
8200   case IcsPlayingBlack:\r
8201   case IcsObserving:\r
8202   case IcsIdle:\r
8203     nowChecked = IDM_IcsClient;\r
8204     break;\r
8205   default:\r
8206   case EndOfGame:\r
8207     nowChecked = 0;\r
8208     break;\r
8209   }\r
8210   CheckMark(prevChecked, MF_UNCHECKED);\r
8211   CheckMark(nowChecked, MF_CHECKED);\r
8212   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8213 \r
8214   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8215     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8216                           MF_BYCOMMAND|MF_ENABLED);\r
8217   } else {\r
8218     (void) EnableMenuItem(GetMenu(hwndMain), \r
8219                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8220   }\r
8221 \r
8222   prevChecked = nowChecked;\r
8223 \r
8224   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8225   if (appData.icsActive) {\r
8226        if (appData.icsEngineAnalyze) {\r
8227                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8228        } else {\r
8229                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8230        }\r
8231   }\r
8232   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8233 }\r
8234 \r
8235 VOID\r
8236 SetICSMode()\r
8237 {\r
8238   HMENU hmenu = GetMenu(hwndMain);\r
8239   SetMenuEnables(hmenu, icsEnables);\r
8240   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8241     MF_BYCOMMAND|MF_ENABLED);\r
8242 #if ZIPPY\r
8243   if (appData.zippyPlay) {\r
8244     SetMenuEnables(hmenu, zippyEnables);\r
8245     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8246          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8247           MF_BYCOMMAND|MF_ENABLED);\r
8248   }\r
8249 #endif\r
8250 }\r
8251 \r
8252 VOID\r
8253 SetGNUMode()\r
8254 {\r
8255   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8256 }\r
8257 \r
8258 VOID\r
8259 SetNCPMode()\r
8260 {\r
8261   HMENU hmenu = GetMenu(hwndMain);\r
8262   SetMenuEnables(hmenu, ncpEnables);\r
8263     DrawMenuBar(hwndMain);\r
8264 }\r
8265 \r
8266 VOID\r
8267 SetCmailMode()\r
8268 {\r
8269   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8270 }\r
8271 \r
8272 VOID \r
8273 SetTrainingModeOn()\r
8274 {\r
8275   int i;\r
8276   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8277   for (i = 0; i < N_BUTTONS; i++) {\r
8278     if (buttonDesc[i].hwnd != NULL)\r
8279       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8280   }\r
8281   CommentPopDown();\r
8282 }\r
8283 \r
8284 VOID SetTrainingModeOff()\r
8285 {\r
8286   int i;\r
8287   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8288   for (i = 0; i < N_BUTTONS; i++) {\r
8289     if (buttonDesc[i].hwnd != NULL)\r
8290       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8291   }\r
8292 }\r
8293 \r
8294 \r
8295 VOID\r
8296 SetUserThinkingEnables()\r
8297 {\r
8298   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8299 }\r
8300 \r
8301 VOID\r
8302 SetMachineThinkingEnables()\r
8303 {\r
8304   HMENU hMenu = GetMenu(hwndMain);\r
8305   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8306 \r
8307   SetMenuEnables(hMenu, machineThinkingEnables);\r
8308 \r
8309   if (gameMode == MachinePlaysBlack) {\r
8310     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8311   } else if (gameMode == MachinePlaysWhite) {\r
8312     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8313   } else if (gameMode == TwoMachinesPlay) {\r
8314     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8315   }\r
8316 }\r
8317 \r
8318 \r
8319 VOID\r
8320 DisplayTitle(char *str)\r
8321 {\r
8322   char title[MSG_SIZ], *host;\r
8323   if (str[0] != NULLCHAR) {\r
8324     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8325   } else if (appData.icsActive) {\r
8326     if (appData.icsCommPort[0] != NULLCHAR)\r
8327       host = "ICS";\r
8328     else \r
8329       host = appData.icsHost;\r
8330       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8331   } else if (appData.noChessProgram) {\r
8332     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8333   } else {\r
8334     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8335     strcat(title, ": ");\r
8336     strcat(title, first.tidy);\r
8337   }\r
8338   SetWindowText(hwndMain, title);\r
8339 }\r
8340 \r
8341 \r
8342 VOID\r
8343 DisplayMessage(char *str1, char *str2)\r
8344 {\r
8345   HDC hdc;\r
8346   HFONT oldFont;\r
8347   int remain = MESSAGE_TEXT_MAX - 1;\r
8348   int len;\r
8349 \r
8350   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8351   messageText[0] = NULLCHAR;\r
8352   if (*str1) {\r
8353     len = strlen(str1);\r
8354     if (len > remain) len = remain;\r
8355     strncpy(messageText, str1, len);\r
8356     messageText[len] = NULLCHAR;\r
8357     remain -= len;\r
8358   }\r
8359   if (*str2 && remain >= 2) {\r
8360     if (*str1) {\r
8361       strcat(messageText, "  ");\r
8362       remain -= 2;\r
8363     }\r
8364     len = strlen(str2);\r
8365     if (len > remain) len = remain;\r
8366     strncat(messageText, str2, len);\r
8367   }\r
8368   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8369   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8370 \r
8371   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8372 \r
8373   SAYMACHINEMOVE();\r
8374 \r
8375   hdc = GetDC(hwndMain);\r
8376   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8377   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8378              &messageRect, messageText, strlen(messageText), NULL);\r
8379   (void) SelectObject(hdc, oldFont);\r
8380   (void) ReleaseDC(hwndMain, hdc);\r
8381 }\r
8382 \r
8383 VOID\r
8384 DisplayError(char *str, int error)\r
8385 {\r
8386   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8387   int len;\r
8388 \r
8389   if (error == 0) {\r
8390     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8391   } else {\r
8392     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8393                         NULL, error, LANG_NEUTRAL,\r
8394                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8395     if (len > 0) {\r
8396       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8397     } else {\r
8398       ErrorMap *em = errmap;\r
8399       while (em->err != 0 && em->err != error) em++;\r
8400       if (em->err != 0) {\r
8401         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8402       } else {\r
8403         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8404       }\r
8405     }\r
8406   }\r
8407   \r
8408   ErrorPopUp(_("Error"), buf);\r
8409 }\r
8410 \r
8411 \r
8412 VOID\r
8413 DisplayMoveError(char *str)\r
8414 {\r
8415   fromX = fromY = -1;\r
8416   ClearHighlights();\r
8417   DrawPosition(FALSE, NULL);\r
8418   if (appData.popupMoveErrors) {\r
8419     ErrorPopUp(_("Error"), str);\r
8420   } else {\r
8421     DisplayMessage(str, "");\r
8422     moveErrorMessageUp = TRUE;\r
8423   }\r
8424 }\r
8425 \r
8426 VOID\r
8427 DisplayFatalError(char *str, int error, int exitStatus)\r
8428 {\r
8429   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8430   int len;\r
8431   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8432 \r
8433   if (error != 0) {\r
8434     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8435                         NULL, error, LANG_NEUTRAL,\r
8436                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8437     if (len > 0) {\r
8438       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8439     } else {\r
8440       ErrorMap *em = errmap;\r
8441       while (em->err != 0 && em->err != error) em++;\r
8442       if (em->err != 0) {\r
8443         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8444       } else {\r
8445         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8446       }\r
8447     }\r
8448     str = buf;\r
8449   }\r
8450   if (appData.debugMode) {\r
8451     fprintf(debugFP, "%s: %s\n", label, str);\r
8452   }\r
8453   if (appData.popupExitMessage) {\r
8454     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8455                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8456   }\r
8457   ExitEvent(exitStatus);\r
8458 }\r
8459 \r
8460 \r
8461 VOID\r
8462 DisplayInformation(char *str)\r
8463 {\r
8464   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8465 }\r
8466 \r
8467 \r
8468 VOID\r
8469 DisplayNote(char *str)\r
8470 {\r
8471   ErrorPopUp(_("Note"), str);\r
8472 }\r
8473 \r
8474 \r
8475 typedef struct {\r
8476   char *title, *question, *replyPrefix;\r
8477   ProcRef pr;\r
8478 } QuestionParams;\r
8479 \r
8480 LRESULT CALLBACK\r
8481 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8482 {\r
8483   static QuestionParams *qp;\r
8484   char reply[MSG_SIZ];\r
8485   int len, err;\r
8486 \r
8487   switch (message) {\r
8488   case WM_INITDIALOG:\r
8489     qp = (QuestionParams *) lParam;\r
8490     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8491     Translate(hDlg, DLG_Question);\r
8492     SetWindowText(hDlg, qp->title);\r
8493     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8494     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8495     return FALSE;\r
8496 \r
8497   case WM_COMMAND:\r
8498     switch (LOWORD(wParam)) {\r
8499     case IDOK:\r
8500       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8501       if (*reply) strcat(reply, " ");\r
8502       len = strlen(reply);\r
8503       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8504       strcat(reply, "\n");\r
8505       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8506       EndDialog(hDlg, TRUE);\r
8507       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8508       return TRUE;\r
8509     case IDCANCEL:\r
8510       EndDialog(hDlg, FALSE);\r
8511       return TRUE;\r
8512     default:\r
8513       break;\r
8514     }\r
8515     break;\r
8516   }\r
8517   return FALSE;\r
8518 }\r
8519 \r
8520 VOID\r
8521 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8522 {\r
8523     QuestionParams qp;\r
8524     FARPROC lpProc;\r
8525     \r
8526     qp.title = title;\r
8527     qp.question = question;\r
8528     qp.replyPrefix = replyPrefix;\r
8529     qp.pr = pr;\r
8530     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8531     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8532       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8533     FreeProcInstance(lpProc);\r
8534 }\r
8535 \r
8536 /* [AS] Pick FRC position */\r
8537 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8538 {\r
8539     static int * lpIndexFRC;\r
8540     BOOL index_is_ok;\r
8541     char buf[16];\r
8542 \r
8543     switch( message )\r
8544     {\r
8545     case WM_INITDIALOG:\r
8546         lpIndexFRC = (int *) lParam;\r
8547 \r
8548         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8549         Translate(hDlg, DLG_NewGameFRC);\r
8550 \r
8551         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8552         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8553         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8554         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8555 \r
8556         break;\r
8557 \r
8558     case WM_COMMAND:\r
8559         switch( LOWORD(wParam) ) {\r
8560         case IDOK:\r
8561             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8562             EndDialog( hDlg, 0 );\r
8563             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8564             return TRUE;\r
8565         case IDCANCEL:\r
8566             EndDialog( hDlg, 1 );   \r
8567             return TRUE;\r
8568         case IDC_NFG_Edit:\r
8569             if( HIWORD(wParam) == EN_CHANGE ) {\r
8570                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8571 \r
8572                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8573             }\r
8574             return TRUE;\r
8575         case IDC_NFG_Random:\r
8576           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8577             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8578             return TRUE;\r
8579         }\r
8580 \r
8581         break;\r
8582     }\r
8583 \r
8584     return FALSE;\r
8585 }\r
8586 \r
8587 int NewGameFRC()\r
8588 {\r
8589     int result;\r
8590     int index = appData.defaultFrcPosition;\r
8591     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8592 \r
8593     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8594 \r
8595     if( result == 0 ) {\r
8596         appData.defaultFrcPosition = index;\r
8597     }\r
8598 \r
8599     return result;\r
8600 }\r
8601 \r
8602 /* [AS] Game list options. Refactored by HGM */\r
8603 \r
8604 HWND gameListOptionsDialog;\r
8605 \r
8606 // low-level front-end: clear text edit / list widget\r
8607 void\r
8608 \r
8609 GLT_ClearList()\r
8610 {\r
8611     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8612 }\r
8613 \r
8614 // low-level front-end: clear text edit / list widget\r
8615 void\r
8616 GLT_DeSelectList()\r
8617 {\r
8618     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8619 }\r
8620 \r
8621 // low-level front-end: append line to text edit / list widget\r
8622 void\r
8623 GLT_AddToList( char *name )\r
8624 {\r
8625     if( name != 0 ) {\r
8626             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8627     }\r
8628 }\r
8629 \r
8630 // low-level front-end: get line from text edit / list widget\r
8631 Boolean\r
8632 GLT_GetFromList( int index, char *name )\r
8633 {\r
8634     if( name != 0 ) {\r
8635             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8636                 return TRUE;\r
8637     }\r
8638     return FALSE;\r
8639 }\r
8640 \r
8641 void GLT_MoveSelection( HWND hDlg, int delta )\r
8642 {\r
8643     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8644     int idx2 = idx1 + delta;\r
8645     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8646 \r
8647     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8648         char buf[128];\r
8649 \r
8650         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8651         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8652         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8653         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8654     }\r
8655 }\r
8656 \r
8657 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8658 {\r
8659     switch( message )\r
8660     {\r
8661     case WM_INITDIALOG:\r
8662         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8663         \r
8664         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8665         Translate(hDlg, DLG_GameListOptions);\r
8666 \r
8667         /* Initialize list */\r
8668         GLT_TagsToList( lpUserGLT );\r
8669 \r
8670         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8671 \r
8672         break;\r
8673 \r
8674     case WM_COMMAND:\r
8675         switch( LOWORD(wParam) ) {\r
8676         case IDOK:\r
8677             GLT_ParseList();\r
8678             EndDialog( hDlg, 0 );\r
8679             return TRUE;\r
8680         case IDCANCEL:\r
8681             EndDialog( hDlg, 1 );\r
8682             return TRUE;\r
8683 \r
8684         case IDC_GLT_Default:\r
8685             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8686             return TRUE;\r
8687 \r
8688         case IDC_GLT_Restore:\r
8689             GLT_TagsToList( appData.gameListTags );\r
8690             return TRUE;\r
8691 \r
8692         case IDC_GLT_Up:\r
8693             GLT_MoveSelection( hDlg, -1 );\r
8694             return TRUE;\r
8695 \r
8696         case IDC_GLT_Down:\r
8697             GLT_MoveSelection( hDlg, +1 );\r
8698             return TRUE;\r
8699         }\r
8700 \r
8701         break;\r
8702     }\r
8703 \r
8704     return FALSE;\r
8705 }\r
8706 \r
8707 int GameListOptions()\r
8708 {\r
8709     int result;\r
8710     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8711 \r
8712       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8713 \r
8714     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8715 \r
8716     if( result == 0 ) {\r
8717         char *oldTags = appData.gameListTags;\r
8718         /* [AS] Memory leak here! */\r
8719         appData.gameListTags = strdup( lpUserGLT ); \r
8720         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8721             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8722     }\r
8723 \r
8724     return result;\r
8725 }\r
8726 \r
8727 VOID\r
8728 DisplayIcsInteractionTitle(char *str)\r
8729 {\r
8730   char consoleTitle[MSG_SIZ];\r
8731 \r
8732     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8733     SetWindowText(hwndConsole, consoleTitle);\r
8734 \r
8735     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8736       char buf[MSG_SIZ], *p = buf, *q;\r
8737         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8738       do {\r
8739         q = strchr(p, ';');\r
8740         if(q) *q++ = 0;\r
8741         if(*p) ChatPopUp(p);\r
8742       } while(p=q);\r
8743     }\r
8744 \r
8745     SetActiveWindow(hwndMain);\r
8746 }\r
8747 \r
8748 void\r
8749 DrawPosition(int fullRedraw, Board board)\r
8750 {\r
8751   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8752 }\r
8753 \r
8754 void NotifyFrontendLogin()\r
8755 {\r
8756         if (hwndConsole)\r
8757                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8758 }\r
8759 \r
8760 VOID\r
8761 ResetFrontEnd()\r
8762 {\r
8763   fromX = fromY = -1;\r
8764   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8765     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8766     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8767     dragInfo.lastpos = dragInfo.pos;\r
8768     dragInfo.start.x = dragInfo.start.y = -1;\r
8769     dragInfo.from = dragInfo.start;\r
8770     ReleaseCapture();\r
8771     DrawPosition(TRUE, NULL);\r
8772   }\r
8773   TagsPopDown();\r
8774 }\r
8775 \r
8776 \r
8777 VOID\r
8778 CommentPopUp(char *title, char *str)\r
8779 {\r
8780   HWND hwnd = GetActiveWindow();\r
8781   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8782   SAY(str);\r
8783   SetActiveWindow(hwnd);\r
8784 }\r
8785 \r
8786 VOID\r
8787 CommentPopDown(void)\r
8788 {\r
8789   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8790   if (commentDialog) {\r
8791     ShowWindow(commentDialog, SW_HIDE);\r
8792   }\r
8793   commentUp = FALSE;\r
8794 }\r
8795 \r
8796 VOID\r
8797 EditCommentPopUp(int index, char *title, char *str)\r
8798 {\r
8799   EitherCommentPopUp(index, title, str, TRUE);\r
8800 }\r
8801 \r
8802 \r
8803 int\r
8804 Roar()\r
8805 {\r
8806   MyPlaySound(&sounds[(int)SoundRoar]);\r
8807   return 1;\r
8808 }\r
8809 \r
8810 VOID\r
8811 RingBell()\r
8812 {\r
8813   MyPlaySound(&sounds[(int)SoundMove]);\r
8814 }\r
8815 \r
8816 VOID PlayIcsWinSound()\r
8817 {\r
8818   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8819 }\r
8820 \r
8821 VOID PlayIcsLossSound()\r
8822 {\r
8823   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8824 }\r
8825 \r
8826 VOID PlayIcsDrawSound()\r
8827 {\r
8828   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8829 }\r
8830 \r
8831 VOID PlayIcsUnfinishedSound()\r
8832 {\r
8833   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8834 }\r
8835 \r
8836 VOID\r
8837 PlayAlarmSound()\r
8838 {\r
8839   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8840 }\r
8841 \r
8842 VOID\r
8843 PlayTellSound()\r
8844 {\r
8845   MyPlaySound(&textAttribs[ColorTell].sound);\r
8846 }\r
8847 \r
8848 \r
8849 VOID\r
8850 EchoOn()\r
8851 {\r
8852   HWND hInput;\r
8853   consoleEcho = TRUE;\r
8854   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8855   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8856   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8857 }\r
8858 \r
8859 \r
8860 VOID\r
8861 EchoOff()\r
8862 {\r
8863   CHARFORMAT cf;\r
8864   HWND hInput;\r
8865   consoleEcho = FALSE;\r
8866   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8867   /* This works OK: set text and background both to the same color */\r
8868   cf = consoleCF;\r
8869   cf.crTextColor = COLOR_ECHOOFF;\r
8870   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8871   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8872 }\r
8873 \r
8874 /* No Raw()...? */\r
8875 \r
8876 void Colorize(ColorClass cc, int continuation)\r
8877 {\r
8878   currentColorClass = cc;\r
8879   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8880   consoleCF.crTextColor = textAttribs[cc].color;\r
8881   consoleCF.dwEffects = textAttribs[cc].effects;\r
8882   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8883 }\r
8884 \r
8885 char *\r
8886 UserName()\r
8887 {\r
8888   static char buf[MSG_SIZ];\r
8889   DWORD bufsiz = MSG_SIZ;\r
8890 \r
8891   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8892         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8893   }\r
8894   if (!GetUserName(buf, &bufsiz)) {\r
8895     /*DisplayError("Error getting user name", GetLastError());*/\r
8896     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8897   }\r
8898   return buf;\r
8899 }\r
8900 \r
8901 char *\r
8902 HostName()\r
8903 {\r
8904   static char buf[MSG_SIZ];\r
8905   DWORD bufsiz = MSG_SIZ;\r
8906 \r
8907   if (!GetComputerName(buf, &bufsiz)) {\r
8908     /*DisplayError("Error getting host name", GetLastError());*/\r
8909     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8910   }\r
8911   return buf;\r
8912 }\r
8913 \r
8914 \r
8915 int\r
8916 ClockTimerRunning()\r
8917 {\r
8918   return clockTimerEvent != 0;\r
8919 }\r
8920 \r
8921 int\r
8922 StopClockTimer()\r
8923 {\r
8924   if (clockTimerEvent == 0) return FALSE;\r
8925   KillTimer(hwndMain, clockTimerEvent);\r
8926   clockTimerEvent = 0;\r
8927   return TRUE;\r
8928 }\r
8929 \r
8930 void\r
8931 StartClockTimer(long millisec)\r
8932 {\r
8933   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8934                              (UINT) millisec, NULL);\r
8935 }\r
8936 \r
8937 void\r
8938 DisplayWhiteClock(long timeRemaining, int highlight)\r
8939 {\r
8940   HDC hdc;\r
8941   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8942 \r
8943   if(appData.noGUI) return;\r
8944   hdc = GetDC(hwndMain);\r
8945   if (!IsIconic(hwndMain)) {\r
8946     DisplayAClock(hdc, timeRemaining, highlight, \r
8947                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8948   }\r
8949   if (highlight && iconCurrent == iconBlack) {\r
8950     iconCurrent = iconWhite;\r
8951     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8952     if (IsIconic(hwndMain)) {\r
8953       DrawIcon(hdc, 2, 2, iconCurrent);\r
8954     }\r
8955   }\r
8956   (void) ReleaseDC(hwndMain, hdc);\r
8957   if (hwndConsole)\r
8958     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8959 }\r
8960 \r
8961 void\r
8962 DisplayBlackClock(long timeRemaining, int highlight)\r
8963 {\r
8964   HDC hdc;\r
8965   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8966 \r
8967 \r
8968   if(appData.noGUI) return;\r
8969   hdc = GetDC(hwndMain);\r
8970   if (!IsIconic(hwndMain)) {\r
8971     DisplayAClock(hdc, timeRemaining, highlight, \r
8972                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8973   }\r
8974   if (highlight && iconCurrent == iconWhite) {\r
8975     iconCurrent = iconBlack;\r
8976     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8977     if (IsIconic(hwndMain)) {\r
8978       DrawIcon(hdc, 2, 2, iconCurrent);\r
8979     }\r
8980   }\r
8981   (void) ReleaseDC(hwndMain, hdc);\r
8982   if (hwndConsole)\r
8983     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8984 }\r
8985 \r
8986 \r
8987 int\r
8988 LoadGameTimerRunning()\r
8989 {\r
8990   return loadGameTimerEvent != 0;\r
8991 }\r
8992 \r
8993 int\r
8994 StopLoadGameTimer()\r
8995 {\r
8996   if (loadGameTimerEvent == 0) return FALSE;\r
8997   KillTimer(hwndMain, loadGameTimerEvent);\r
8998   loadGameTimerEvent = 0;\r
8999   return TRUE;\r
9000 }\r
9001 \r
9002 void\r
9003 StartLoadGameTimer(long millisec)\r
9004 {\r
9005   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9006                                 (UINT) millisec, NULL);\r
9007 }\r
9008 \r
9009 void\r
9010 AutoSaveGame()\r
9011 {\r
9012   char *defName;\r
9013   FILE *f;\r
9014   char fileTitle[MSG_SIZ];\r
9015 \r
9016   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9017   f = OpenFileDialog(hwndMain, "a", defName,\r
9018                      appData.oldSaveStyle ? "gam" : "pgn",\r
9019                      GAME_FILT, \r
9020                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9021   if (f != NULL) {\r
9022     SaveGame(f, 0, "");\r
9023     fclose(f);\r
9024   }\r
9025 }\r
9026 \r
9027 \r
9028 void\r
9029 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9030 {\r
9031   if (delayedTimerEvent != 0) {\r
9032     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9033       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9034     }\r
9035     KillTimer(hwndMain, delayedTimerEvent);\r
9036     delayedTimerEvent = 0;\r
9037     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9038     delayedTimerCallback();\r
9039   }\r
9040   delayedTimerCallback = cb;\r
9041   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9042                                 (UINT) millisec, NULL);\r
9043 }\r
9044 \r
9045 DelayedEventCallback\r
9046 GetDelayedEvent()\r
9047 {\r
9048   if (delayedTimerEvent) {\r
9049     return delayedTimerCallback;\r
9050   } else {\r
9051     return NULL;\r
9052   }\r
9053 }\r
9054 \r
9055 void\r
9056 CancelDelayedEvent()\r
9057 {\r
9058   if (delayedTimerEvent) {\r
9059     KillTimer(hwndMain, delayedTimerEvent);\r
9060     delayedTimerEvent = 0;\r
9061   }\r
9062 }\r
9063 \r
9064 DWORD GetWin32Priority(int nice)\r
9065 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9066 /*\r
9067 REALTIME_PRIORITY_CLASS     0x00000100\r
9068 HIGH_PRIORITY_CLASS         0x00000080\r
9069 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9070 NORMAL_PRIORITY_CLASS       0x00000020\r
9071 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9072 IDLE_PRIORITY_CLASS         0x00000040\r
9073 */\r
9074         if (nice < -15) return 0x00000080;\r
9075         if (nice < 0)   return 0x00008000;\r
9076         if (nice == 0)  return 0x00000020;\r
9077         if (nice < 15)  return 0x00004000;\r
9078         return 0x00000040;\r
9079 }\r
9080 \r
9081 void RunCommand(char *cmdLine)\r
9082 {\r
9083   /* Now create the child process. */\r
9084   STARTUPINFO siStartInfo;\r
9085   PROCESS_INFORMATION piProcInfo;\r
9086 \r
9087   siStartInfo.cb = sizeof(STARTUPINFO);\r
9088   siStartInfo.lpReserved = NULL;\r
9089   siStartInfo.lpDesktop = NULL;\r
9090   siStartInfo.lpTitle = NULL;\r
9091   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9092   siStartInfo.cbReserved2 = 0;\r
9093   siStartInfo.lpReserved2 = NULL;\r
9094   siStartInfo.hStdInput = NULL;\r
9095   siStartInfo.hStdOutput = NULL;\r
9096   siStartInfo.hStdError = NULL;\r
9097 \r
9098   CreateProcess(NULL,\r
9099                 cmdLine,           /* command line */\r
9100                 NULL,      /* process security attributes */\r
9101                 NULL,      /* primary thread security attrs */\r
9102                 TRUE,      /* handles are inherited */\r
9103                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9104                 NULL,      /* use parent's environment */\r
9105                 NULL,\r
9106                 &siStartInfo, /* STARTUPINFO pointer */\r
9107                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9108 \r
9109   CloseHandle(piProcInfo.hThread);\r
9110 }\r
9111 \r
9112 /* Start a child process running the given program.\r
9113    The process's standard output can be read from "from", and its\r
9114    standard input can be written to "to".\r
9115    Exit with fatal error if anything goes wrong.\r
9116    Returns an opaque pointer that can be used to destroy the process\r
9117    later.\r
9118 */\r
9119 int\r
9120 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9121 {\r
9122 #define BUFSIZE 4096\r
9123 \r
9124   HANDLE hChildStdinRd, hChildStdinWr,\r
9125     hChildStdoutRd, hChildStdoutWr;\r
9126   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9127   SECURITY_ATTRIBUTES saAttr;\r
9128   BOOL fSuccess;\r
9129   PROCESS_INFORMATION piProcInfo;\r
9130   STARTUPINFO siStartInfo;\r
9131   ChildProc *cp;\r
9132   char buf[MSG_SIZ];\r
9133   DWORD err;\r
9134 \r
9135   if (appData.debugMode) {\r
9136     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9137   }\r
9138 \r
9139   *pr = NoProc;\r
9140 \r
9141   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9142   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9143   saAttr.bInheritHandle = TRUE;\r
9144   saAttr.lpSecurityDescriptor = NULL;\r
9145 \r
9146   /*\r
9147    * The steps for redirecting child's STDOUT:\r
9148    *     1. Create anonymous pipe to be STDOUT for child.\r
9149    *     2. Create a noninheritable duplicate of read handle,\r
9150    *         and close the inheritable read handle.\r
9151    */\r
9152 \r
9153   /* Create a pipe for the child's STDOUT. */\r
9154   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9155     return GetLastError();\r
9156   }\r
9157 \r
9158   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9159   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9160                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9161                              FALSE,     /* not inherited */\r
9162                              DUPLICATE_SAME_ACCESS);\r
9163   if (! fSuccess) {\r
9164     return GetLastError();\r
9165   }\r
9166   CloseHandle(hChildStdoutRd);\r
9167 \r
9168   /*\r
9169    * The steps for redirecting child's STDIN:\r
9170    *     1. Create anonymous pipe to be STDIN for child.\r
9171    *     2. Create a noninheritable duplicate of write handle,\r
9172    *         and close the inheritable write handle.\r
9173    */\r
9174 \r
9175   /* Create a pipe for the child's STDIN. */\r
9176   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9177     return GetLastError();\r
9178   }\r
9179 \r
9180   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9181   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9182                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9183                              FALSE,     /* not inherited */\r
9184                              DUPLICATE_SAME_ACCESS);\r
9185   if (! fSuccess) {\r
9186     return GetLastError();\r
9187   }\r
9188   CloseHandle(hChildStdinWr);\r
9189 \r
9190   /* Arrange to (1) look in dir for the child .exe file, and\r
9191    * (2) have dir be the child's working directory.  Interpret\r
9192    * dir relative to the directory WinBoard loaded from. */\r
9193   GetCurrentDirectory(MSG_SIZ, buf);\r
9194   SetCurrentDirectory(installDir);\r
9195   SetCurrentDirectory(dir);\r
9196 \r
9197   /* Now create the child process. */\r
9198 \r
9199   siStartInfo.cb = sizeof(STARTUPINFO);\r
9200   siStartInfo.lpReserved = NULL;\r
9201   siStartInfo.lpDesktop = NULL;\r
9202   siStartInfo.lpTitle = NULL;\r
9203   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9204   siStartInfo.cbReserved2 = 0;\r
9205   siStartInfo.lpReserved2 = NULL;\r
9206   siStartInfo.hStdInput = hChildStdinRd;\r
9207   siStartInfo.hStdOutput = hChildStdoutWr;\r
9208   siStartInfo.hStdError = hChildStdoutWr;\r
9209 \r
9210   fSuccess = CreateProcess(NULL,\r
9211                            cmdLine,        /* command line */\r
9212                            NULL,           /* process security attributes */\r
9213                            NULL,           /* primary thread security attrs */\r
9214                            TRUE,           /* handles are inherited */\r
9215                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9216                            NULL,           /* use parent's environment */\r
9217                            NULL,\r
9218                            &siStartInfo, /* STARTUPINFO pointer */\r
9219                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9220 \r
9221   err = GetLastError();\r
9222   SetCurrentDirectory(buf); /* return to prev directory */\r
9223   if (! fSuccess) {\r
9224     return err;\r
9225   }\r
9226 \r
9227   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9228     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9229     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9230   }\r
9231 \r
9232   /* Close the handles we don't need in the parent */\r
9233   CloseHandle(piProcInfo.hThread);\r
9234   CloseHandle(hChildStdinRd);\r
9235   CloseHandle(hChildStdoutWr);\r
9236 \r
9237   /* Prepare return value */\r
9238   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9239   cp->kind = CPReal;\r
9240   cp->hProcess = piProcInfo.hProcess;\r
9241   cp->pid = piProcInfo.dwProcessId;\r
9242   cp->hFrom = hChildStdoutRdDup;\r
9243   cp->hTo = hChildStdinWrDup;\r
9244 \r
9245   *pr = (void *) cp;\r
9246 \r
9247   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9248      2000 where engines sometimes don't see the initial command(s)\r
9249      from WinBoard and hang.  I don't understand how that can happen,\r
9250      but the Sleep is harmless, so I've put it in.  Others have also\r
9251      reported what may be the same problem, so hopefully this will fix\r
9252      it for them too.  */\r
9253   Sleep(500);\r
9254 \r
9255   return NO_ERROR;\r
9256 }\r
9257 \r
9258 \r
9259 void\r
9260 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9261 {\r
9262   ChildProc *cp; int result;\r
9263 \r
9264   cp = (ChildProc *) pr;\r
9265   if (cp == NULL) return;\r
9266 \r
9267   switch (cp->kind) {\r
9268   case CPReal:\r
9269     /* TerminateProcess is considered harmful, so... */\r
9270     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9271     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9272     /* The following doesn't work because the chess program\r
9273        doesn't "have the same console" as WinBoard.  Maybe\r
9274        we could arrange for this even though neither WinBoard\r
9275        nor the chess program uses a console for stdio? */\r
9276     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9277 \r
9278     /* [AS] Special termination modes for misbehaving programs... */\r
9279     if( signal & 8 ) { \r
9280         result = TerminateProcess( cp->hProcess, 0 );\r
9281 \r
9282         if ( appData.debugMode) {\r
9283             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9284         }\r
9285     }\r
9286     else if( signal & 4 ) {\r
9287         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9288 \r
9289         if( dw != WAIT_OBJECT_0 ) {\r
9290             result = TerminateProcess( cp->hProcess, 0 );\r
9291 \r
9292             if ( appData.debugMode) {\r
9293                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9294             }\r
9295 \r
9296         }\r
9297     }\r
9298 \r
9299     CloseHandle(cp->hProcess);\r
9300     break;\r
9301 \r
9302   case CPComm:\r
9303     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9304     break;\r
9305 \r
9306   case CPSock:\r
9307     closesocket(cp->sock);\r
9308     WSACleanup();\r
9309     break;\r
9310 \r
9311   case CPRcmd:\r
9312     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9313     closesocket(cp->sock);\r
9314     closesocket(cp->sock2);\r
9315     WSACleanup();\r
9316     break;\r
9317   }\r
9318   free(cp);\r
9319 }\r
9320 \r
9321 void\r
9322 InterruptChildProcess(ProcRef pr)\r
9323 {\r
9324   ChildProc *cp;\r
9325 \r
9326   cp = (ChildProc *) pr;\r
9327   if (cp == NULL) return;\r
9328   switch (cp->kind) {\r
9329   case CPReal:\r
9330     /* The following doesn't work because the chess program\r
9331        doesn't "have the same console" as WinBoard.  Maybe\r
9332        we could arrange for this even though neither WinBoard\r
9333        nor the chess program uses a console for stdio */\r
9334     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9335     break;\r
9336 \r
9337   case CPComm:\r
9338   case CPSock:\r
9339     /* Can't interrupt */\r
9340     break;\r
9341 \r
9342   case CPRcmd:\r
9343     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9344     break;\r
9345   }\r
9346 }\r
9347 \r
9348 \r
9349 int\r
9350 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9351 {\r
9352   char cmdLine[MSG_SIZ];\r
9353 \r
9354   if (port[0] == NULLCHAR) {\r
9355     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9356   } else {\r
9357     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9358   }\r
9359   return StartChildProcess(cmdLine, "", pr);\r
9360 }\r
9361 \r
9362 \r
9363 /* Code to open TCP sockets */\r
9364 \r
9365 int\r
9366 OpenTCP(char *host, char *port, ProcRef *pr)\r
9367 {\r
9368   ChildProc *cp;\r
9369   int err;\r
9370   SOCKET s;\r
9371 \r
9372   struct sockaddr_in sa, mysa;\r
9373   struct hostent FAR *hp;\r
9374   unsigned short uport;\r
9375   WORD wVersionRequested;\r
9376   WSADATA wsaData;\r
9377 \r
9378   /* Initialize socket DLL */\r
9379   wVersionRequested = MAKEWORD(1, 1);\r
9380   err = WSAStartup(wVersionRequested, &wsaData);\r
9381   if (err != 0) return err;\r
9382 \r
9383   /* Make socket */\r
9384   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9385     err = WSAGetLastError();\r
9386     WSACleanup();\r
9387     return err;\r
9388   }\r
9389 \r
9390   /* Bind local address using (mostly) don't-care values.\r
9391    */\r
9392   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9393   mysa.sin_family = AF_INET;\r
9394   mysa.sin_addr.s_addr = INADDR_ANY;\r
9395   uport = (unsigned short) 0;\r
9396   mysa.sin_port = htons(uport);\r
9397   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9398       == SOCKET_ERROR) {\r
9399     err = WSAGetLastError();\r
9400     WSACleanup();\r
9401     return err;\r
9402   }\r
9403 \r
9404   /* Resolve remote host name */\r
9405   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9406   if (!(hp = gethostbyname(host))) {\r
9407     unsigned int b0, b1, b2, b3;\r
9408 \r
9409     err = WSAGetLastError();\r
9410 \r
9411     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9412       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9413       hp->h_addrtype = AF_INET;\r
9414       hp->h_length = 4;\r
9415       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9416       hp->h_addr_list[0] = (char *) malloc(4);\r
9417       hp->h_addr_list[0][0] = (char) b0;\r
9418       hp->h_addr_list[0][1] = (char) b1;\r
9419       hp->h_addr_list[0][2] = (char) b2;\r
9420       hp->h_addr_list[0][3] = (char) b3;\r
9421     } else {\r
9422       WSACleanup();\r
9423       return err;\r
9424     }\r
9425   }\r
9426   sa.sin_family = hp->h_addrtype;\r
9427   uport = (unsigned short) atoi(port);\r
9428   sa.sin_port = htons(uport);\r
9429   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9430 \r
9431   /* Make connection */\r
9432   if (connect(s, (struct sockaddr *) &sa,\r
9433               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9434     err = WSAGetLastError();\r
9435     WSACleanup();\r
9436     return err;\r
9437   }\r
9438 \r
9439   /* Prepare return value */\r
9440   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9441   cp->kind = CPSock;\r
9442   cp->sock = s;\r
9443   *pr = (ProcRef *) cp;\r
9444 \r
9445   return NO_ERROR;\r
9446 }\r
9447 \r
9448 int\r
9449 OpenCommPort(char *name, ProcRef *pr)\r
9450 {\r
9451   HANDLE h;\r
9452   COMMTIMEOUTS ct;\r
9453   ChildProc *cp;\r
9454   char fullname[MSG_SIZ];\r
9455 \r
9456   if (*name != '\\')\r
9457     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9458   else\r
9459     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9460 \r
9461   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9462                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9463   if (h == (HANDLE) -1) {\r
9464     return GetLastError();\r
9465   }\r
9466   hCommPort = h;\r
9467 \r
9468   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9469 \r
9470   /* Accumulate characters until a 100ms pause, then parse */\r
9471   ct.ReadIntervalTimeout = 100;\r
9472   ct.ReadTotalTimeoutMultiplier = 0;\r
9473   ct.ReadTotalTimeoutConstant = 0;\r
9474   ct.WriteTotalTimeoutMultiplier = 0;\r
9475   ct.WriteTotalTimeoutConstant = 0;\r
9476   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9477 \r
9478   /* Prepare return value */\r
9479   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9480   cp->kind = CPComm;\r
9481   cp->hFrom = h;\r
9482   cp->hTo = h;\r
9483   *pr = (ProcRef *) cp;\r
9484 \r
9485   return NO_ERROR;\r
9486 }\r
9487 \r
9488 int\r
9489 OpenLoopback(ProcRef *pr)\r
9490 {\r
9491   DisplayFatalError(_("Not implemented"), 0, 1);\r
9492   return NO_ERROR;\r
9493 }\r
9494 \r
9495 \r
9496 int\r
9497 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9498 {\r
9499   ChildProc *cp;\r
9500   int err;\r
9501   SOCKET s, s2, s3;\r
9502   struct sockaddr_in sa, mysa;\r
9503   struct hostent FAR *hp;\r
9504   unsigned short uport;\r
9505   WORD wVersionRequested;\r
9506   WSADATA wsaData;\r
9507   int fromPort;\r
9508   char stderrPortStr[MSG_SIZ];\r
9509 \r
9510   /* Initialize socket DLL */\r
9511   wVersionRequested = MAKEWORD(1, 1);\r
9512   err = WSAStartup(wVersionRequested, &wsaData);\r
9513   if (err != 0) return err;\r
9514 \r
9515   /* Resolve remote host name */\r
9516   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9517   if (!(hp = gethostbyname(host))) {\r
9518     unsigned int b0, b1, b2, b3;\r
9519 \r
9520     err = WSAGetLastError();\r
9521 \r
9522     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9523       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9524       hp->h_addrtype = AF_INET;\r
9525       hp->h_length = 4;\r
9526       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9527       hp->h_addr_list[0] = (char *) malloc(4);\r
9528       hp->h_addr_list[0][0] = (char) b0;\r
9529       hp->h_addr_list[0][1] = (char) b1;\r
9530       hp->h_addr_list[0][2] = (char) b2;\r
9531       hp->h_addr_list[0][3] = (char) b3;\r
9532     } else {\r
9533       WSACleanup();\r
9534       return err;\r
9535     }\r
9536   }\r
9537   sa.sin_family = hp->h_addrtype;\r
9538   uport = (unsigned short) 514;\r
9539   sa.sin_port = htons(uport);\r
9540   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9541 \r
9542   /* Bind local socket to unused "privileged" port address\r
9543    */\r
9544   s = INVALID_SOCKET;\r
9545   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9546   mysa.sin_family = AF_INET;\r
9547   mysa.sin_addr.s_addr = INADDR_ANY;\r
9548   for (fromPort = 1023;; fromPort--) {\r
9549     if (fromPort < 0) {\r
9550       WSACleanup();\r
9551       return WSAEADDRINUSE;\r
9552     }\r
9553     if (s == INVALID_SOCKET) {\r
9554       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9555         err = WSAGetLastError();\r
9556         WSACleanup();\r
9557         return err;\r
9558       }\r
9559     }\r
9560     uport = (unsigned short) fromPort;\r
9561     mysa.sin_port = htons(uport);\r
9562     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9563         == SOCKET_ERROR) {\r
9564       err = WSAGetLastError();\r
9565       if (err == WSAEADDRINUSE) continue;\r
9566       WSACleanup();\r
9567       return err;\r
9568     }\r
9569     if (connect(s, (struct sockaddr *) &sa,\r
9570       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9571       err = WSAGetLastError();\r
9572       if (err == WSAEADDRINUSE) {\r
9573         closesocket(s);\r
9574         s = -1;\r
9575         continue;\r
9576       }\r
9577       WSACleanup();\r
9578       return err;\r
9579     }\r
9580     break;\r
9581   }\r
9582 \r
9583   /* Bind stderr local socket to unused "privileged" port address\r
9584    */\r
9585   s2 = 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 == prevStderrPort) continue; // don't reuse port\r
9591     if (fromPort < 0) {\r
9592       (void) closesocket(s);\r
9593       WSACleanup();\r
9594       return WSAEADDRINUSE;\r
9595     }\r
9596     if (s2 == INVALID_SOCKET) {\r
9597       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9598         err = WSAGetLastError();\r
9599         closesocket(s);\r
9600         WSACleanup();\r
9601         return err;\r
9602       }\r
9603     }\r
9604     uport = (unsigned short) fromPort;\r
9605     mysa.sin_port = htons(uport);\r
9606     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9607         == SOCKET_ERROR) {\r
9608       err = WSAGetLastError();\r
9609       if (err == WSAEADDRINUSE) continue;\r
9610       (void) closesocket(s);\r
9611       WSACleanup();\r
9612       return err;\r
9613     }\r
9614     if (listen(s2, 1) == SOCKET_ERROR) {\r
9615       err = WSAGetLastError();\r
9616       if (err == WSAEADDRINUSE) {\r
9617         closesocket(s2);\r
9618         s2 = INVALID_SOCKET;\r
9619         continue;\r
9620       }\r
9621       (void) closesocket(s);\r
9622       (void) closesocket(s2);\r
9623       WSACleanup();\r
9624       return err;\r
9625     }\r
9626     break;\r
9627   }\r
9628   prevStderrPort = fromPort; // remember port used\r
9629   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9630 \r
9631   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9632     err = WSAGetLastError();\r
9633     (void) closesocket(s);\r
9634     (void) closesocket(s2);\r
9635     WSACleanup();\r
9636     return err;\r
9637   }\r
9638 \r
9639   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9640     err = WSAGetLastError();\r
9641     (void) closesocket(s);\r
9642     (void) closesocket(s2);\r
9643     WSACleanup();\r
9644     return err;\r
9645   }\r
9646   if (*user == NULLCHAR) user = UserName();\r
9647   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9648     err = WSAGetLastError();\r
9649     (void) closesocket(s);\r
9650     (void) closesocket(s2);\r
9651     WSACleanup();\r
9652     return err;\r
9653   }\r
9654   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9655     err = WSAGetLastError();\r
9656     (void) closesocket(s);\r
9657     (void) closesocket(s2);\r
9658     WSACleanup();\r
9659     return err;\r
9660   }\r
9661 \r
9662   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9663     err = WSAGetLastError();\r
9664     (void) closesocket(s);\r
9665     (void) closesocket(s2);\r
9666     WSACleanup();\r
9667     return err;\r
9668   }\r
9669   (void) closesocket(s2);  /* Stop listening */\r
9670 \r
9671   /* Prepare return value */\r
9672   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9673   cp->kind = CPRcmd;\r
9674   cp->sock = s;\r
9675   cp->sock2 = s3;\r
9676   *pr = (ProcRef *) cp;\r
9677 \r
9678   return NO_ERROR;\r
9679 }\r
9680 \r
9681 \r
9682 InputSourceRef\r
9683 AddInputSource(ProcRef pr, int lineByLine,\r
9684                InputCallback func, VOIDSTAR closure)\r
9685 {\r
9686   InputSource *is, *is2 = NULL;\r
9687   ChildProc *cp = (ChildProc *) pr;\r
9688 \r
9689   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9690   is->lineByLine = lineByLine;\r
9691   is->func = func;\r
9692   is->closure = closure;\r
9693   is->second = NULL;\r
9694   is->next = is->buf;\r
9695   if (pr == NoProc) {\r
9696     is->kind = CPReal;\r
9697     consoleInputSource = is;\r
9698   } else {\r
9699     is->kind = cp->kind;\r
9700     /* \r
9701         [AS] Try to avoid a race condition if the thread is given control too early:\r
9702         we create all threads suspended so that the is->hThread variable can be\r
9703         safely assigned, then let the threads start with ResumeThread.\r
9704     */\r
9705     switch (cp->kind) {\r
9706     case CPReal:\r
9707       is->hFile = cp->hFrom;\r
9708       cp->hFrom = NULL; /* now owned by InputThread */\r
9709       is->hThread =\r
9710         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9711                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9712       break;\r
9713 \r
9714     case CPComm:\r
9715       is->hFile = cp->hFrom;\r
9716       cp->hFrom = NULL; /* now owned by InputThread */\r
9717       is->hThread =\r
9718         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9719                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9720       break;\r
9721 \r
9722     case CPSock:\r
9723       is->sock = cp->sock;\r
9724       is->hThread =\r
9725         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9726                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9727       break;\r
9728 \r
9729     case CPRcmd:\r
9730       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9731       *is2 = *is;\r
9732       is->sock = cp->sock;\r
9733       is->second = is2;\r
9734       is2->sock = cp->sock2;\r
9735       is2->second = is2;\r
9736       is->hThread =\r
9737         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9738                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9739       is2->hThread =\r
9740         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9741                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9742       break;\r
9743     }\r
9744 \r
9745     if( is->hThread != NULL ) {\r
9746         ResumeThread( is->hThread );\r
9747     }\r
9748 \r
9749     if( is2 != NULL && is2->hThread != NULL ) {\r
9750         ResumeThread( is2->hThread );\r
9751     }\r
9752   }\r
9753 \r
9754   return (InputSourceRef) is;\r
9755 }\r
9756 \r
9757 void\r
9758 RemoveInputSource(InputSourceRef isr)\r
9759 {\r
9760   InputSource *is;\r
9761 \r
9762   is = (InputSource *) isr;\r
9763   is->hThread = NULL;  /* tell thread to stop */\r
9764   CloseHandle(is->hThread);\r
9765   if (is->second != NULL) {\r
9766     is->second->hThread = NULL;\r
9767     CloseHandle(is->second->hThread);\r
9768   }\r
9769 }\r
9770 \r
9771 int no_wrap(char *message, int count)\r
9772 {\r
9773     ConsoleOutput(message, count, FALSE);\r
9774     return count;\r
9775 }\r
9776 \r
9777 int\r
9778 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9779 {\r
9780   DWORD dOutCount;\r
9781   int outCount = SOCKET_ERROR;\r
9782   ChildProc *cp = (ChildProc *) pr;\r
9783   static OVERLAPPED ovl;\r
9784 \r
9785   static int line = 0;\r
9786 \r
9787   if (pr == NoProc)\r
9788   {\r
9789     if (appData.noJoin || !appData.useInternalWrap)\r
9790       return no_wrap(message, count);\r
9791     else\r
9792     {\r
9793       int width = get_term_width();\r
9794       int len = wrap(NULL, message, count, width, &line);\r
9795       char *msg = malloc(len);\r
9796       int dbgchk;\r
9797 \r
9798       if (!msg)\r
9799         return no_wrap(message, count);\r
9800       else\r
9801       {\r
9802         dbgchk = wrap(msg, message, count, width, &line);\r
9803         if (dbgchk != len && appData.debugMode)\r
9804             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9805         ConsoleOutput(msg, len, FALSE);\r
9806         free(msg);\r
9807         return len;\r
9808       }\r
9809     }\r
9810   }\r
9811 \r
9812   if (ovl.hEvent == NULL) {\r
9813     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9814   }\r
9815   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9816 \r
9817   switch (cp->kind) {\r
9818   case CPSock:\r
9819   case CPRcmd:\r
9820     outCount = send(cp->sock, message, count, 0);\r
9821     if (outCount == SOCKET_ERROR) {\r
9822       *outError = WSAGetLastError();\r
9823     } else {\r
9824       *outError = NO_ERROR;\r
9825     }\r
9826     break;\r
9827 \r
9828   case CPReal:\r
9829     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9830                   &dOutCount, NULL)) {\r
9831       *outError = NO_ERROR;\r
9832       outCount = (int) dOutCount;\r
9833     } else {\r
9834       *outError = GetLastError();\r
9835     }\r
9836     break;\r
9837 \r
9838   case CPComm:\r
9839     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9840                             &dOutCount, &ovl);\r
9841     if (*outError == NO_ERROR) {\r
9842       outCount = (int) dOutCount;\r
9843     }\r
9844     break;\r
9845   }\r
9846   return outCount;\r
9847 }\r
9848 \r
9849 void\r
9850 DoSleep(int n)\r
9851 {\r
9852     if(n != 0) Sleep(n);\r
9853 }\r
9854 \r
9855 int\r
9856 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9857                        long msdelay)\r
9858 {\r
9859   /* Ignore delay, not implemented for WinBoard */\r
9860   return OutputToProcess(pr, message, count, outError);\r
9861 }\r
9862 \r
9863 \r
9864 void\r
9865 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9866                         char *buf, int count, int error)\r
9867 {\r
9868   DisplayFatalError(_("Not implemented"), 0, 1);\r
9869 }\r
9870 \r
9871 /* see wgamelist.c for Game List functions */\r
9872 /* see wedittags.c for Edit Tags functions */\r
9873 \r
9874 \r
9875 int\r
9876 ICSInitScript()\r
9877 {\r
9878   FILE *f;\r
9879   char buf[MSG_SIZ];\r
9880   char *dummy;\r
9881 \r
9882   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9883     f = fopen(buf, "r");\r
9884     if (f != NULL) {\r
9885       ProcessICSInitScript(f);\r
9886       fclose(f);\r
9887       return TRUE;\r
9888     }\r
9889   }\r
9890   return FALSE;\r
9891 }\r
9892 \r
9893 \r
9894 VOID\r
9895 StartAnalysisClock()\r
9896 {\r
9897   if (analysisTimerEvent) return;\r
9898   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9899                                         (UINT) 2000, NULL);\r
9900 }\r
9901 \r
9902 VOID\r
9903 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9904 {\r
9905   highlightInfo.sq[0].x = fromX;\r
9906   highlightInfo.sq[0].y = fromY;\r
9907   highlightInfo.sq[1].x = toX;\r
9908   highlightInfo.sq[1].y = toY;\r
9909 }\r
9910 \r
9911 VOID\r
9912 ClearHighlights()\r
9913 {\r
9914   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9915     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9916 }\r
9917 \r
9918 VOID\r
9919 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9920 {\r
9921   premoveHighlightInfo.sq[0].x = fromX;\r
9922   premoveHighlightInfo.sq[0].y = fromY;\r
9923   premoveHighlightInfo.sq[1].x = toX;\r
9924   premoveHighlightInfo.sq[1].y = toY;\r
9925 }\r
9926 \r
9927 VOID\r
9928 ClearPremoveHighlights()\r
9929 {\r
9930   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9931     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9932 }\r
9933 \r
9934 VOID\r
9935 ShutDownFrontEnd()\r
9936 {\r
9937   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9938   DeleteClipboardTempFiles();\r
9939 }\r
9940 \r
9941 void\r
9942 BoardToTop()\r
9943 {\r
9944     if (IsIconic(hwndMain))\r
9945       ShowWindow(hwndMain, SW_RESTORE);\r
9946 \r
9947     SetActiveWindow(hwndMain);\r
9948 }\r
9949 \r
9950 /*\r
9951  * Prototypes for animation support routines\r
9952  */\r
9953 static void ScreenSquare(int column, int row, POINT * pt);\r
9954 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9955      POINT frames[], int * nFrames);\r
9956 \r
9957 \r
9958 #define kFactor 4\r
9959 \r
9960 void\r
9961 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9962 {       // [HGM] atomic: animate blast wave\r
9963         int i;\r
9964 \r
9965         explodeInfo.fromX = fromX;\r
9966         explodeInfo.fromY = fromY;\r
9967         explodeInfo.toX = toX;\r
9968         explodeInfo.toY = toY;\r
9969         for(i=1; i<4*kFactor; i++) {\r
9970             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9971             DrawPosition(FALSE, board);\r
9972             Sleep(appData.animSpeed);\r
9973         }\r
9974         explodeInfo.radius = 0;\r
9975         DrawPosition(TRUE, board);\r
9976 }\r
9977 \r
9978 void\r
9979 AnimateMove(board, fromX, fromY, toX, toY)\r
9980      Board board;\r
9981      int fromX;\r
9982      int fromY;\r
9983      int toX;\r
9984      int toY;\r
9985 {\r
9986   ChessSquare piece;\r
9987   int x = toX, y = toY;\r
9988   POINT start, finish, mid;\r
9989   POINT frames[kFactor * 2 + 1];\r
9990   int nFrames, n;\r
9991 \r
9992   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9993 \r
9994   if (!appData.animate) return;\r
9995   if (doingSizing) return;\r
9996   if (fromY < 0 || fromX < 0) return;\r
9997   piece = board[fromY][fromX];\r
9998   if (piece >= EmptySquare) return;\r
9999 \r
10000   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
10001 \r
10002 again:\r
10003 \r
10004   ScreenSquare(fromX, fromY, &start);\r
10005   ScreenSquare(toX, toY, &finish);\r
10006 \r
10007   /* All moves except knight jumps move in straight line */\r
10008   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10009     mid.x = start.x + (finish.x - start.x) / 2;\r
10010     mid.y = start.y + (finish.y - start.y) / 2;\r
10011   } else {\r
10012     /* Knight: make straight movement then diagonal */\r
10013     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10014        mid.x = start.x + (finish.x - start.x) / 2;\r
10015        mid.y = start.y;\r
10016      } else {\r
10017        mid.x = start.x;\r
10018        mid.y = start.y + (finish.y - start.y) / 2;\r
10019      }\r
10020   }\r
10021   \r
10022   /* Don't use as many frames for very short moves */\r
10023   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10024     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10025   else\r
10026     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10027 \r
10028   animInfo.from.x = fromX;\r
10029   animInfo.from.y = fromY;\r
10030   animInfo.to.x = toX;\r
10031   animInfo.to.y = toY;\r
10032   animInfo.lastpos = start;\r
10033   animInfo.piece = piece;\r
10034   for (n = 0; n < nFrames; n++) {\r
10035     animInfo.pos = frames[n];\r
10036     DrawPosition(FALSE, NULL);\r
10037     animInfo.lastpos = animInfo.pos;\r
10038     Sleep(appData.animSpeed);\r
10039   }\r
10040   animInfo.pos = finish;\r
10041   DrawPosition(FALSE, NULL);\r
10042 \r
10043   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10044 \r
10045   animInfo.piece = EmptySquare;\r
10046   Explode(board, fromX, fromY, toX, toY);\r
10047 }\r
10048 \r
10049 /*      Convert board position to corner of screen rect and color       */\r
10050 \r
10051 static void\r
10052 ScreenSquare(column, row, pt)\r
10053      int column; int row; POINT * pt;\r
10054 {\r
10055   if (flipView) {\r
10056     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10057     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10058   } else {\r
10059     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10060     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10061   }\r
10062 }\r
10063 \r
10064 /*      Generate a series of frame coords from start->mid->finish.\r
10065         The movement rate doubles until the half way point is\r
10066         reached, then halves back down to the final destination,\r
10067         which gives a nice slow in/out effect. The algorithmn\r
10068         may seem to generate too many intermediates for short\r
10069         moves, but remember that the purpose is to attract the\r
10070         viewers attention to the piece about to be moved and\r
10071         then to where it ends up. Too few frames would be less\r
10072         noticeable.                                             */\r
10073 \r
10074 static void\r
10075 Tween(start, mid, finish, factor, frames, nFrames)\r
10076      POINT * start; POINT * mid;\r
10077      POINT * finish; int factor;\r
10078      POINT frames[]; int * nFrames;\r
10079 {\r
10080   int n, fraction = 1, count = 0;\r
10081 \r
10082   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10083   for (n = 0; n < factor; n++)\r
10084     fraction *= 2;\r
10085   for (n = 0; n < factor; n++) {\r
10086     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10087     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10088     count ++;\r
10089     fraction = fraction / 2;\r
10090   }\r
10091   \r
10092   /* Midpoint */\r
10093   frames[count] = *mid;\r
10094   count ++;\r
10095   \r
10096   /* Slow out, stepping 1/2, then 1/4, ... */\r
10097   fraction = 2;\r
10098   for (n = 0; n < factor; n++) {\r
10099     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10100     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10101     count ++;\r
10102     fraction = fraction * 2;\r
10103   }\r
10104   *nFrames = count;\r
10105 }\r
10106 \r
10107 void\r
10108 SettingsPopUp(ChessProgramState *cps)\r
10109 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10110       EngineOptionsPopup(savedHwnd, cps);\r
10111 }\r
10112 \r
10113 int flock(int fid, int code)\r
10114 {\r
10115     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10116     OVERLAPPED ov;\r
10117     ov.hEvent = NULL;\r
10118     ov.Offset = 0;\r
10119     ov.OffsetHigh = 0;\r
10120     switch(code) {\r
10121       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10122 \r
10123       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10124       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10125       default: return -1;\r
10126     }\r
10127     return 0;\r
10128 }\r
10129 \r
10130 char *\r
10131 Col2Text (int n)\r
10132 {\r
10133     static int i=0;\r
10134     static char col[8][20];\r
10135     COLORREF color = *(COLORREF *) colorVariable[n];\r
10136     i = i+1 & 7;\r
10137     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10138     return col[i];\r
10139 }\r
10140 \r
10141 void\r
10142 ActivateTheme (int new)\r
10143 {   // Redo initialization of features depending on options that can occur in themes\r
10144    InitTextures();\r
10145    if(new) InitDrawingColors();\r
10146    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10147    InitDrawingSizes(boardSize, 0);\r
10148    InvalidateRect(hwndMain, NULL, TRUE);\r
10149 }\r