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