Round board size to one where piece images available (WB)
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012 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 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
96 \r
97   int myrandom(void);\r
98   void mysrandom(unsigned int seed);\r
99 \r
100 extern int whiteFlag, blackFlag;\r
101 Boolean flipClock = FALSE;\r
102 extern HANDLE chatHandle[];\r
103 extern enum ICS_TYPE ics_type;\r
104 \r
105 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
106 int  MyGetFullPathName P((char *name, char *fullname));\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
108 VOID NewVariantPopup(HWND hwnd);\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
110                    /*char*/int promoChar));\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P((char *s));\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129   ChessSquare piece;\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
142 \r
143 typedef struct { // [HGM] atomic\r
144   int fromX, fromY, toX, toY, radius;\r
145 } ExplodeInfo;\r
146 \r
147 static ExplodeInfo explodeInfo;\r
148 \r
149 /* Window class names */\r
150 char szAppName[] = "WinBoard";\r
151 char szConsoleName[] = "WBConsole";\r
152 \r
153 /* Title bar text */\r
154 char szTitle[] = "WinBoard";\r
155 char szConsoleTitle[] = "I C S Interaction";\r
156 \r
157 char *programName;\r
158 char *settingsFileName;\r
159 Boolean saveSettingsOnExit;\r
160 char installDir[MSG_SIZ];\r
161 int errorExitStatus;\r
162 \r
163 BoardSize boardSize;\r
164 Boolean chessProgram;\r
165 //static int boardX, boardY;\r
166 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
167 int squareSize, lineGap, minorSize;\r
168 static int winW, winH;\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
170 static int logoHeight = 0;\r
171 static char messageText[MESSAGE_TEXT_MAX];\r
172 static int clockTimerEvent = 0;\r
173 static int loadGameTimerEvent = 0;\r
174 static int analysisTimerEvent = 0;\r
175 static DelayedEventCallback delayedTimerCallback;\r
176 static int delayedTimerEvent = 0;\r
177 static int buttonCount = 2;\r
178 char *icsTextMenuString;\r
179 char *icsNames;\r
180 char *firstChessProgramNames;\r
181 char *secondChessProgramNames;\r
182 \r
183 #define PALETTESIZE 256\r
184 \r
185 HINSTANCE hInst;          /* current instance */\r
186 Boolean alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 static HWND savedHwnd;\r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   markerBrush,      /* [HGM] markers */\r
201   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
204 static HPEN gridPen = NULL;\r
205 static HPEN highlightPen = NULL;\r
206 static HPEN premovePen = NULL;\r
207 static NPLOGPALETTE pLogPal;\r
208 static BOOL paletteChanged = FALSE;\r
209 static HICON iconWhite, iconBlack, iconCurrent;\r
210 static int doingSizing = FALSE;\r
211 static int lastSizing = 0;\r
212 static int prevStderrPort;\r
213 static HBITMAP userLogo;\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #if defined(_winmajor)\r
226 #define oldDialog (_winmajor < 4)\r
227 #else\r
228 #define oldDialog 0\r
229 #endif\r
230 #endif\r
231 \r
232 #define INTERNATIONAL\r
233 \r
234 #ifdef INTERNATIONAL\r
235 #  define _(s) T_(s)\r
236 #  define N_(s) s\r
237 #else\r
238 #  define _(s) s\r
239 #  define N_(s) s\r
240 #  define T_(s) s\r
241 #  define Translate(x, y)\r
242 #  define LoadLanguageFile(s)\r
243 #endif\r
244 \r
245 #ifdef INTERNATIONAL\r
246 \r
247 Boolean barbaric; // flag indicating if translation is needed\r
248 \r
249 // list of item numbers used in each dialog (used to alter language at run time)\r
250 \r
251 #define ABOUTBOX -1  /* not sure why these are needed */\r
252 #define ABOUTBOX2 -1\r
253 \r
254 int dialogItems[][42] = {\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
257   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
259   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
261   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
264   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
267   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
268 { ABOUTBOX2, IDC_ChessBoard }, \r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
270   OPT_GameListClose, IDC_GameListDoFilter }, \r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
272 { DLG_Error, IDOK }, \r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
274   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
277   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
278   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
279 { DLG_IndexNumber, IDC_Index }, \r
280 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
281 { DLG_TypeInName, IDOK, IDCANCEL }, \r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
283   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
285   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
286   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
287   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
288   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
289   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
290   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
292   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
293   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
294   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
295   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
296   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
297   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
298   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
299   GPB_General, GPB_Alarm }, \r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
301   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
302   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
303   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
304   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
305   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
306   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
307   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
309   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
310   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
311   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
312   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
313   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
314   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
315   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
316   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
318   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
319   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
320   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
321   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
324   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
325 { DLG_MoveHistory }, \r
326 { DLG_EvalGraph }, \r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
330   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
331   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
332   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
334   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
335   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
336 { 0 }\r
337 };\r
338 \r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
340 static int lastChecked;\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
342 extern int tinyLayout;\r
343 extern char * menuBarText[][10];\r
344 \r
345 void\r
346 LoadLanguageFile(char *name)\r
347 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
348     FILE *f;\r
349     int i=0, j=0, n=0, k;\r
350     char buf[MSG_SIZ];\r
351 \r
352     if(!name || name[0] == NULLCHAR) return;\r
353       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
354     appData.language = oldLanguage;\r
355     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
356     if((f = fopen(buf, "r")) == NULL) return;\r
357     while((k = fgetc(f)) != EOF) {\r
358         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
359         languageBuf[i] = k;\r
360         if(k == '\n') {\r
361             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
362                 char *p;\r
363                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
364                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
365                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
366                         english[j] = languageBuf + n + 1; *p = 0;\r
367                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
369                     }\r
370                 }\r
371             }\r
372             n = i + 1;\r
373         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
374             switch(k) {\r
375               case 'n': k = '\n'; break;\r
376               case 'r': k = '\r'; break;\r
377               case 't': k = '\t'; break;\r
378             }\r
379             languageBuf[--i] = k;\r
380         }\r
381         i++;\r
382     }\r
383     fclose(f);\r
384     barbaric = (j != 0);\r
385     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
386 }\r
387 \r
388 char *\r
389 T_(char *s)\r
390 {   // return the translation of the given string\r
391     // efficiency can be improved a lot...\r
392     int i=0;\r
393     static char buf[MSG_SIZ];\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
395     if(!barbaric) return s;\r
396     if(!s) return ""; // sanity\r
397     while(english[i]) {\r
398         if(!strcmp(s, english[i])) return foreign[i];\r
399         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
400             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
401             return buf;\r
402         }\r
403         i++;\r
404     }\r
405     return s;\r
406 }\r
407 \r
408 void\r
409 Translate(HWND hDlg, int dialogID)\r
410 {   // translate all text items in the given dialog\r
411     int i=0, j, k;\r
412     char buf[MSG_SIZ], *s;\r
413     if(!barbaric) return;\r
414     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
415     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
416     GetWindowText( hDlg, buf, MSG_SIZ );\r
417     s = T_(buf);\r
418     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
419     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
420         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
421         if(strlen(buf) == 0) continue;\r
422         s = T_(buf);\r
423         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
424     }\r
425 }\r
426 \r
427 HMENU\r
428 TranslateOneMenu(int i, HMENU subMenu)\r
429 {\r
430     int j;\r
431     static MENUITEMINFO info;\r
432 \r
433     info.cbSize = sizeof(MENUITEMINFO);\r
434     info.fMask = MIIM_STATE | MIIM_TYPE;\r
435           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
436             char buf[MSG_SIZ];\r
437             info.dwTypeData = buf;\r
438             info.cch = sizeof(buf);\r
439             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
440             if(i < 10) {\r
441                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
442                 else menuText[i][j] = strdup(buf); // remember original on first change\r
443             }\r
444             if(buf[0] == NULLCHAR) continue;\r
445             info.dwTypeData = T_(buf);\r
446             info.cch = strlen(buf)+1;\r
447             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
448           }\r
449     return subMenu;\r
450 }\r
451 \r
452 void\r
453 TranslateMenus(int addLanguage)\r
454 {\r
455     int i;\r
456     WIN32_FIND_DATA fileData;\r
457     HANDLE hFind;\r
458 #define IDM_English 1970\r
459     if(1) {\r
460         HMENU mainMenu = GetMenu(hwndMain);\r
461         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
462           HMENU subMenu = GetSubMenu(mainMenu, i);\r
463           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
464                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
465           TranslateOneMenu(i, subMenu);\r
466         }\r
467         DrawMenuBar(hwndMain);\r
468     }\r
469 \r
470     if(!addLanguage) return;\r
471     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
472         HMENU mainMenu = GetMenu(hwndMain);\r
473         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
474         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
475         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
476         i = 0; lastChecked = IDM_English;\r
477         do {\r
478             char *p, *q = fileData.cFileName;\r
479             int checkFlag = MF_UNCHECKED;\r
480             languageFile[i] = strdup(q);\r
481             if(barbaric && !strcmp(oldLanguage, q)) {\r
482                 checkFlag = MF_CHECKED;\r
483                 lastChecked = IDM_English + i + 1;\r
484                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
485             }\r
486             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
487             p = strstr(fileData.cFileName, ".lng");\r
488             if(p) *p = 0;\r
489             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
490         } while(FindNextFile(hFind, &fileData));\r
491         FindClose(hFind);\r
492     }\r
493 }\r
494 \r
495 #endif\r
496 \r
497 #define IDM_RecentEngines 3000\r
498 \r
499 void\r
500 RecentEngineMenu (char *s)\r
501 {\r
502     if(appData.icsActive) return;\r
503     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
504         HMENU mainMenu = GetMenu(hwndMain);\r
505         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
506         int i=IDM_RecentEngines;\r
507         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
508         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
509         while(*s) {\r
510           char *p = strchr(s, '\n');\r
511           if(p == NULL) return; // malformed!\r
512           *p = NULLCHAR;\r
513           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
514           *p = '\n';\r
515           s = p+1;\r
516         }\r
517     }\r
518 }\r
519 \r
520 \r
521 typedef struct {\r
522   char *name;\r
523   int squareSize;\r
524   int lineGap;\r
525   int smallLayout;\r
526   int tinyLayout;\r
527   int cliWidth, cliHeight;\r
528 } SizeInfo;\r
529 \r
530 SizeInfo sizeInfo[] = \r
531 {\r
532   { "tiny",     21, 0, 1, 1, 0, 0 },\r
533   { "teeny",    25, 1, 1, 1, 0, 0 },\r
534   { "dinky",    29, 1, 1, 1, 0, 0 },\r
535   { "petite",   33, 1, 1, 1, 0, 0 },\r
536   { "slim",     37, 2, 1, 0, 0, 0 },\r
537   { "small",    40, 2, 1, 0, 0, 0 },\r
538   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
539   { "middling", 49, 2, 0, 0, 0, 0 },\r
540   { "average",  54, 2, 0, 0, 0, 0 },\r
541   { "moderate", 58, 3, 0, 0, 0, 0 },\r
542   { "medium",   64, 3, 0, 0, 0, 0 },\r
543   { "bulky",    72, 3, 0, 0, 0, 0 },\r
544   { "large",    80, 3, 0, 0, 0, 0 },\r
545   { "big",      87, 3, 0, 0, 0, 0 },\r
546   { "huge",     95, 3, 0, 0, 0, 0 },\r
547   { "giant",    108, 3, 0, 0, 0, 0 },\r
548   { "colossal", 116, 4, 0, 0, 0, 0 },\r
549   { "titanic",  129, 4, 0, 0, 0, 0 },\r
550   { NULL, 0, 0, 0, 0, 0, 0 }\r
551 };\r
552 \r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
555 {\r
556   { 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
557   { 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
558   { 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
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574 };\r
575 \r
576 MyFont *font[NUM_SIZES][NUM_FONTS];\r
577 \r
578 typedef struct {\r
579   char *label;\r
580   int id;\r
581   HWND hwnd;\r
582   WNDPROC wndproc;\r
583 } MyButtonDesc;\r
584 \r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
586 #define N_BUTTONS 5\r
587 \r
588 MyButtonDesc buttonDesc[N_BUTTONS] =\r
589 {\r
590   {"<<", IDM_ToStart, NULL, NULL},\r
591   {"<", IDM_Backward, NULL, NULL},\r
592   {"P", IDM_Pause, NULL, NULL},\r
593   {">", IDM_Forward, NULL, NULL},\r
594   {">>", IDM_ToEnd, NULL, NULL},\r
595 };\r
596 \r
597 int tinyLayout = 0, smallLayout = 0;\r
598 #define MENU_BAR_ITEMS 9\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
600   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
601   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
602 };\r
603 \r
604 \r
605 MySound sounds[(int)NSoundClasses];\r
606 MyTextAttribs textAttribs[(int)NColorClasses];\r
607 \r
608 MyColorizeAttribs colorizeAttribs[] = {\r
609   { (COLORREF)0, 0, N_("Shout Text") },\r
610   { (COLORREF)0, 0, N_("SShout/CShout") },\r
611   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
612   { (COLORREF)0, 0, N_("Channel Text") },\r
613   { (COLORREF)0, 0, N_("Kibitz Text") },\r
614   { (COLORREF)0, 0, N_("Tell Text") },\r
615   { (COLORREF)0, 0, N_("Challenge Text") },\r
616   { (COLORREF)0, 0, N_("Request Text") },\r
617   { (COLORREF)0, 0, N_("Seek Text") },\r
618   { (COLORREF)0, 0, N_("Normal Text") },\r
619   { (COLORREF)0, 0, N_("None") }\r
620 };\r
621 \r
622 \r
623 \r
624 static char *commentTitle;\r
625 static char *commentText;\r
626 static int commentIndex;\r
627 static Boolean editComment = FALSE;\r
628 \r
629 \r
630 char errorTitle[MSG_SIZ];\r
631 char errorMessage[2*MSG_SIZ];\r
632 HWND errorDialog = NULL;\r
633 BOOLEAN moveErrorMessageUp = FALSE;\r
634 BOOLEAN consoleEcho = TRUE;\r
635 CHARFORMAT consoleCF;\r
636 COLORREF consoleBackgroundColor;\r
637 \r
638 char *programVersion;\r
639 \r
640 #define CPReal 1\r
641 #define CPComm 2\r
642 #define CPSock 3\r
643 #define CPRcmd 4\r
644 typedef int CPKind;\r
645 \r
646 typedef struct {\r
647   CPKind kind;\r
648   HANDLE hProcess;\r
649   DWORD pid;\r
650   HANDLE hTo;\r
651   HANDLE hFrom;\r
652   SOCKET sock;\r
653   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
654 } ChildProc;\r
655 \r
656 #define INPUT_SOURCE_BUF_SIZE 4096\r
657 \r
658 typedef struct _InputSource {\r
659   CPKind kind;\r
660   HANDLE hFile;\r
661   SOCKET sock;\r
662   int lineByLine;\r
663   HANDLE hThread;\r
664   DWORD id;\r
665   char buf[INPUT_SOURCE_BUF_SIZE];\r
666   char *next;\r
667   DWORD count;\r
668   int error;\r
669   InputCallback func;\r
670   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
671   VOIDSTAR closure;\r
672 } InputSource;\r
673 \r
674 InputSource *consoleInputSource;\r
675 \r
676 DCB dcb;\r
677 \r
678 /* forward */\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
680 VOID ConsoleCreate();\r
681 LRESULT CALLBACK\r
682   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);\r
686 LRESULT CALLBACK\r
687   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
689 void ParseIcsTextMenu(char *icsTextMenuString);\r
690 VOID PopUpNameDialog(char firstchar);\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
692 \r
693 /* [AS] */\r
694 int NewGameFRC();\r
695 int GameListOptions();\r
696 \r
697 int dummy; // [HGM] for obsolete args\r
698 \r
699 HWND hwndMain = NULL;        /* root window*/\r
700 HWND hwndConsole = NULL;\r
701 HWND commentDialog = NULL;\r
702 HWND moveHistoryDialog = NULL;\r
703 HWND evalGraphDialog = NULL;\r
704 HWND engineOutputDialog = NULL;\r
705 HWND gameListDialog = NULL;\r
706 HWND editTagsDialog = NULL;\r
707 \r
708 int commentUp = FALSE;\r
709 \r
710 WindowPlacement wpMain;\r
711 WindowPlacement wpConsole;\r
712 WindowPlacement wpComment;\r
713 WindowPlacement wpMoveHistory;\r
714 WindowPlacement wpEvalGraph;\r
715 WindowPlacement wpEngineOutput;\r
716 WindowPlacement wpGameList;\r
717 WindowPlacement wpTags;\r
718 \r
719 VOID EngineOptionsPopup(); // [HGM] settings\r
720 \r
721 VOID GothicPopUp(char *title, VariantClass variant);\r
722 /*\r
723  * Setting "frozen" should disable all user input other than deleting\r
724  * the window.  We do this while engines are initializing themselves.\r
725  */\r
726 static int frozen = 0;\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];\r
728 void FreezeUI()\r
729 {\r
730   HMENU hmenu;\r
731   int i;\r
732 \r
733   if (frozen) return;\r
734   frozen = 1;\r
735   hmenu = GetMenu(hwndMain);\r
736   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
737     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
738   }\r
739   DrawMenuBar(hwndMain);\r
740 }\r
741 \r
742 /* Undo a FreezeUI */\r
743 void ThawUI()\r
744 {\r
745   HMENU hmenu;\r
746   int i;\r
747 \r
748   if (!frozen) return;\r
749   frozen = 0;\r
750   hmenu = GetMenu(hwndMain);\r
751   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
752     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
753   }\r
754   DrawMenuBar(hwndMain);\r
755 }\r
756 \r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
758 \r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
760 #ifdef JAWS\r
761 #include "jaws.c"\r
762 #else\r
763 #define JAWS_INIT\r
764 #define JAWS_ARGS\r
765 #define JAWS_ALT_INTERCEPT\r
766 #define JAWS_KBUP_NAVIGATION\r
767 #define JAWS_KBDOWN_NAVIGATION\r
768 #define JAWS_MENU_ITEMS\r
769 #define JAWS_SILENCE\r
770 #define JAWS_REPLAY\r
771 #define JAWS_ACCEL\r
772 #define JAWS_COPYRIGHT\r
773 #define JAWS_DELETE(X) X\r
774 #define SAYMACHINEMOVE()\r
775 #define SAY(X)\r
776 #endif\r
777 \r
778 /*---------------------------------------------------------------------------*\\r
779  *\r
780  * WinMain\r
781  *\r
782 \*---------------------------------------------------------------------------*/\r
783 \r
784 int APIENTRY\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
786         LPSTR lpCmdLine, int nCmdShow)\r
787 {\r
788   MSG msg;\r
789   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
790 //  INITCOMMONCONTROLSEX ex;\r
791 \r
792   debugFP = stderr;\r
793 \r
794   LoadLibrary("RICHED32.DLL");\r
795   consoleCF.cbSize = sizeof(CHARFORMAT);\r
796 \r
797   if (!InitApplication(hInstance)) {\r
798     return (FALSE);\r
799   }\r
800   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
801     return (FALSE);\r
802   }\r
803 \r
804   JAWS_INIT\r
805   TranslateMenus(1);\r
806 \r
807 //  InitCommonControlsEx(&ex);\r
808   InitCommonControls();\r
809 \r
810   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
811   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
812   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
813 \r
814   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
815 \r
816   while (GetMessage(&msg, /* message structure */\r
817                     NULL, /* handle of window receiving the message */\r
818                     0,    /* lowest message to examine */\r
819                     0))   /* highest message to examine */\r
820     {\r
821 \r
822       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
823         // [HGM] navigate: switch between all windows with tab\r
824         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
825         int i, currentElement = 0;\r
826 \r
827         // first determine what element of the chain we come from (if any)\r
828         if(appData.icsActive) {\r
829             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
830             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
831         }\r
832         if(engineOutputDialog && EngineOutputIsUp()) {\r
833             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
834             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
835         }\r
836         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
837             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
838         }\r
839         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
840         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
841         if(msg.hwnd == e1)                 currentElement = 2; else\r
842         if(msg.hwnd == e2)                 currentElement = 3; else\r
843         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
844         if(msg.hwnd == mh)                currentElement = 4; else\r
845         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
846         if(msg.hwnd == hText)  currentElement = 5; else\r
847         if(msg.hwnd == hInput) currentElement = 6; else\r
848         for (i = 0; i < N_BUTTONS; i++) {\r
849             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
850         }\r
851 \r
852         // determine where to go to\r
853         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
854           do {\r
855             currentElement = (currentElement + direction) % 7;\r
856             switch(currentElement) {\r
857                 case 0:\r
858                   h = hwndMain; break; // passing this case always makes the loop exit\r
859                 case 1:\r
860                   h = buttonDesc[0].hwnd; break; // could be NULL\r
861                 case 2:\r
862                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
863                   h = e1; break;\r
864                 case 3:\r
865                   if(!EngineOutputIsUp()) continue;\r
866                   h = e2; break;\r
867                 case 4:\r
868                   if(!MoveHistoryIsUp()) continue;\r
869                   h = mh; break;\r
870 //              case 6: // input to eval graph does not seem to get here!\r
871 //                if(!EvalGraphIsUp()) continue;\r
872 //                h = evalGraphDialog; break;\r
873                 case 5:\r
874                   if(!appData.icsActive) continue;\r
875                   SAY("display");\r
876                   h = hText; break;\r
877                 case 6:\r
878                   if(!appData.icsActive) continue;\r
879                   SAY("input");\r
880                   h = hInput; break;\r
881             }\r
882           } while(h == 0);\r
883 \r
884           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
885           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
886           SetFocus(h);\r
887 \r
888           continue; // this message now has been processed\r
889         }\r
890       }\r
891 \r
892       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
893           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
894           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
895           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
896           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
897           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
898           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
899           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
900           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
901           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
902         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
903         for(i=0; i<MAX_CHAT; i++) \r
904             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
905                 done = 1; break;\r
906         }\r
907         if(done) continue; // [HGM] chat: end patch\r
908         TranslateMessage(&msg); /* Translates virtual key codes */\r
909         DispatchMessage(&msg);  /* Dispatches message to window */\r
910       }\r
911     }\r
912 \r
913 \r
914   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
915 }\r
916 \r
917 /*---------------------------------------------------------------------------*\\r
918  *\r
919  * Initialization functions\r
920  *\r
921 \*---------------------------------------------------------------------------*/\r
922 \r
923 void\r
924 SetUserLogo()\r
925 {   // update user logo if necessary\r
926     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
927 \r
928     if(appData.autoLogo) {\r
929           curName = UserName();\r
930           if(strcmp(curName, oldUserName)) {\r
931                 GetCurrentDirectory(MSG_SIZ, dir);\r
932                 SetCurrentDirectory(installDir);\r
933                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
934                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
935                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
936                 if(userLogo == NULL)\r
937                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
938                 SetCurrentDirectory(dir); /* return to prev directory */\r
939           }\r
940     }\r
941 }\r
942 \r
943 BOOL\r
944 InitApplication(HINSTANCE hInstance)\r
945 {\r
946   WNDCLASS wc;\r
947 \r
948   /* Fill in window class structure with parameters that describe the */\r
949   /* main window. */\r
950 \r
951   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
952   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
953   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
954   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
955   wc.hInstance     = hInstance;         /* Owner of this class */\r
956   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
957   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
958   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
959   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
960   wc.lpszClassName = szAppName;                 /* Name to register as */\r
961 \r
962   /* Register the window class and return success/failure code. */\r
963   if (!RegisterClass(&wc)) return FALSE;\r
964 \r
965   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
966   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
967   wc.cbClsExtra    = 0;\r
968   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
969   wc.hInstance     = hInstance;\r
970   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
971   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
972   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
973   wc.lpszMenuName  = NULL;\r
974   wc.lpszClassName = szConsoleName;\r
975 \r
976   if (!RegisterClass(&wc)) return FALSE;\r
977   return TRUE;\r
978 }\r
979 \r
980 \r
981 /* Set by InitInstance, used by EnsureOnScreen */\r
982 int screenHeight, screenWidth;\r
983 \r
984 void\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
986 {\r
987 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
988   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
989   if (*x > screenWidth - 32) *x = 0;\r
990   if (*y > screenHeight - 32) *y = 0;\r
991   if (*x < minX) *x = minX;\r
992   if (*y < minY) *y = minY;\r
993 }\r
994 \r
995 VOID\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
997 {\r
998   char buf[MSG_SIZ], dir[MSG_SIZ];\r
999   GetCurrentDirectory(MSG_SIZ, dir);\r
1000   SetCurrentDirectory(installDir);\r
1001   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1002       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1003 \r
1004       if (cps->programLogo == NULL && appData.debugMode) {\r
1005           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1006       }\r
1007   } else if(appData.autoLogo) {\r
1008       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1009         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1010         cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1011       } else\r
1012       if(appData.directory[n] && appData.directory[n][0]) {\r
1013         SetCurrentDirectory(appData.directory[n]);\r
1014         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1015       }\r
1016   }\r
1017   SetCurrentDirectory(dir); /* return to prev directory */\r
1018 }\r
1019 \r
1020 VOID\r
1021 InitTextures()\r
1022 {\r
1023   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1024   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1025   \r
1026   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1027       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1028       liteBackTextureMode = appData.liteBackTextureMode;\r
1029 \r
1030       if (liteBackTexture == NULL && appData.debugMode) {\r
1031           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1032       }\r
1033   }\r
1034   \r
1035   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1036       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1037       darkBackTextureMode = appData.darkBackTextureMode;\r
1038 \r
1039       if (darkBackTexture == NULL && appData.debugMode) {\r
1040           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1041       }\r
1042   }\r
1043 }\r
1044 \r
1045 BOOL\r
1046 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1047 {\r
1048   HWND hwnd; /* Main window handle. */\r
1049   int ibs;\r
1050   WINDOWPLACEMENT wp;\r
1051   char *filepart;\r
1052 \r
1053   hInst = hInstance;    /* Store instance handle in our global variable */\r
1054   programName = szAppName;\r
1055 \r
1056   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1057     *filepart = NULLCHAR;\r
1058   } else {\r
1059     GetCurrentDirectory(MSG_SIZ, installDir);\r
1060   }\r
1061   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1062   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
1063   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1064   /* xboard, and older WinBoards, controlled the move sound with the\r
1065      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1066      always turn the option on (so that the backend will call us),\r
1067      then let the user turn the sound off by setting it to silence if\r
1068      desired.  To accommodate old winboard.ini files saved by old\r
1069      versions of WinBoard, we also turn off the sound if the option\r
1070      was initially set to false. [HGM] taken out of InitAppData */\r
1071   if (!appData.ringBellAfterMoves) {\r
1072     sounds[(int)SoundMove].name = strdup("");\r
1073     appData.ringBellAfterMoves = TRUE;\r
1074   }\r
1075   if (appData.debugMode) {\r
1076     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1077     setbuf(debugFP, NULL);\r
1078   }\r
1079 \r
1080   LoadLanguageFile(appData.language);\r
1081 \r
1082   InitBackEnd1();\r
1083 \r
1084 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1085 //  InitEngineUCI( installDir, &second );\r
1086 \r
1087   /* Create a main window for this application instance. */\r
1088   hwnd = CreateWindow(szAppName, szTitle,\r
1089                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1090                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1091                       NULL, NULL, hInstance, NULL);\r
1092   hwndMain = hwnd;\r
1093 \r
1094   /* If window could not be created, return "failure" */\r
1095   if (!hwnd) {\r
1096     return (FALSE);\r
1097   }\r
1098 \r
1099   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1100   LoadLogo(&first, 0, FALSE);\r
1101   LoadLogo(&second, 1, appData.icsActive);\r
1102 \r
1103   SetUserLogo();\r
1104 \r
1105   iconWhite = LoadIcon(hInstance, "icon_white");\r
1106   iconBlack = LoadIcon(hInstance, "icon_black");\r
1107   iconCurrent = iconWhite;\r
1108   InitDrawingColors();\r
1109   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1110   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1111   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1112     /* Compute window size for each board size, and use the largest\r
1113        size that fits on this screen as the default. */\r
1114     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1115     if (boardSize == (BoardSize)-1 &&\r
1116         winH <= screenHeight\r
1117            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1118         && winW <= screenWidth) {\r
1119       boardSize = (BoardSize)ibs;\r
1120     }\r
1121   }\r
1122 \r
1123   InitDrawingSizes(boardSize, 0);\r
1124   RecentEngineMenu(appData.recentEngineList);\r
1125   InitMenuChecks();\r
1126   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1127 \r
1128   /* [AS] Load textures if specified */\r
1129   InitTextures();\r
1130 \r
1131   mysrandom( (unsigned) time(NULL) );\r
1132 \r
1133   /* [AS] Restore layout */\r
1134   if( wpMoveHistory.visible ) {\r
1135       MoveHistoryPopUp();\r
1136   }\r
1137 \r
1138   if( wpEvalGraph.visible ) {\r
1139       EvalGraphPopUp();\r
1140   }\r
1141 \r
1142   if( wpEngineOutput.visible ) {\r
1143       EngineOutputPopUp();\r
1144   }\r
1145 \r
1146   /* Make the window visible; update its client area; and return "success" */\r
1147   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1148   wp.length = sizeof(WINDOWPLACEMENT);\r
1149   wp.flags = 0;\r
1150   wp.showCmd = nCmdShow;\r
1151   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1152   wp.rcNormalPosition.left = wpMain.x;\r
1153   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1154   wp.rcNormalPosition.top = wpMain.y;\r
1155   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1156   SetWindowPlacement(hwndMain, &wp);\r
1157 \r
1158   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1159 \r
1160   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1161                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1162 \r
1163   if (hwndConsole) {\r
1164 #if AOT_CONSOLE\r
1165     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1166                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1167 #endif\r
1168     ShowWindow(hwndConsole, nCmdShow);\r
1169     SetActiveWindow(hwndConsole);\r
1170   }\r
1171   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1172   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1173 \r
1174   return TRUE;\r
1175 \r
1176 }\r
1177 \r
1178 VOID\r
1179 InitMenuChecks()\r
1180 {\r
1181   HMENU hmenu = GetMenu(hwndMain);\r
1182 \r
1183   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1184                         MF_BYCOMMAND|((appData.icsActive &&\r
1185                                        *appData.icsCommPort != NULLCHAR) ?\r
1186                                       MF_ENABLED : MF_GRAYED));\r
1187   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1188                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1189                                      MF_CHECKED : MF_UNCHECKED));\r
1190 }\r
1191 \r
1192 //---------------------------------------------------------------------------------------------------------\r
1193 \r
1194 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1195 #define XBOARD FALSE\r
1196 \r
1197 #define OPTCHAR "/"\r
1198 #define SEPCHAR "="\r
1199 #define TOPLEVEL 0\r
1200 \r
1201 #include "args.h"\r
1202 \r
1203 // front-end part of option handling\r
1204 \r
1205 VOID\r
1206 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1207 {\r
1208   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1209   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1210   DeleteDC(hdc);\r
1211   lf->lfWidth = 0;\r
1212   lf->lfEscapement = 0;\r
1213   lf->lfOrientation = 0;\r
1214   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1215   lf->lfItalic = mfp->italic;\r
1216   lf->lfUnderline = mfp->underline;\r
1217   lf->lfStrikeOut = mfp->strikeout;\r
1218   lf->lfCharSet = mfp->charset;\r
1219   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1220   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1221   lf->lfQuality = DEFAULT_QUALITY;\r
1222   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1223     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1224 }\r
1225 \r
1226 void\r
1227 CreateFontInMF(MyFont *mf)\r
1228\r
1229   LFfromMFP(&mf->lf, &mf->mfp);\r
1230   if (mf->hf) DeleteObject(mf->hf);\r
1231   mf->hf = CreateFontIndirect(&mf->lf);\r
1232 }\r
1233 \r
1234 // [HGM] This platform-dependent table provides the location for storing the color info\r
1235 void *\r
1236 colorVariable[] = {\r
1237   &whitePieceColor, \r
1238   &blackPieceColor, \r
1239   &lightSquareColor,\r
1240   &darkSquareColor, \r
1241   &highlightSquareColor,\r
1242   &premoveHighlightColor,\r
1243   NULL,\r
1244   &consoleBackgroundColor,\r
1245   &appData.fontForeColorWhite,\r
1246   &appData.fontBackColorWhite,\r
1247   &appData.fontForeColorBlack,\r
1248   &appData.fontBackColorBlack,\r
1249   &appData.evalHistColorWhite,\r
1250   &appData.evalHistColorBlack,\r
1251   &appData.highlightArrowColor,\r
1252 };\r
1253 \r
1254 /* Command line font name parser.  NULL name means do nothing.\r
1255    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1256    For backward compatibility, syntax without the colon is also\r
1257    accepted, but font names with digits in them won't work in that case.\r
1258 */\r
1259 VOID\r
1260 ParseFontName(char *name, MyFontParams *mfp)\r
1261 {\r
1262   char *p, *q;\r
1263   if (name == NULL) return;\r
1264   p = name;\r
1265   q = strchr(p, ':');\r
1266   if (q) {\r
1267     if (q - p >= sizeof(mfp->faceName))\r
1268       ExitArgError(_("Font name too long:"), name, TRUE);\r
1269     memcpy(mfp->faceName, p, q - p);\r
1270     mfp->faceName[q - p] = NULLCHAR;\r
1271     p = q + 1;\r
1272   } else {\r
1273     q = mfp->faceName;\r
1274     while (*p && !isdigit(*p)) {\r
1275       *q++ = *p++;\r
1276       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1277         ExitArgError(_("Font name too long:"), name, TRUE);\r
1278     }\r
1279     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1280     *q = NULLCHAR;\r
1281   }\r
1282   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1283   mfp->pointSize = (float) atof(p);\r
1284   mfp->bold = (strchr(p, 'b') != NULL);\r
1285   mfp->italic = (strchr(p, 'i') != NULL);\r
1286   mfp->underline = (strchr(p, 'u') != NULL);\r
1287   mfp->strikeout = (strchr(p, 's') != NULL);\r
1288   mfp->charset = DEFAULT_CHARSET;\r
1289   q = strchr(p, 'c');\r
1290   if (q)\r
1291     mfp->charset = (BYTE) atoi(q+1);\r
1292 }\r
1293 \r
1294 void\r
1295 ParseFont(char *name, int number)\r
1296 { // wrapper to shield back-end from 'font'\r
1297   ParseFontName(name, &font[boardSize][number]->mfp);\r
1298 }\r
1299 \r
1300 void\r
1301 SetFontDefaults()\r
1302 { // in WB  we have a 2D array of fonts; this initializes their description\r
1303   int i, j;\r
1304   /* Point font array elements to structures and\r
1305      parse default font names */\r
1306   for (i=0; i<NUM_FONTS; i++) {\r
1307     for (j=0; j<NUM_SIZES; j++) {\r
1308       font[j][i] = &fontRec[j][i];\r
1309       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1310     }\r
1311   }\r
1312 }\r
1313 \r
1314 void\r
1315 CreateFonts()\r
1316 { // here we create the actual fonts from the selected descriptions\r
1317   int i, j;\r
1318   for (i=0; i<NUM_FONTS; i++) {\r
1319     for (j=0; j<NUM_SIZES; j++) {\r
1320       CreateFontInMF(font[j][i]);\r
1321     }\r
1322   }\r
1323 }\r
1324 /* Color name parser.\r
1325    X version accepts X color names, but this one\r
1326    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1327 COLORREF\r
1328 ParseColorName(char *name)\r
1329 {\r
1330   int red, green, blue, count;\r
1331   char buf[MSG_SIZ];\r
1332 \r
1333   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1334   if (count != 3) {\r
1335     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1336       &red, &green, &blue);\r
1337   }\r
1338   if (count != 3) {\r
1339     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1340     DisplayError(buf, 0);\r
1341     return RGB(0, 0, 0);\r
1342   }\r
1343   return PALETTERGB(red, green, blue);\r
1344 }\r
1345 \r
1346 void\r
1347 ParseColor(int n, char *name)\r
1348 { // for WinBoard the color is an int, which needs to be derived from the string\r
1349   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1350 }\r
1351 \r
1352 void\r
1353 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1354 {\r
1355   char *e = argValue;\r
1356   int eff = 0;\r
1357 \r
1358   while (*e) {\r
1359     if (*e == 'b')      eff |= CFE_BOLD;\r
1360     else if (*e == 'i') eff |= CFE_ITALIC;\r
1361     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1362     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1363     else if (*e == '#' || isdigit(*e)) break;\r
1364     e++;\r
1365   }\r
1366   *effects = eff;\r
1367   *color   = ParseColorName(e);\r
1368 }\r
1369 \r
1370 void\r
1371 ParseTextAttribs(ColorClass cc, char *s)\r
1372 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1373     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1374     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1375 }\r
1376 \r
1377 void\r
1378 ParseBoardSize(void *addr, char *name)\r
1379 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1380   BoardSize bs = SizeTiny;\r
1381   while (sizeInfo[bs].name != NULL) {\r
1382     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1383         *(BoardSize *)addr = bs;\r
1384         return;\r
1385     }\r
1386     bs++;\r
1387   }\r
1388   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1389 }\r
1390 \r
1391 void\r
1392 LoadAllSounds()\r
1393 { // [HGM] import name from appData first\r
1394   ColorClass cc;\r
1395   SoundClass sc;\r
1396   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1397     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1398     textAttribs[cc].sound.data = NULL;\r
1399     MyLoadSound(&textAttribs[cc].sound);\r
1400   }\r
1401   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1402     textAttribs[cc].sound.name = strdup("");\r
1403     textAttribs[cc].sound.data = NULL;\r
1404   }\r
1405   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1406     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1407     sounds[sc].data = NULL;\r
1408     MyLoadSound(&sounds[sc]);\r
1409   }\r
1410 }\r
1411 \r
1412 void\r
1413 SetCommPortDefaults()\r
1414 {\r
1415    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1416   dcb.DCBlength = sizeof(DCB);\r
1417   dcb.BaudRate = 9600;\r
1418   dcb.fBinary = TRUE;\r
1419   dcb.fParity = FALSE;\r
1420   dcb.fOutxCtsFlow = FALSE;\r
1421   dcb.fOutxDsrFlow = FALSE;\r
1422   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1423   dcb.fDsrSensitivity = FALSE;\r
1424   dcb.fTXContinueOnXoff = TRUE;\r
1425   dcb.fOutX = FALSE;\r
1426   dcb.fInX = FALSE;\r
1427   dcb.fNull = FALSE;\r
1428   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1429   dcb.fAbortOnError = FALSE;\r
1430   dcb.ByteSize = 7;\r
1431   dcb.Parity = SPACEPARITY;\r
1432   dcb.StopBits = ONESTOPBIT;\r
1433 }\r
1434 \r
1435 // [HGM] args: these three cases taken out to stay in front-end\r
1436 void\r
1437 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1438 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1439         // while the curent board size determines the element. This system should be ported to XBoard.\r
1440         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1441         int bs;\r
1442         for (bs=0; bs<NUM_SIZES; bs++) {\r
1443           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1444           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1445           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1446             ad->argName, mfp->faceName, mfp->pointSize,\r
1447             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1448             mfp->bold ? "b" : "",\r
1449             mfp->italic ? "i" : "",\r
1450             mfp->underline ? "u" : "",\r
1451             mfp->strikeout ? "s" : "",\r
1452             (int)mfp->charset);\r
1453         }\r
1454       }\r
1455 \r
1456 void\r
1457 ExportSounds()\r
1458 { // [HGM] copy the names from the internal WB variables to appData\r
1459   ColorClass cc;\r
1460   SoundClass sc;\r
1461   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1462     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1463   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1464     (&appData.soundMove)[sc] = sounds[sc].name;\r
1465 }\r
1466 \r
1467 void\r
1468 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1469 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1470         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1471         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1472           (ta->effects & CFE_BOLD) ? "b" : "",\r
1473           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1474           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1475           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1476           (ta->effects) ? " " : "",\r
1477           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1478       }\r
1479 \r
1480 void\r
1481 SaveColor(FILE *f, ArgDescriptor *ad)\r
1482 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1483         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1484         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1485           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1486 }\r
1487 \r
1488 void\r
1489 SaveBoardSize(FILE *f, char *name, void *addr)\r
1490 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1491   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1492 }\r
1493 \r
1494 void\r
1495 ParseCommPortSettings(char *s)\r
1496 { // wrapper to keep dcb from back-end\r
1497   ParseCommSettings(s, &dcb);\r
1498 }\r
1499 \r
1500 void\r
1501 GetWindowCoords()\r
1502 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1503   GetActualPlacement(hwndMain, &wpMain);\r
1504   GetActualPlacement(hwndConsole, &wpConsole);\r
1505   GetActualPlacement(commentDialog, &wpComment);\r
1506   GetActualPlacement(editTagsDialog, &wpTags);\r
1507   GetActualPlacement(gameListDialog, &wpGameList);\r
1508   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1509   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1510   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1511 }\r
1512 \r
1513 void\r
1514 PrintCommPortSettings(FILE *f, char *name)\r
1515 { // wrapper to shield back-end from DCB\r
1516       PrintCommSettings(f, name, &dcb);\r
1517 }\r
1518 \r
1519 int\r
1520 MySearchPath(char *installDir, char *name, char *fullname)\r
1521 {\r
1522   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1523   if(name[0]== '%') {\r
1524     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1525     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1526       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1527       *strchr(buf, '%') = 0;\r
1528       strcat(fullname, getenv(buf));\r
1529       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1530     }\r
1531     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1532     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1533     return (int) strlen(fullname);\r
1534   }\r
1535   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1536 }\r
1537 \r
1538 int\r
1539 MyGetFullPathName(char *name, char *fullname)\r
1540 {\r
1541   char *dummy;\r
1542   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1543 }\r
1544 \r
1545 int\r
1546 MainWindowUp()\r
1547 { // [HGM] args: allows testing if main window is realized from back-end\r
1548   return hwndMain != NULL;\r
1549 }\r
1550 \r
1551 void\r
1552 PopUpStartupDialog()\r
1553 {\r
1554     FARPROC lpProc;\r
1555     \r
1556     LoadLanguageFile(appData.language);\r
1557     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1558     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1559     FreeProcInstance(lpProc);\r
1560 }\r
1561 \r
1562 /*---------------------------------------------------------------------------*\\r
1563  *\r
1564  * GDI board drawing routines\r
1565  *\r
1566 \*---------------------------------------------------------------------------*/\r
1567 \r
1568 /* [AS] Draw square using background texture */\r
1569 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1570 {\r
1571     XFORM   x;\r
1572 \r
1573     if( mode == 0 ) {\r
1574         return; /* Should never happen! */\r
1575     }\r
1576 \r
1577     SetGraphicsMode( dst, GM_ADVANCED );\r
1578 \r
1579     switch( mode ) {\r
1580     case 1:\r
1581         /* Identity */\r
1582         break;\r
1583     case 2:\r
1584         /* X reflection */\r
1585         x.eM11 = -1.0;\r
1586         x.eM12 = 0;\r
1587         x.eM21 = 0;\r
1588         x.eM22 = 1.0;\r
1589         x.eDx = (FLOAT) dw + dx - 1;\r
1590         x.eDy = 0;\r
1591         dx = 0;\r
1592         SetWorldTransform( dst, &x );\r
1593         break;\r
1594     case 3:\r
1595         /* Y reflection */\r
1596         x.eM11 = 1.0;\r
1597         x.eM12 = 0;\r
1598         x.eM21 = 0;\r
1599         x.eM22 = -1.0;\r
1600         x.eDx = 0;\r
1601         x.eDy = (FLOAT) dh + dy - 1;\r
1602         dy = 0;\r
1603         SetWorldTransform( dst, &x );\r
1604         break;\r
1605     case 4:\r
1606         /* X/Y flip */\r
1607         x.eM11 = 0;\r
1608         x.eM12 = 1.0;\r
1609         x.eM21 = 1.0;\r
1610         x.eM22 = 0;\r
1611         x.eDx = (FLOAT) dx;\r
1612         x.eDy = (FLOAT) dy;\r
1613         dx = 0;\r
1614         dy = 0;\r
1615         SetWorldTransform( dst, &x );\r
1616         break;\r
1617     }\r
1618 \r
1619     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1620 \r
1621     x.eM11 = 1.0;\r
1622     x.eM12 = 0;\r
1623     x.eM21 = 0;\r
1624     x.eM22 = 1.0;\r
1625     x.eDx = 0;\r
1626     x.eDy = 0;\r
1627     SetWorldTransform( dst, &x );\r
1628 \r
1629     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1630 }\r
1631 \r
1632 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1633 enum {\r
1634     PM_WP = (int) WhitePawn, \r
1635     PM_WN = (int) WhiteKnight, \r
1636     PM_WB = (int) WhiteBishop, \r
1637     PM_WR = (int) WhiteRook, \r
1638     PM_WQ = (int) WhiteQueen, \r
1639     PM_WF = (int) WhiteFerz, \r
1640     PM_WW = (int) WhiteWazir, \r
1641     PM_WE = (int) WhiteAlfil, \r
1642     PM_WM = (int) WhiteMan, \r
1643     PM_WO = (int) WhiteCannon, \r
1644     PM_WU = (int) WhiteUnicorn, \r
1645     PM_WH = (int) WhiteNightrider, \r
1646     PM_WA = (int) WhiteAngel, \r
1647     PM_WC = (int) WhiteMarshall, \r
1648     PM_WAB = (int) WhiteCardinal, \r
1649     PM_WD = (int) WhiteDragon, \r
1650     PM_WL = (int) WhiteLance, \r
1651     PM_WS = (int) WhiteCobra, \r
1652     PM_WV = (int) WhiteFalcon, \r
1653     PM_WSG = (int) WhiteSilver, \r
1654     PM_WG = (int) WhiteGrasshopper, \r
1655     PM_WK = (int) WhiteKing,\r
1656     PM_BP = (int) BlackPawn, \r
1657     PM_BN = (int) BlackKnight, \r
1658     PM_BB = (int) BlackBishop, \r
1659     PM_BR = (int) BlackRook, \r
1660     PM_BQ = (int) BlackQueen, \r
1661     PM_BF = (int) BlackFerz, \r
1662     PM_BW = (int) BlackWazir, \r
1663     PM_BE = (int) BlackAlfil, \r
1664     PM_BM = (int) BlackMan,\r
1665     PM_BO = (int) BlackCannon, \r
1666     PM_BU = (int) BlackUnicorn, \r
1667     PM_BH = (int) BlackNightrider, \r
1668     PM_BA = (int) BlackAngel, \r
1669     PM_BC = (int) BlackMarshall, \r
1670     PM_BG = (int) BlackGrasshopper, \r
1671     PM_BAB = (int) BlackCardinal,\r
1672     PM_BD = (int) BlackDragon,\r
1673     PM_BL = (int) BlackLance,\r
1674     PM_BS = (int) BlackCobra,\r
1675     PM_BV = (int) BlackFalcon,\r
1676     PM_BSG = (int) BlackSilver,\r
1677     PM_BK = (int) BlackKing\r
1678 };\r
1679 \r
1680 static HFONT hPieceFont = NULL;\r
1681 static HBITMAP hPieceMask[(int) EmptySquare];\r
1682 static HBITMAP hPieceFace[(int) EmptySquare];\r
1683 static int fontBitmapSquareSize = 0;\r
1684 static char pieceToFontChar[(int) EmptySquare] =\r
1685                               { 'p', 'n', 'b', 'r', 'q', \r
1686                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1687                       'k', 'o', 'm', 'v', 't', 'w', \r
1688                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1689                                                               'l' };\r
1690 \r
1691 extern BOOL SetCharTable( char *table, const char * map );\r
1692 /* [HGM] moved to backend.c */\r
1693 \r
1694 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1695 {\r
1696     HBRUSH hbrush;\r
1697     BYTE r1 = GetRValue( color );\r
1698     BYTE g1 = GetGValue( color );\r
1699     BYTE b1 = GetBValue( color );\r
1700     BYTE r2 = r1 / 2;\r
1701     BYTE g2 = g1 / 2;\r
1702     BYTE b2 = b1 / 2;\r
1703     RECT rc;\r
1704 \r
1705     /* Create a uniform background first */\r
1706     hbrush = CreateSolidBrush( color );\r
1707     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1708     FillRect( hdc, &rc, hbrush );\r
1709     DeleteObject( hbrush );\r
1710     \r
1711     if( mode == 1 ) {\r
1712         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1713         int steps = squareSize / 2;\r
1714         int i;\r
1715 \r
1716         for( i=0; i<steps; i++ ) {\r
1717             BYTE r = r1 - (r1-r2) * i / steps;\r
1718             BYTE g = g1 - (g1-g2) * i / steps;\r
1719             BYTE b = b1 - (b1-b2) * i / steps;\r
1720 \r
1721             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1722             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1723             FillRect( hdc, &rc, hbrush );\r
1724             DeleteObject(hbrush);\r
1725         }\r
1726     }\r
1727     else if( mode == 2 ) {\r
1728         /* Diagonal gradient, good more or less for every piece */\r
1729         POINT triangle[3];\r
1730         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1731         HBRUSH hbrush_old;\r
1732         int steps = squareSize;\r
1733         int i;\r
1734 \r
1735         triangle[0].x = squareSize - steps;\r
1736         triangle[0].y = squareSize;\r
1737         triangle[1].x = squareSize;\r
1738         triangle[1].y = squareSize;\r
1739         triangle[2].x = squareSize;\r
1740         triangle[2].y = squareSize - steps;\r
1741 \r
1742         for( i=0; i<steps; i++ ) {\r
1743             BYTE r = r1 - (r1-r2) * i / steps;\r
1744             BYTE g = g1 - (g1-g2) * i / steps;\r
1745             BYTE b = b1 - (b1-b2) * i / steps;\r
1746 \r
1747             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1748             hbrush_old = SelectObject( hdc, hbrush );\r
1749             Polygon( hdc, triangle, 3 );\r
1750             SelectObject( hdc, hbrush_old );\r
1751             DeleteObject(hbrush);\r
1752             triangle[0].x++;\r
1753             triangle[2].y++;\r
1754         }\r
1755 \r
1756         SelectObject( hdc, hpen );\r
1757     }\r
1758 }\r
1759 \r
1760 /*\r
1761     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1762     seems to work ok. The main problem here is to find the "inside" of a chess\r
1763     piece: follow the steps as explained below.\r
1764 */\r
1765 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1766 {\r
1767     HBITMAP hbm;\r
1768     HBITMAP hbm_old;\r
1769     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1770     RECT rc;\r
1771     SIZE sz;\r
1772     POINT pt;\r
1773     int backColor = whitePieceColor; \r
1774     int foreColor = blackPieceColor;\r
1775     \r
1776     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1777         backColor = appData.fontBackColorWhite;\r
1778         foreColor = appData.fontForeColorWhite;\r
1779     }\r
1780     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1781         backColor = appData.fontBackColorBlack;\r
1782         foreColor = appData.fontForeColorBlack;\r
1783     }\r
1784 \r
1785     /* Mask */\r
1786     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1787 \r
1788     hbm_old = SelectObject( hdc, hbm );\r
1789 \r
1790     rc.left = 0;\r
1791     rc.top = 0;\r
1792     rc.right = squareSize;\r
1793     rc.bottom = squareSize;\r
1794 \r
1795     /* Step 1: background is now black */\r
1796     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1797 \r
1798     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1799 \r
1800     pt.x = (squareSize - sz.cx) / 2;\r
1801     pt.y = (squareSize - sz.cy) / 2;\r
1802 \r
1803     SetBkMode( hdc, TRANSPARENT );\r
1804     SetTextColor( hdc, chroma );\r
1805     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1806     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1807 \r
1808     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1809     /* Step 3: the area outside the piece is filled with white */\r
1810 //    FloodFill( hdc, 0, 0, chroma );\r
1811     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1812     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1813     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1814     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1815     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1816     /* \r
1817         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1818         but if the start point is not inside the piece we're lost!\r
1819         There should be a better way to do this... if we could create a region or path\r
1820         from the fill operation we would be fine for example.\r
1821     */\r
1822 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1823     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1824 \r
1825     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1826         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1827         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1828 \r
1829         SelectObject( dc2, bm2 );\r
1830         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1831         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1832         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1833         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1834         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1835 \r
1836         DeleteDC( dc2 );\r
1837         DeleteObject( bm2 );\r
1838     }\r
1839 \r
1840     SetTextColor( hdc, 0 );\r
1841     /* \r
1842         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1843         draw the piece again in black for safety.\r
1844     */\r
1845     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1846 \r
1847     SelectObject( hdc, hbm_old );\r
1848 \r
1849     if( hPieceMask[index] != NULL ) {\r
1850         DeleteObject( hPieceMask[index] );\r
1851     }\r
1852 \r
1853     hPieceMask[index] = hbm;\r
1854 \r
1855     /* Face */\r
1856     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1857 \r
1858     SelectObject( hdc, hbm );\r
1859 \r
1860     {\r
1861         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1862         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1863         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1864 \r
1865         SelectObject( dc1, hPieceMask[index] );\r
1866         SelectObject( dc2, bm2 );\r
1867         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1868         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1869         \r
1870         /* \r
1871             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1872             the piece background and deletes (makes transparent) the rest.\r
1873             Thanks to that mask, we are free to paint the background with the greates\r
1874             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1875             We use this, to make gradients and give the pieces a "roundish" look.\r
1876         */\r
1877         SetPieceBackground( hdc, backColor, 2 );\r
1878         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1879 \r
1880         DeleteDC( dc2 );\r
1881         DeleteDC( dc1 );\r
1882         DeleteObject( bm2 );\r
1883     }\r
1884 \r
1885     SetTextColor( hdc, foreColor );\r
1886     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1887 \r
1888     SelectObject( hdc, hbm_old );\r
1889 \r
1890     if( hPieceFace[index] != NULL ) {\r
1891         DeleteObject( hPieceFace[index] );\r
1892     }\r
1893 \r
1894     hPieceFace[index] = hbm;\r
1895 }\r
1896 \r
1897 static int TranslatePieceToFontPiece( int piece )\r
1898 {\r
1899     switch( piece ) {\r
1900     case BlackPawn:\r
1901         return PM_BP;\r
1902     case BlackKnight:\r
1903         return PM_BN;\r
1904     case BlackBishop:\r
1905         return PM_BB;\r
1906     case BlackRook:\r
1907         return PM_BR;\r
1908     case BlackQueen:\r
1909         return PM_BQ;\r
1910     case BlackKing:\r
1911         return PM_BK;\r
1912     case WhitePawn:\r
1913         return PM_WP;\r
1914     case WhiteKnight:\r
1915         return PM_WN;\r
1916     case WhiteBishop:\r
1917         return PM_WB;\r
1918     case WhiteRook:\r
1919         return PM_WR;\r
1920     case WhiteQueen:\r
1921         return PM_WQ;\r
1922     case WhiteKing:\r
1923         return PM_WK;\r
1924 \r
1925     case BlackAngel:\r
1926         return PM_BA;\r
1927     case BlackMarshall:\r
1928         return PM_BC;\r
1929     case BlackFerz:\r
1930         return PM_BF;\r
1931     case BlackNightrider:\r
1932         return PM_BH;\r
1933     case BlackAlfil:\r
1934         return PM_BE;\r
1935     case BlackWazir:\r
1936         return PM_BW;\r
1937     case BlackUnicorn:\r
1938         return PM_BU;\r
1939     case BlackCannon:\r
1940         return PM_BO;\r
1941     case BlackGrasshopper:\r
1942         return PM_BG;\r
1943     case BlackMan:\r
1944         return PM_BM;\r
1945     case BlackSilver:\r
1946         return PM_BSG;\r
1947     case BlackLance:\r
1948         return PM_BL;\r
1949     case BlackFalcon:\r
1950         return PM_BV;\r
1951     case BlackCobra:\r
1952         return PM_BS;\r
1953     case BlackCardinal:\r
1954         return PM_BAB;\r
1955     case BlackDragon:\r
1956         return PM_BD;\r
1957 \r
1958     case WhiteAngel:\r
1959         return PM_WA;\r
1960     case WhiteMarshall:\r
1961         return PM_WC;\r
1962     case WhiteFerz:\r
1963         return PM_WF;\r
1964     case WhiteNightrider:\r
1965         return PM_WH;\r
1966     case WhiteAlfil:\r
1967         return PM_WE;\r
1968     case WhiteWazir:\r
1969         return PM_WW;\r
1970     case WhiteUnicorn:\r
1971         return PM_WU;\r
1972     case WhiteCannon:\r
1973         return PM_WO;\r
1974     case WhiteGrasshopper:\r
1975         return PM_WG;\r
1976     case WhiteMan:\r
1977         return PM_WM;\r
1978     case WhiteSilver:\r
1979         return PM_WSG;\r
1980     case WhiteLance:\r
1981         return PM_WL;\r
1982     case WhiteFalcon:\r
1983         return PM_WV;\r
1984     case WhiteCobra:\r
1985         return PM_WS;\r
1986     case WhiteCardinal:\r
1987         return PM_WAB;\r
1988     case WhiteDragon:\r
1989         return PM_WD;\r
1990     }\r
1991 \r
1992     return 0;\r
1993 }\r
1994 \r
1995 void CreatePiecesFromFont()\r
1996 {\r
1997     LOGFONT lf;\r
1998     HDC hdc_window = NULL;\r
1999     HDC hdc = NULL;\r
2000     HFONT hfont_old;\r
2001     int fontHeight;\r
2002     int i;\r
2003 \r
2004     if( fontBitmapSquareSize < 0 ) {\r
2005         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2006         return;\r
2007     }\r
2008 \r
2009     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2010             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2011         fontBitmapSquareSize = -1;\r
2012         return;\r
2013     }\r
2014 \r
2015     if( fontBitmapSquareSize != squareSize ) {\r
2016         hdc_window = GetDC( hwndMain );\r
2017         hdc = CreateCompatibleDC( hdc_window );\r
2018 \r
2019         if( hPieceFont != NULL ) {\r
2020             DeleteObject( hPieceFont );\r
2021         }\r
2022         else {\r
2023             for( i=0; i<=(int)BlackKing; i++ ) {\r
2024                 hPieceMask[i] = NULL;\r
2025                 hPieceFace[i] = NULL;\r
2026             }\r
2027         }\r
2028 \r
2029         fontHeight = 75;\r
2030 \r
2031         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2032             fontHeight = appData.fontPieceSize;\r
2033         }\r
2034 \r
2035         fontHeight = (fontHeight * squareSize) / 100;\r
2036 \r
2037         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2038         lf.lfWidth = 0;\r
2039         lf.lfEscapement = 0;\r
2040         lf.lfOrientation = 0;\r
2041         lf.lfWeight = FW_NORMAL;\r
2042         lf.lfItalic = 0;\r
2043         lf.lfUnderline = 0;\r
2044         lf.lfStrikeOut = 0;\r
2045         lf.lfCharSet = DEFAULT_CHARSET;\r
2046         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2047         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2048         lf.lfQuality = PROOF_QUALITY;\r
2049         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2050         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2051         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2052 \r
2053         hPieceFont = CreateFontIndirect( &lf );\r
2054 \r
2055         if( hPieceFont == NULL ) {\r
2056             fontBitmapSquareSize = -2;\r
2057         }\r
2058         else {\r
2059             /* Setup font-to-piece character table */\r
2060             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2061                 /* No (or wrong) global settings, try to detect the font */\r
2062                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2063                     /* Alpha */\r
2064                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2065                 }\r
2066                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2067                     /* DiagramTT* family */\r
2068                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2069                 }\r
2070                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2071                     /* Fairy symbols */\r
2072                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2073                 }\r
2074                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2075                     /* Good Companion (Some characters get warped as literal :-( */\r
2076                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2077                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2078                     SetCharTable(pieceToFontChar, s);\r
2079                 }\r
2080                 else {\r
2081                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2082                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2083                 }\r
2084             }\r
2085 \r
2086             /* Create bitmaps */\r
2087             hfont_old = SelectObject( hdc, hPieceFont );\r
2088             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2089                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2090                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2091 \r
2092             SelectObject( hdc, hfont_old );\r
2093 \r
2094             fontBitmapSquareSize = squareSize;\r
2095         }\r
2096     }\r
2097 \r
2098     if( hdc != NULL ) {\r
2099         DeleteDC( hdc );\r
2100     }\r
2101 \r
2102     if( hdc_window != NULL ) {\r
2103         ReleaseDC( hwndMain, hdc_window );\r
2104     }\r
2105 }\r
2106 \r
2107 HBITMAP\r
2108 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2109 {\r
2110   char name[128];\r
2111 \r
2112     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2113   if (gameInfo.event &&\r
2114       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2115       strcmp(name, "k80s") == 0) {\r
2116     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2117   }\r
2118   return LoadBitmap(hinst, name);\r
2119 }\r
2120 \r
2121 \r
2122 /* Insert a color into the program's logical palette\r
2123    structure.  This code assumes the given color is\r
2124    the result of the RGB or PALETTERGB macro, and it\r
2125    knows how those macros work (which is documented).\r
2126 */\r
2127 VOID\r
2128 InsertInPalette(COLORREF color)\r
2129 {\r
2130   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2131 \r
2132   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2133     DisplayFatalError(_("Too many colors"), 0, 1);\r
2134     pLogPal->palNumEntries--;\r
2135     return;\r
2136   }\r
2137 \r
2138   pe->peFlags = (char) 0;\r
2139   pe->peRed = (char) (0xFF & color);\r
2140   pe->peGreen = (char) (0xFF & (color >> 8));\r
2141   pe->peBlue = (char) (0xFF & (color >> 16));\r
2142   return;\r
2143 }\r
2144 \r
2145 \r
2146 VOID\r
2147 InitDrawingColors()\r
2148 {\r
2149   if (pLogPal == NULL) {\r
2150     /* Allocate enough memory for a logical palette with\r
2151      * PALETTESIZE entries and set the size and version fields\r
2152      * of the logical palette structure.\r
2153      */\r
2154     pLogPal = (NPLOGPALETTE)\r
2155       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2156                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2157     pLogPal->palVersion    = 0x300;\r
2158   }\r
2159   pLogPal->palNumEntries = 0;\r
2160 \r
2161   InsertInPalette(lightSquareColor);\r
2162   InsertInPalette(darkSquareColor);\r
2163   InsertInPalette(whitePieceColor);\r
2164   InsertInPalette(blackPieceColor);\r
2165   InsertInPalette(highlightSquareColor);\r
2166   InsertInPalette(premoveHighlightColor);\r
2167 \r
2168   /*  create a logical color palette according the information\r
2169    *  in the LOGPALETTE structure.\r
2170    */\r
2171   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2172 \r
2173   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2174   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2175   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2176   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2177   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2178   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2179   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2180   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2181   /* [AS] Force rendering of the font-based pieces */\r
2182   if( fontBitmapSquareSize > 0 ) {\r
2183     fontBitmapSquareSize = 0;\r
2184   }\r
2185 }\r
2186 \r
2187 \r
2188 int\r
2189 BoardWidth(int boardSize, int n)\r
2190 { /* [HGM] argument n added to allow different width and height */\r
2191   int lineGap = sizeInfo[boardSize].lineGap;\r
2192 \r
2193   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2194       lineGap = appData.overrideLineGap;\r
2195   }\r
2196 \r
2197   return (n + 1) * lineGap +\r
2198           n * sizeInfo[boardSize].squareSize;\r
2199 }\r
2200 \r
2201 /* Respond to board resize by dragging edge */\r
2202 VOID\r
2203 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2204 {\r
2205   BoardSize newSize = NUM_SIZES - 1;\r
2206   static int recurse = 0;\r
2207   if (IsIconic(hwndMain)) return;\r
2208   if (recurse > 0) return;\r
2209   recurse++;\r
2210   while (newSize > 0) {\r
2211         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2212         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2213            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2214     newSize--;\r
2215   } \r
2216   boardSize = newSize;\r
2217   InitDrawingSizes(boardSize, flags);\r
2218   recurse--;\r
2219 }\r
2220 \r
2221 \r
2222 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2223 \r
2224 VOID\r
2225 InitDrawingSizes(BoardSize boardSize, int flags)\r
2226 {\r
2227   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2228   ChessSquare piece;\r
2229   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2230   HDC hdc;\r
2231   SIZE clockSize, messageSize;\r
2232   HFONT oldFont;\r
2233   char buf[MSG_SIZ];\r
2234   char *str;\r
2235   HMENU hmenu = GetMenu(hwndMain);\r
2236   RECT crect, wrect, oldRect;\r
2237   int offby;\r
2238   LOGBRUSH logbrush;\r
2239   VariantClass v = gameInfo.variant;\r
2240 \r
2241   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2242   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2243 \r
2244   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2245   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2246   oldBoardSize = boardSize;\r
2247 \r
2248   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2249   { // correct board size to one where built-in pieces exist\r
2250     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2251        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2252       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2253       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2254       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2255       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2256       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2257                                    boardSize = SizeMiddling;\r
2258     }\r
2259   }\r
2260   if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2261 \r
2262   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2263   oldRect.top = wpMain.y;\r
2264   oldRect.right = wpMain.x + wpMain.width;\r
2265   oldRect.bottom = wpMain.y + wpMain.height;\r
2266 \r
2267   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2268   smallLayout = sizeInfo[boardSize].smallLayout;\r
2269   squareSize = sizeInfo[boardSize].squareSize;\r
2270   lineGap = sizeInfo[boardSize].lineGap;\r
2271   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2272 \r
2273   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2274       lineGap = appData.overrideLineGap;\r
2275   }\r
2276 \r
2277   if (tinyLayout != oldTinyLayout) {\r
2278     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2279     if (tinyLayout) {\r
2280       style &= ~WS_SYSMENU;\r
2281       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2282                  "&Minimize\tCtrl+F4");\r
2283     } else {\r
2284       style |= WS_SYSMENU;\r
2285       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2286     }\r
2287     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2288 \r
2289     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2290       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2291         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2292     }\r
2293     DrawMenuBar(hwndMain);\r
2294   }\r
2295 \r
2296   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2297   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2298 \r
2299   /* Get text area sizes */\r
2300   hdc = GetDC(hwndMain);\r
2301   if (appData.clockMode) {\r
2302     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2303   } else {\r
2304     snprintf(buf, MSG_SIZ, _("White"));\r
2305   }\r
2306   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2307   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2308   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2309   str = _("We only care about the height here");\r
2310   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2311   SelectObject(hdc, oldFont);\r
2312   ReleaseDC(hwndMain, hdc);\r
2313 \r
2314   /* Compute where everything goes */\r
2315   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2316         /* [HGM] logo: if either logo is on, reserve space for it */\r
2317         logoHeight =  2*clockSize.cy;\r
2318         leftLogoRect.left   = OUTER_MARGIN;\r
2319         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2320         leftLogoRect.top    = OUTER_MARGIN;\r
2321         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2322 \r
2323         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2324         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2325         rightLogoRect.top    = OUTER_MARGIN;\r
2326         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2327 \r
2328 \r
2329     whiteRect.left = leftLogoRect.right;\r
2330     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2331     whiteRect.top = OUTER_MARGIN;\r
2332     whiteRect.bottom = whiteRect.top + logoHeight;\r
2333 \r
2334     blackRect.right = rightLogoRect.left;\r
2335     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2336     blackRect.top = whiteRect.top;\r
2337     blackRect.bottom = whiteRect.bottom;\r
2338   } else {\r
2339     whiteRect.left = OUTER_MARGIN;\r
2340     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2341     whiteRect.top = OUTER_MARGIN;\r
2342     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2343 \r
2344     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2345     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2346     blackRect.top = whiteRect.top;\r
2347     blackRect.bottom = whiteRect.bottom;\r
2348 \r
2349     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2350   }\r
2351 \r
2352   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2353   if (appData.showButtonBar) {\r
2354     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2355       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2356   } else {\r
2357     messageRect.right = OUTER_MARGIN + boardWidth;\r
2358   }\r
2359   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2360   messageRect.bottom = messageRect.top + messageSize.cy;\r
2361 \r
2362   boardRect.left = OUTER_MARGIN;\r
2363   boardRect.right = boardRect.left + boardWidth;\r
2364   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2365   boardRect.bottom = boardRect.top + boardHeight;\r
2366 \r
2367   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2368   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2369   oldTinyLayout = tinyLayout;\r
2370   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2371   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2372     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2373   winW *= 1 + twoBoards;\r
2374   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2375   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2376   wpMain.height = winH; //       without disturbing window attachments\r
2377   GetWindowRect(hwndMain, &wrect);\r
2378   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2379                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2380 \r
2381   // [HGM] placement: let attached windows follow size change.\r
2382   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2383   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2384   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2385   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2386   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2387 \r
2388   /* compensate if menu bar wrapped */\r
2389   GetClientRect(hwndMain, &crect);\r
2390   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2391   wpMain.height += offby;\r
2392   switch (flags) {\r
2393   case WMSZ_TOPLEFT:\r
2394     SetWindowPos(hwndMain, NULL, \r
2395                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2396                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2397     break;\r
2398 \r
2399   case WMSZ_TOPRIGHT:\r
2400   case WMSZ_TOP:\r
2401     SetWindowPos(hwndMain, NULL, \r
2402                  wrect.left, wrect.bottom - wpMain.height, \r
2403                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2404     break;\r
2405 \r
2406   case WMSZ_BOTTOMLEFT:\r
2407   case WMSZ_LEFT:\r
2408     SetWindowPos(hwndMain, NULL, \r
2409                  wrect.right - wpMain.width, wrect.top, \r
2410                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2411     break;\r
2412 \r
2413   case WMSZ_BOTTOMRIGHT:\r
2414   case WMSZ_BOTTOM:\r
2415   case WMSZ_RIGHT:\r
2416   default:\r
2417     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2418                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2419     break;\r
2420   }\r
2421 \r
2422   hwndPause = NULL;\r
2423   for (i = 0; i < N_BUTTONS; i++) {\r
2424     if (buttonDesc[i].hwnd != NULL) {\r
2425       DestroyWindow(buttonDesc[i].hwnd);\r
2426       buttonDesc[i].hwnd = NULL;\r
2427     }\r
2428     if (appData.showButtonBar) {\r
2429       buttonDesc[i].hwnd =\r
2430         CreateWindow("BUTTON", buttonDesc[i].label,\r
2431                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2432                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2433                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2434                      (HMENU) buttonDesc[i].id,\r
2435                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2436       if (tinyLayout) {\r
2437         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2438                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2439                     MAKELPARAM(FALSE, 0));\r
2440       }\r
2441       if (buttonDesc[i].id == IDM_Pause)\r
2442         hwndPause = buttonDesc[i].hwnd;\r
2443       buttonDesc[i].wndproc = (WNDPROC)\r
2444         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2445     }\r
2446   }\r
2447   if (gridPen != NULL) DeleteObject(gridPen);\r
2448   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2449   if (premovePen != NULL) DeleteObject(premovePen);\r
2450   if (lineGap != 0) {\r
2451     logbrush.lbStyle = BS_SOLID;\r
2452     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2453     gridPen =\r
2454       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2455                    lineGap, &logbrush, 0, NULL);\r
2456     logbrush.lbColor = highlightSquareColor;\r
2457     highlightPen =\r
2458       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2459                    lineGap, &logbrush, 0, NULL);\r
2460 \r
2461     logbrush.lbColor = premoveHighlightColor; \r
2462     premovePen =\r
2463       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2464                    lineGap, &logbrush, 0, NULL);\r
2465 \r
2466     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2467     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2468       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2469       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2470         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2471       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2472         BOARD_WIDTH * (squareSize + lineGap);\r
2473       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2474     }\r
2475     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2476       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
2477       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2478         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2479         lineGap / 2 + (i * (squareSize + lineGap));\r
2480       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2481         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
2482       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2483     }\r
2484   }\r
2485 \r
2486   /* [HGM] Licensing requirement */\r
2487 #ifdef GOTHIC\r
2488   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2489 #endif\r
2490 #ifdef FALCON\r
2491   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2492 #endif\r
2493   GothicPopUp( "", VariantNormal);\r
2494 \r
2495 \r
2496 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2497 \r
2498   /* Load piece bitmaps for this board size */\r
2499   for (i=0; i<=2; i++) {\r
2500     for (piece = WhitePawn;\r
2501          (int) piece < (int) BlackPawn;\r
2502          piece = (ChessSquare) ((int) piece + 1)) {\r
2503       if (pieceBitmap[i][piece] != NULL)\r
2504         DeleteObject(pieceBitmap[i][piece]);\r
2505     }\r
2506   }\r
2507 \r
2508   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2509   // Orthodox Chess pieces\r
2510   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2511   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2512   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2513   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2514   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2515   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2516   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2517   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2518   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2519   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2520   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2521   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2522   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2523   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2524   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2525   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2526     // in Shogi, Hijack the unused Queen for Lance\r
2527     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2528     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2529     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2530   } else {\r
2531     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2532     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2533     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2534   }\r
2535 \r
2536   if(squareSize <= 72 && squareSize >= 33) { \r
2537     /* A & C are available in most sizes now */\r
2538     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2539       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2540       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2541       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2542       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2543       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2544       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2545       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2546       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2547       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2548       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2549       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2550       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2551     } else { // Smirf-like\r
2552       if(gameInfo.variant == VariantSChess) {\r
2553         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2554         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2555         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2556       } else {\r
2557         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2558         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2559         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2560       }\r
2561     }\r
2562     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2563       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2564       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2565       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2566     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2567       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2568       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2569       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2570     } else { // WinBoard standard\r
2571       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2572       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2573       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2574     }\r
2575   }\r
2576 \r
2577 \r
2578   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2579     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2580     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2581     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2582     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2583     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2584     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2585     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2586     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2587     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2588     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2589     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2590     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2591     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2592     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2593     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2594     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2595     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2596     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2597     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2598     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2599     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2600     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2601     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2602     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2603     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2604     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2605     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2606     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2607     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2608     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2609 \r
2610     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2611       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2612       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2613       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2614       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2615       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2616       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2617       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2618       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2619       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2620       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2621       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2622       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2623     } else {\r
2624       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2625       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2626       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2627       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2628       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2629       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2630       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2631       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2632       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2633       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2634       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2635       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2636     }\r
2637 \r
2638   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2639     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2640     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2641     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2642     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2643     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2644     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2645     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2646     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2647     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2648     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2649     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2650     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2651     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2652     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2653   }\r
2654 \r
2655 \r
2656   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2657   /* special Shogi support in this size */\r
2658   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2659       for (piece = WhitePawn;\r
2660            (int) piece < (int) BlackPawn;\r
2661            piece = (ChessSquare) ((int) piece + 1)) {\r
2662         if (pieceBitmap[i][piece] != NULL)\r
2663           DeleteObject(pieceBitmap[i][piece]);\r
2664       }\r
2665     }\r
2666   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2667   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2668   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2669   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2670   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2671   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2672   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2673   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2674   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2675   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2676   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2677   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2678   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2679   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2680   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2681   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2682   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2683   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2684   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2685   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2686   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2687   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2688   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2689   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2690   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2691   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2692   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2693   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2694   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2695   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2696   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2697   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2698   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2699   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2700   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2701   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2702   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2703   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2704   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2705   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2706   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2707   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2708   minorSize = 0;\r
2709   }\r
2710 }\r
2711 \r
2712 HBITMAP\r
2713 PieceBitmap(ChessSquare p, int kind)\r
2714 {\r
2715   if ((int) p >= (int) BlackPawn)\r
2716     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2717 \r
2718   return pieceBitmap[kind][(int) p];\r
2719 }\r
2720 \r
2721 /***************************************************************/\r
2722 \r
2723 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2724 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2725 /*\r
2726 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2727 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2728 */\r
2729 \r
2730 VOID\r
2731 SquareToPos(int row, int column, int * x, int * y)\r
2732 {\r
2733   if (flipView) {\r
2734     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
2735     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2736   } else {\r
2737     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2738     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
2739   }\r
2740 }\r
2741 \r
2742 VOID\r
2743 DrawCoordsOnDC(HDC hdc)\r
2744 {\r
2745   static char files[] = "0123456789012345678901221098765432109876543210";\r
2746   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2747   char str[2] = { NULLCHAR, NULLCHAR };\r
2748   int oldMode, oldAlign, x, y, start, i;\r
2749   HFONT oldFont;\r
2750   HBRUSH oldBrush;\r
2751 \r
2752   if (!appData.showCoords)\r
2753     return;\r
2754 \r
2755   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2756 \r
2757   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2758   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2759   oldAlign = GetTextAlign(hdc);\r
2760   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2761 \r
2762   y = boardRect.top + lineGap;\r
2763   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2764 \r
2765   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2766   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2767     str[0] = files[start + i];\r
2768     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2769     y += squareSize + lineGap;\r
2770   }\r
2771 \r
2772   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2773 \r
2774   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2775   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2776     str[0] = ranks[start + i];\r
2777     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2778     x += squareSize + lineGap;\r
2779   }    \r
2780 \r
2781   SelectObject(hdc, oldBrush);\r
2782   SetBkMode(hdc, oldMode);\r
2783   SetTextAlign(hdc, oldAlign);\r
2784   SelectObject(hdc, oldFont);\r
2785 }\r
2786 \r
2787 VOID\r
2788 DrawGridOnDC(HDC hdc)\r
2789 {\r
2790   HPEN oldPen;\r
2791  \r
2792   if (lineGap != 0) {\r
2793     oldPen = SelectObject(hdc, gridPen);\r
2794     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2795     SelectObject(hdc, oldPen);\r
2796   }\r
2797 }\r
2798 \r
2799 #define HIGHLIGHT_PEN 0\r
2800 #define PREMOVE_PEN   1\r
2801 \r
2802 VOID\r
2803 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2804 {\r
2805   int x1, y1;\r
2806   HPEN oldPen, hPen;\r
2807   if (lineGap == 0) return;\r
2808   if (flipView) {\r
2809     x1 = boardRect.left +\r
2810       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
2811     y1 = boardRect.top +\r
2812       lineGap/2 + y * (squareSize + lineGap);\r
2813   } else {\r
2814     x1 = boardRect.left +\r
2815       lineGap/2 + x * (squareSize + lineGap);\r
2816     y1 = boardRect.top +\r
2817       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
2818   }\r
2819   hPen = pen ? premovePen : highlightPen;\r
2820   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2821   MoveToEx(hdc, x1, y1, NULL);\r
2822   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2823   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2824   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2825   LineTo(hdc, x1, y1);\r
2826   SelectObject(hdc, oldPen);\r
2827 }\r
2828 \r
2829 VOID\r
2830 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2831 {\r
2832   int i;\r
2833   for (i=0; i<2; i++) {\r
2834     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2835       DrawHighlightOnDC(hdc, TRUE,\r
2836                         h->sq[i].x, h->sq[i].y,\r
2837                         pen);\r
2838   }\r
2839 }\r
2840 \r
2841 /* Note: sqcolor is used only in monoMode */\r
2842 /* Note that this code is largely duplicated in woptions.c,\r
2843    function DrawSampleSquare, so that needs to be updated too */\r
2844 VOID\r
2845 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2846 {\r
2847   HBITMAP oldBitmap;\r
2848   HBRUSH oldBrush;\r
2849   int tmpSize;\r
2850 \r
2851   if (appData.blindfold) return;\r
2852 \r
2853   /* [AS] Use font-based pieces if needed */\r
2854   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2855     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2856     CreatePiecesFromFont();\r
2857 \r
2858     if( fontBitmapSquareSize == squareSize ) {\r
2859         int index = TranslatePieceToFontPiece(piece);\r
2860 \r
2861         SelectObject( tmphdc, hPieceMask[ index ] );\r
2862 \r
2863       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2864         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2865       else\r
2866         BitBlt( hdc,\r
2867             x, y,\r
2868             squareSize, squareSize,\r
2869             tmphdc,\r
2870             0, 0,\r
2871             SRCAND );\r
2872 \r
2873         SelectObject( tmphdc, hPieceFace[ index ] );\r
2874 \r
2875       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2876         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2877       else\r
2878         BitBlt( hdc,\r
2879             x, y,\r
2880             squareSize, squareSize,\r
2881             tmphdc,\r
2882             0, 0,\r
2883             SRCPAINT );\r
2884 \r
2885         return;\r
2886     }\r
2887   }\r
2888 \r
2889   if (appData.monoMode) {\r
2890     SelectObject(tmphdc, PieceBitmap(piece, \r
2891       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2892     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2893            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2894   } else {\r
2895     tmpSize = squareSize;\r
2896     if(minorSize &&\r
2897         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2898          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2899       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2900       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2901       x += (squareSize - minorSize)>>1;\r
2902       y += squareSize - minorSize - 2;\r
2903       tmpSize = minorSize;\r
2904     }\r
2905     if (color || appData.allWhite ) {\r
2906       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2907       if( color )\r
2908               oldBrush = SelectObject(hdc, whitePieceBrush);\r
2909       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2910       if(appData.upsideDown && color==flipView)\r
2911         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2912       else\r
2913         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2914       /* Use black for outline of white pieces */\r
2915       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2916       if(appData.upsideDown && color==flipView)\r
2917         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2918       else\r
2919         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2920     } else {\r
2921       /* Use square color for details of black pieces */\r
2922       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2923       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2924       if(appData.upsideDown && !flipView)\r
2925         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2926       else\r
2927         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2928     }\r
2929     SelectObject(hdc, oldBrush);\r
2930     SelectObject(tmphdc, oldBitmap);\r
2931   }\r
2932 }\r
2933 \r
2934 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
2935 int GetBackTextureMode( int algo )\r
2936 {\r
2937     int result = BACK_TEXTURE_MODE_DISABLED;\r
2938 \r
2939     switch( algo ) \r
2940     {\r
2941         case BACK_TEXTURE_MODE_PLAIN:\r
2942             result = 1; /* Always use identity map */\r
2943             break;\r
2944         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
2945             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
2946             break;\r
2947     }\r
2948 \r
2949     return result;\r
2950 }\r
2951 \r
2952 /* \r
2953     [AS] Compute and save texture drawing info, otherwise we may not be able\r
2954     to handle redraws cleanly (as random numbers would always be different).\r
2955 */\r
2956 VOID RebuildTextureSquareInfo()\r
2957 {\r
2958     BITMAP bi;\r
2959     int lite_w = 0;\r
2960     int lite_h = 0;\r
2961     int dark_w = 0;\r
2962     int dark_h = 0;\r
2963     int row;\r
2964     int col;\r
2965 \r
2966     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
2967 \r
2968     if( liteBackTexture != NULL ) {\r
2969         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2970             lite_w = bi.bmWidth;\r
2971             lite_h = bi.bmHeight;\r
2972         }\r
2973     }\r
2974 \r
2975     if( darkBackTexture != NULL ) {\r
2976         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
2977             dark_w = bi.bmWidth;\r
2978             dark_h = bi.bmHeight;\r
2979         }\r
2980     }\r
2981 \r
2982     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
2983         for( col=0; col<BOARD_WIDTH; col++ ) {\r
2984             if( (col + row) & 1 ) {\r
2985                 /* Lite square */\r
2986                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
2987                   if( lite_w >= squareSize*BOARD_WIDTH )\r
2988                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
2989                   else\r
2990                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
2991                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
2992                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
2993                   else\r
2994                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
2995                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
2996                 }\r
2997             }\r
2998             else {\r
2999                 /* Dark square */\r
3000                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3001                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3002                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3003                   else\r
3004                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3005                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3006                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3007                   else\r
3008                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3009                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3010                 }\r
3011             }\r
3012         }\r
3013     }\r
3014 }\r
3015 \r
3016 /* [AS] Arrow highlighting support */\r
3017 \r
3018 static double A_WIDTH = 5; /* Width of arrow body */\r
3019 \r
3020 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3021 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3022 \r
3023 static double Sqr( double x )\r
3024 {\r
3025     return x*x;\r
3026 }\r
3027 \r
3028 static int Round( double x )\r
3029 {\r
3030     return (int) (x + 0.5);\r
3031 }\r
3032 \r
3033 /* Draw an arrow between two points using current settings */\r
3034 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3035 {\r
3036     POINT arrow[7];\r
3037     double dx, dy, j, k, x, y;\r
3038 \r
3039     if( d_x == s_x ) {\r
3040         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3041 \r
3042         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3043         arrow[0].y = s_y;\r
3044 \r
3045         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3046         arrow[1].y = d_y - h;\r
3047 \r
3048         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3049         arrow[2].y = d_y - h;\r
3050 \r
3051         arrow[3].x = d_x;\r
3052         arrow[3].y = d_y;\r
3053 \r
3054         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3055         arrow[5].y = d_y - h;\r
3056 \r
3057         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3058         arrow[4].y = d_y - h;\r
3059 \r
3060         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3061         arrow[6].y = s_y;\r
3062     }\r
3063     else if( d_y == s_y ) {\r
3064         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3065 \r
3066         arrow[0].x = s_x;\r
3067         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3068 \r
3069         arrow[1].x = d_x - w;\r
3070         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3071 \r
3072         arrow[2].x = d_x - w;\r
3073         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3074 \r
3075         arrow[3].x = d_x;\r
3076         arrow[3].y = d_y;\r
3077 \r
3078         arrow[5].x = d_x - w;\r
3079         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3080 \r
3081         arrow[4].x = d_x - w;\r
3082         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3083 \r
3084         arrow[6].x = s_x;\r
3085         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3086     }\r
3087     else {\r
3088         /* [AS] Needed a lot of paper for this! :-) */\r
3089         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3090         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3091   \r
3092         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3093 \r
3094         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3095 \r
3096         x = s_x;\r
3097         y = s_y;\r
3098 \r
3099         arrow[0].x = Round(x - j);\r
3100         arrow[0].y = Round(y + j*dx);\r
3101 \r
3102         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3103         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3104 \r
3105         if( d_x > s_x ) {\r
3106             x = (double) d_x - k;\r
3107             y = (double) d_y - k*dy;\r
3108         }\r
3109         else {\r
3110             x = (double) d_x + k;\r
3111             y = (double) d_y + k*dy;\r
3112         }\r
3113 \r
3114         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3115 \r
3116         arrow[6].x = Round(x - j);\r
3117         arrow[6].y = Round(y + j*dx);\r
3118 \r
3119         arrow[2].x = Round(arrow[6].x + 2*j);\r
3120         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3121 \r
3122         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3123         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3124 \r
3125         arrow[4].x = d_x;\r
3126         arrow[4].y = d_y;\r
3127 \r
3128         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3129         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3130     }\r
3131 \r
3132     Polygon( hdc, arrow, 7 );\r
3133 }\r
3134 \r
3135 /* [AS] Draw an arrow between two squares */\r
3136 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3137 {\r
3138     int s_x, s_y, d_x, d_y;\r
3139     HPEN hpen;\r
3140     HPEN holdpen;\r
3141     HBRUSH hbrush;\r
3142     HBRUSH holdbrush;\r
3143     LOGBRUSH stLB;\r
3144 \r
3145     if( s_col == d_col && s_row == d_row ) {\r
3146         return;\r
3147     }\r
3148 \r
3149     /* Get source and destination points */\r
3150     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3151     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3152 \r
3153     if( d_y > s_y ) {\r
3154         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3155     }\r
3156     else if( d_y < s_y ) {\r
3157         d_y += squareSize / 2 + squareSize / 4;\r
3158     }\r
3159     else {\r
3160         d_y += squareSize / 2;\r
3161     }\r
3162 \r
3163     if( d_x > s_x ) {\r
3164         d_x += squareSize / 2 - squareSize / 4;\r
3165     }\r
3166     else if( d_x < s_x ) {\r
3167         d_x += squareSize / 2 + squareSize / 4;\r
3168     }\r
3169     else {\r
3170         d_x += squareSize / 2;\r
3171     }\r
3172 \r
3173     s_x += squareSize / 2;\r
3174     s_y += squareSize / 2;\r
3175 \r
3176     /* Adjust width */\r
3177     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3178 \r
3179     /* Draw */\r
3180     stLB.lbStyle = BS_SOLID;\r
3181     stLB.lbColor = appData.highlightArrowColor;\r
3182     stLB.lbHatch = 0;\r
3183 \r
3184     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3185     holdpen = SelectObject( hdc, hpen );\r
3186     hbrush = CreateBrushIndirect( &stLB );\r
3187     holdbrush = SelectObject( hdc, hbrush );\r
3188 \r
3189     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3190 \r
3191     SelectObject( hdc, holdpen );\r
3192     SelectObject( hdc, holdbrush );\r
3193     DeleteObject( hpen );\r
3194     DeleteObject( hbrush );\r
3195 }\r
3196 \r
3197 BOOL HasHighlightInfo()\r
3198 {\r
3199     BOOL result = FALSE;\r
3200 \r
3201     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3202         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3203     {\r
3204         result = TRUE;\r
3205     }\r
3206 \r
3207     return result;\r
3208 }\r
3209 \r
3210 BOOL IsDrawArrowEnabled()\r
3211 {\r
3212     BOOL result = FALSE;\r
3213 \r
3214     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3215         result = TRUE;\r
3216     }\r
3217 \r
3218     return result;\r
3219 }\r
3220 \r
3221 VOID DrawArrowHighlight( HDC hdc )\r
3222 {\r
3223     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3224         DrawArrowBetweenSquares( hdc,\r
3225             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3226             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3227     }\r
3228 }\r
3229 \r
3230 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3231 {\r
3232     HRGN result = NULL;\r
3233 \r
3234     if( HasHighlightInfo() ) {\r
3235         int x1, y1, x2, y2;\r
3236         int sx, sy, dx, dy;\r
3237 \r
3238         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3239         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3240 \r
3241         sx = MIN( x1, x2 );\r
3242         sy = MIN( y1, y2 );\r
3243         dx = MAX( x1, x2 ) + squareSize;\r
3244         dy = MAX( y1, y2 ) + squareSize;\r
3245 \r
3246         result = CreateRectRgn( sx, sy, dx, dy );\r
3247     }\r
3248 \r
3249     return result;\r
3250 }\r
3251 \r
3252 /*\r
3253     Warning: this function modifies the behavior of several other functions. \r
3254     \r
3255     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3256     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3257     repaint is scattered all over the place, which is not good for features such as\r
3258     "arrow highlighting" that require a full repaint of the board.\r
3259 \r
3260     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3261     user interaction, when speed is not so important) but especially to avoid errors\r
3262     in the displayed graphics.\r
3263 \r
3264     In such patched places, I always try refer to this function so there is a single\r
3265     place to maintain knowledge.\r
3266     \r
3267     To restore the original behavior, just return FALSE unconditionally.\r
3268 */\r
3269 BOOL IsFullRepaintPreferrable()\r
3270 {\r
3271     BOOL result = FALSE;\r
3272 \r
3273     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3274         /* Arrow may appear on the board */\r
3275         result = TRUE;\r
3276     }\r
3277 \r
3278     return result;\r
3279 }\r
3280 \r
3281 /* \r
3282     This function is called by DrawPosition to know whether a full repaint must\r
3283     be forced or not.\r
3284 \r
3285     Only DrawPosition may directly call this function, which makes use of \r
3286     some state information. Other function should call DrawPosition specifying \r
3287     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3288 */\r
3289 BOOL DrawPositionNeedsFullRepaint()\r
3290 {\r
3291     BOOL result = FALSE;\r
3292 \r
3293     /* \r
3294         Probably a slightly better policy would be to trigger a full repaint\r
3295         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3296         but animation is fast enough that it's difficult to notice.\r
3297     */\r
3298     if( animInfo.piece == EmptySquare ) {\r
3299         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3300             result = TRUE;\r
3301         }\r
3302     }\r
3303 \r
3304     return result;\r
3305 }\r
3306 \r
3307 VOID\r
3308 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3309 {\r
3310   int row, column, x, y, square_color, piece_color;\r
3311   ChessSquare piece;\r
3312   HBRUSH oldBrush;\r
3313   HDC texture_hdc = NULL;\r
3314 \r
3315   /* [AS] Initialize background textures if needed */\r
3316   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3317       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3318       if( backTextureSquareSize != squareSize \r
3319        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3320           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3321           backTextureSquareSize = squareSize;\r
3322           RebuildTextureSquareInfo();\r
3323       }\r
3324 \r
3325       texture_hdc = CreateCompatibleDC( hdc );\r
3326   }\r
3327 \r
3328   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3329     for (column = 0; column < BOARD_WIDTH; column++) {\r
3330   \r
3331       SquareToPos(row, column, &x, &y);\r
3332 \r
3333       piece = board[row][column];\r
3334 \r
3335       square_color = ((column + row) % 2) == 1;\r
3336       if( gameInfo.variant == VariantXiangqi ) {\r
3337           square_color = !InPalace(row, column);\r
3338           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3339           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3340       }\r
3341       piece_color = (int) piece < (int) BlackPawn;\r
3342 \r
3343 \r
3344       /* [HGM] holdings file: light square or black */\r
3345       if(column == BOARD_LEFT-2) {\r
3346             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3347                 square_color = 1;\r
3348             else {\r
3349                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3350                 continue;\r
3351             }\r
3352       } else\r
3353       if(column == BOARD_RGHT + 1 ) {\r
3354             if( row < gameInfo.holdingsSize )\r
3355                 square_color = 1;\r
3356             else {\r
3357                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3358                 continue;\r
3359             }\r
3360       }\r
3361       if(column == BOARD_LEFT-1 ) /* left align */\r
3362             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3363       else if( column == BOARD_RGHT) /* right align */\r
3364             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3365       else\r
3366       if (appData.monoMode) {\r
3367         if (piece == EmptySquare) {\r
3368           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3369                  square_color ? WHITENESS : BLACKNESS);\r
3370         } else {\r
3371           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3372         }\r
3373       } \r
3374       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3375           /* [AS] Draw the square using a texture bitmap */\r
3376           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3377           int r = row, c = column; // [HGM] do not flip board in flipView\r
3378           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3379 \r
3380           DrawTile( x, y, \r
3381               squareSize, squareSize, \r
3382               hdc, \r
3383               texture_hdc,\r
3384               backTextureSquareInfo[r][c].mode,\r
3385               backTextureSquareInfo[r][c].x,\r
3386               backTextureSquareInfo[r][c].y );\r
3387 \r
3388           SelectObject( texture_hdc, hbm );\r
3389 \r
3390           if (piece != EmptySquare) {\r
3391               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3392           }\r
3393       }\r
3394       else {\r
3395         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3396 \r
3397         oldBrush = SelectObject(hdc, brush );\r
3398         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3399         SelectObject(hdc, oldBrush);\r
3400         if (piece != EmptySquare)\r
3401           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3402       }\r
3403     }\r
3404   }\r
3405 \r
3406   if( texture_hdc != NULL ) {\r
3407     DeleteDC( texture_hdc );\r
3408   }\r
3409 }\r
3410 \r
3411 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3412 void fputDW(FILE *f, int x)\r
3413 {\r
3414         fputc(x     & 255, f);\r
3415         fputc(x>>8  & 255, f);\r
3416         fputc(x>>16 & 255, f);\r
3417         fputc(x>>24 & 255, f);\r
3418 }\r
3419 \r
3420 #define MAX_CLIPS 200   /* more than enough */\r
3421 \r
3422 VOID\r
3423 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3424 {\r
3425 //  HBITMAP bufferBitmap;\r
3426   BITMAP bi;\r
3427 //  RECT Rect;\r
3428   HDC tmphdc;\r
3429   HBITMAP hbm;\r
3430   int w = 100, h = 50;\r
3431 \r
3432   if(logo == NULL) {\r
3433     if(!logoHeight) return;\r
3434     FillRect( hdc, &logoRect, whitePieceBrush );\r
3435   }\r
3436 //  GetClientRect(hwndMain, &Rect);\r
3437 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3438 //                                      Rect.bottom-Rect.top+1);\r
3439   tmphdc = CreateCompatibleDC(hdc);\r
3440   hbm = SelectObject(tmphdc, logo);\r
3441   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3442             w = bi.bmWidth;\r
3443             h = bi.bmHeight;\r
3444   }\r
3445   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3446                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3447   SelectObject(tmphdc, hbm);\r
3448   DeleteDC(tmphdc);\r
3449 }\r
3450 \r
3451 VOID\r
3452 DisplayLogos()\r
3453 {\r
3454   if(logoHeight) {\r
3455         HDC hdc = GetDC(hwndMain);\r
3456         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3457         if(appData.autoLogo) {\r
3458           \r
3459           switch(gameMode) { // pick logos based on game mode\r
3460             case IcsObserving:\r
3461                 whiteLogo = second.programLogo; // ICS logo\r
3462                 blackLogo = second.programLogo;\r
3463             default:\r
3464                 break;\r
3465             case IcsPlayingWhite:\r
3466                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3467                 blackLogo = second.programLogo; // ICS logo\r
3468                 break;\r
3469             case IcsPlayingBlack:\r
3470                 whiteLogo = second.programLogo; // ICS logo\r
3471                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3472                 break;\r
3473             case TwoMachinesPlay:\r
3474                 if(first.twoMachinesColor[0] == 'b') {\r
3475                     whiteLogo = second.programLogo;\r
3476                     blackLogo = first.programLogo;\r
3477                 }\r
3478                 break;\r
3479             case MachinePlaysWhite:\r
3480                 blackLogo = userLogo;\r
3481                 break;\r
3482             case MachinePlaysBlack:\r
3483                 whiteLogo = userLogo;\r
3484                 blackLogo = first.programLogo;\r
3485           }\r
3486         }\r
3487         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3488         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3489         ReleaseDC(hwndMain, hdc);\r
3490   }\r
3491 }\r
3492 \r
3493 void\r
3494 UpdateLogos(int display)\r
3495 { // called after loading new engine(s), in tourney or from menu\r
3496   LoadLogo(&first, 0, FALSE);\r
3497   LoadLogo(&second, 1, appData.icsActive);\r
3498   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3499   if(display) DisplayLogos();\r
3500 }\r
3501 \r
3502 static HDC hdcSeek;\r
3503 \r
3504 // [HGM] seekgraph\r
3505 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3506 {\r
3507     POINT stPt;\r
3508     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3509     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3510     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3511     SelectObject( hdcSeek, hp );\r
3512 }\r
3513 \r
3514 // front-end wrapper for drawing functions to do rectangles\r
3515 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3516 {\r
3517     HPEN hp;\r
3518     RECT rc;\r
3519 \r
3520     if (hdcSeek == NULL) {\r
3521     hdcSeek = GetDC(hwndMain);\r
3522       if (!appData.monoMode) {\r
3523         SelectPalette(hdcSeek, hPal, FALSE);\r
3524         RealizePalette(hdcSeek);\r
3525       }\r
3526     }\r
3527     hp = SelectObject( hdcSeek, gridPen );\r
3528     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3529     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3530     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3531     SelectObject( hdcSeek, hp );\r
3532 }\r
3533 \r
3534 // front-end wrapper for putting text in graph\r
3535 void DrawSeekText(char *buf, int x, int y)\r
3536 {\r
3537         SIZE stSize;\r
3538         SetBkMode( hdcSeek, TRANSPARENT );\r
3539         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3540         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3541 }\r
3542 \r
3543 void DrawSeekDot(int x, int y, int color)\r
3544 {\r
3545         int square = color & 0x80;\r
3546         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3547                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3548         color &= 0x7F;\r
3549         if(square)\r
3550             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3551                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3552         else\r
3553             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3554                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3555             SelectObject(hdcSeek, oldBrush);\r
3556 }\r
3557 \r
3558 VOID\r
3559 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3560 {\r
3561   static Board lastReq[2], lastDrawn[2];\r
3562   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3563   static int lastDrawnFlipView = 0;\r
3564   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3565   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3566   HDC tmphdc;\r
3567   HDC hdcmem;\r
3568   HBITMAP bufferBitmap;\r
3569   HBITMAP oldBitmap;\r
3570   RECT Rect;\r
3571   HRGN clips[MAX_CLIPS];\r
3572   ChessSquare dragged_piece = EmptySquare;\r
3573   int nr = twoBoards*partnerUp;\r
3574 \r
3575   /* I'm undecided on this - this function figures out whether a full\r
3576    * repaint is necessary on its own, so there's no real reason to have the\r
3577    * caller tell it that.  I think this can safely be set to FALSE - but\r
3578    * if we trust the callers not to request full repaints unnessesarily, then\r
3579    * we could skip some clipping work.  In other words, only request a full\r
3580    * redraw when the majority of pieces have changed positions (ie. flip, \r
3581    * gamestart and similar)  --Hawk\r
3582    */\r
3583   Boolean fullrepaint = repaint;\r
3584 \r
3585   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3586 \r
3587   if( DrawPositionNeedsFullRepaint() ) {\r
3588       fullrepaint = TRUE;\r
3589   }\r
3590 \r
3591   if (board == NULL) {\r
3592     if (!lastReqValid[nr]) {\r
3593       return;\r
3594     }\r
3595     board = lastReq[nr];\r
3596   } else {\r
3597     CopyBoard(lastReq[nr], board);\r
3598     lastReqValid[nr] = 1;\r
3599   }\r
3600 \r
3601   if (doingSizing) {\r
3602     return;\r
3603   }\r
3604 \r
3605   if (IsIconic(hwndMain)) {\r
3606     return;\r
3607   }\r
3608 \r
3609   if (hdc == NULL) {\r
3610     hdc = GetDC(hwndMain);\r
3611     if (!appData.monoMode) {\r
3612       SelectPalette(hdc, hPal, FALSE);\r
3613       RealizePalette(hdc);\r
3614     }\r
3615     releaseDC = TRUE;\r
3616   } else {\r
3617     releaseDC = FALSE;\r
3618   }\r
3619 \r
3620   /* Create some work-DCs */\r
3621   hdcmem = CreateCompatibleDC(hdc);\r
3622   tmphdc = CreateCompatibleDC(hdc);\r
3623 \r
3624   /* If dragging is in progress, we temporarely remove the piece */\r
3625   /* [HGM] or temporarily decrease count if stacked              */\r
3626   /*       !! Moved to before board compare !!                   */\r
3627   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3628     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3629     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3630             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3631         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3632     } else \r
3633     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3634             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3635         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3636     } else \r
3637         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3638   }\r
3639 \r
3640   /* Figure out which squares need updating by comparing the \r
3641    * newest board with the last drawn board and checking if\r
3642    * flipping has changed.\r
3643    */\r
3644   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3645     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3646       for (column = 0; column < BOARD_WIDTH; column++) {\r
3647         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3648           SquareToPos(row, column, &x, &y);\r
3649           clips[num_clips++] =\r
3650             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3651         }\r
3652       }\r
3653     }\r
3654    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3655     for (i=0; i<2; i++) {\r
3656       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3657           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3658         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3659             lastDrawnHighlight.sq[i].y >= 0) {\r
3660           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3661                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3662           clips[num_clips++] =\r
3663             CreateRectRgn(x - lineGap, y - lineGap, \r
3664                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3665         }\r
3666         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3667           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3668           clips[num_clips++] =\r
3669             CreateRectRgn(x - lineGap, y - lineGap, \r
3670                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3671         }\r
3672       }\r
3673     }\r
3674     for (i=0; i<2; i++) {\r
3675       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3676           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3677         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3678             lastDrawnPremove.sq[i].y >= 0) {\r
3679           SquareToPos(lastDrawnPremove.sq[i].y,\r
3680                       lastDrawnPremove.sq[i].x, &x, &y);\r
3681           clips[num_clips++] =\r
3682             CreateRectRgn(x - lineGap, y - lineGap, \r
3683                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3684         }\r
3685         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3686             premoveHighlightInfo.sq[i].y >= 0) {\r
3687           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3688                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3689           clips[num_clips++] =\r
3690             CreateRectRgn(x - lineGap, y - lineGap, \r
3691                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3692         }\r
3693       }\r
3694     }\r
3695    } else { // nr == 1\r
3696         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3697         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3698         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3699         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3700       for (i=0; i<2; i++) {\r
3701         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3702             partnerHighlightInfo.sq[i].y >= 0) {\r
3703           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3704                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3705           clips[num_clips++] =\r
3706             CreateRectRgn(x - lineGap, y - lineGap, \r
3707                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3708         }\r
3709         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3710             oldPartnerHighlight.sq[i].y >= 0) {\r
3711           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3712                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3713           clips[num_clips++] =\r
3714             CreateRectRgn(x - lineGap, y - lineGap, \r
3715                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3716         }\r
3717       }\r
3718    }\r
3719   } else {\r
3720     fullrepaint = TRUE;\r
3721   }\r
3722 \r
3723   /* Create a buffer bitmap - this is the actual bitmap\r
3724    * being written to.  When all the work is done, we can\r
3725    * copy it to the real DC (the screen).  This avoids\r
3726    * the problems with flickering.\r
3727    */\r
3728   GetClientRect(hwndMain, &Rect);\r
3729   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3730                                         Rect.bottom-Rect.top+1);\r
3731   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3732   if (!appData.monoMode) {\r
3733     SelectPalette(hdcmem, hPal, FALSE);\r
3734   }\r
3735 \r
3736   /* Create clips for dragging */\r
3737   if (!fullrepaint) {\r
3738     if (dragInfo.from.x >= 0) {\r
3739       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3740       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3741     }\r
3742     if (dragInfo.start.x >= 0) {\r
3743       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3744       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3745     }\r
3746     if (dragInfo.pos.x >= 0) {\r
3747       x = dragInfo.pos.x - squareSize / 2;\r
3748       y = dragInfo.pos.y - squareSize / 2;\r
3749       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3750     }\r
3751     if (dragInfo.lastpos.x >= 0) {\r
3752       x = dragInfo.lastpos.x - squareSize / 2;\r
3753       y = dragInfo.lastpos.y - squareSize / 2;\r
3754       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3755     }\r
3756   }\r
3757 \r
3758   /* Are we animating a move?  \r
3759    * If so, \r
3760    *   - remove the piece from the board (temporarely)\r
3761    *   - calculate the clipping region\r
3762    */\r
3763   if (!fullrepaint) {\r
3764     if (animInfo.piece != EmptySquare) {\r
3765       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3766       x = boardRect.left + animInfo.lastpos.x;\r
3767       y = boardRect.top + animInfo.lastpos.y;\r
3768       x2 = boardRect.left + animInfo.pos.x;\r
3769       y2 = boardRect.top + animInfo.pos.y;\r
3770       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3771       /* Slight kludge.  The real problem is that after AnimateMove is\r
3772          done, the position on the screen does not match lastDrawn.\r
3773          This currently causes trouble only on e.p. captures in\r
3774          atomic, where the piece moves to an empty square and then\r
3775          explodes.  The old and new positions both had an empty square\r
3776          at the destination, but animation has drawn a piece there and\r
3777          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3778       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3779     }\r
3780   }\r
3781 \r
3782   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3783   if (num_clips == 0)\r
3784     fullrepaint = TRUE;\r
3785 \r
3786   /* Set clipping on the memory DC */\r
3787   if (!fullrepaint) {\r
3788     SelectClipRgn(hdcmem, clips[0]);\r
3789     for (x = 1; x < num_clips; x++) {\r
3790       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3791         abort();  // this should never ever happen!\r
3792     }\r
3793   }\r
3794 \r
3795   /* Do all the drawing to the memory DC */\r
3796   if(explodeInfo.radius) { // [HGM] atomic\r
3797         HBRUSH oldBrush;\r
3798         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3799         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3800         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3801         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3802         x += squareSize/2;\r
3803         y += squareSize/2;\r
3804         if(!fullrepaint) {\r
3805           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3806           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3807         }\r
3808         DrawGridOnDC(hdcmem);\r
3809         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3810         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3811         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3812         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3813         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3814         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3815         SelectObject(hdcmem, oldBrush);\r
3816   } else {\r
3817     DrawGridOnDC(hdcmem);\r
3818     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3819         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3820         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3821     } else {\r
3822         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3823         oldPartnerHighlight = partnerHighlightInfo;\r
3824     }\r
3825     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3826   }\r
3827   if(nr == 0) // [HGM] dual: markers only on left board\r
3828   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3829     for (column = 0; column < BOARD_WIDTH; column++) {\r
3830         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3831             HBRUSH oldBrush = SelectObject(hdcmem, \r
3832                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3833             SquareToPos(row, column, &x, &y);\r
3834             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3835                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3836             SelectObject(hdcmem, oldBrush);\r
3837         }\r
3838     }\r
3839   }\r
3840 \r
3841   if( appData.highlightMoveWithArrow ) {\r
3842     DrawArrowHighlight(hdcmem);\r
3843   }\r
3844 \r
3845   DrawCoordsOnDC(hdcmem);\r
3846 \r
3847   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3848                  /* to make sure lastDrawn contains what is actually drawn */\r
3849 \r
3850   /* Put the dragged piece back into place and draw it (out of place!) */\r
3851     if (dragged_piece != EmptySquare) {\r
3852     /* [HGM] or restack */\r
3853     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3854                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3855     else\r
3856     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3857                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3858     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3859     x = dragInfo.pos.x - squareSize / 2;\r
3860     y = dragInfo.pos.y - squareSize / 2;\r
3861     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3862                   ((int) dragInfo.piece < (int) BlackPawn), \r
3863                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3864   }   \r
3865   \r
3866   /* Put the animated piece back into place and draw it */\r
3867   if (animInfo.piece != EmptySquare) {\r
3868     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3869     x = boardRect.left + animInfo.pos.x;\r
3870     y = boardRect.top + animInfo.pos.y;\r
3871     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3872                   ((int) animInfo.piece < (int) BlackPawn),\r
3873                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3874   }\r
3875 \r
3876   /* Release the bufferBitmap by selecting in the old bitmap \r
3877    * and delete the memory DC\r
3878    */\r
3879   SelectObject(hdcmem, oldBitmap);\r
3880   DeleteDC(hdcmem);\r
3881 \r
3882   /* Set clipping on the target DC */\r
3883   if (!fullrepaint) {\r
3884     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3885         RECT rect;\r
3886         GetRgnBox(clips[x], &rect);\r
3887         DeleteObject(clips[x]);\r
3888         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3889                           rect.right + wpMain.width/2, rect.bottom);\r
3890     }\r
3891     SelectClipRgn(hdc, clips[0]);\r
3892     for (x = 1; x < num_clips; x++) {\r
3893       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3894         abort();   // this should never ever happen!\r
3895     } \r
3896   }\r
3897 \r
3898   /* Copy the new bitmap onto the screen in one go.\r
3899    * This way we avoid any flickering\r
3900    */\r
3901   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3902   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3903          boardRect.right - boardRect.left,\r
3904          boardRect.bottom - boardRect.top,\r
3905          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3906   if(saveDiagFlag) { \r
3907     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3908     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3909 \r
3910     GetObject(bufferBitmap, sizeof(b), &b);\r
3911     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3912         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3913         bih.biWidth = b.bmWidth;\r
3914         bih.biHeight = b.bmHeight;\r
3915         bih.biPlanes = 1;\r
3916         bih.biBitCount = b.bmBitsPixel;\r
3917         bih.biCompression = 0;\r
3918         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3919         bih.biXPelsPerMeter = 0;\r
3920         bih.biYPelsPerMeter = 0;\r
3921         bih.biClrUsed = 0;\r
3922         bih.biClrImportant = 0;\r
3923 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3924 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3925         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3926 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3927 \r
3928         wb = b.bmWidthBytes;\r
3929         // count colors\r
3930         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3931                 int k = ((int*) pData)[i];\r
3932                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3933                 if(j >= 16) break;\r
3934                 color[j] = k;\r
3935                 if(j >= nrColors) nrColors = j+1;\r
3936         }\r
3937         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3938                 INT p = 0;\r
3939                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3940                     for(w=0; w<(wb>>2); w+=2) {\r
3941                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3942                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3943                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3944                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3945                         pData[p++] = m | j<<4;\r
3946                     }\r
3947                     while(p&3) pData[p++] = 0;\r
3948                 }\r
3949                 fac = 3;\r
3950                 wb = ((wb+31)>>5)<<2;\r
3951         }\r
3952         // write BITMAPFILEHEADER\r
3953         fprintf(diagFile, "BM");\r
3954         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3955         fputDW(diagFile, 0);\r
3956         fputDW(diagFile, 0x36 + (fac?64:0));\r
3957         // write BITMAPINFOHEADER\r
3958         fputDW(diagFile, 40);\r
3959         fputDW(diagFile, b.bmWidth);\r
3960         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3961         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3962         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3963         fputDW(diagFile, 0);\r
3964         fputDW(diagFile, 0);\r
3965         fputDW(diagFile, 0);\r
3966         fputDW(diagFile, 0);\r
3967         fputDW(diagFile, 0);\r
3968         fputDW(diagFile, 0);\r
3969         // write color table\r
3970         if(fac)\r
3971         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3972         // write bitmap data\r
3973         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3974                 fputc(pData[i], diagFile);\r
3975         free(pData);\r
3976      }\r
3977   }\r
3978 \r
3979   SelectObject(tmphdc, oldBitmap);\r
3980 \r
3981   /* Massive cleanup */\r
3982   for (x = 0; x < num_clips; x++)\r
3983     DeleteObject(clips[x]);\r
3984 \r
3985   DeleteDC(tmphdc);\r
3986   DeleteObject(bufferBitmap);\r
3987 \r
3988   if (releaseDC) \r
3989     ReleaseDC(hwndMain, hdc);\r
3990   \r
3991   if (lastDrawnFlipView != flipView && nr == 0) {\r
3992     if (flipView)\r
3993       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3994     else\r
3995       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3996   }\r
3997 \r
3998 /*  CopyBoard(lastDrawn, board);*/\r
3999   lastDrawnHighlight = highlightInfo;\r
4000   lastDrawnPremove   = premoveHighlightInfo;\r
4001   lastDrawnFlipView = flipView;\r
4002   lastDrawnValid[nr] = 1;\r
4003 }\r
4004 \r
4005 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4006 int\r
4007 SaveDiagram(f)\r
4008      FILE *f;\r
4009 {\r
4010     saveDiagFlag = 1; diagFile = f;\r
4011     HDCDrawPosition(NULL, TRUE, NULL);\r
4012     saveDiagFlag = 0;\r
4013 \r
4014     fclose(f);\r
4015     return TRUE;\r
4016 }\r
4017 \r
4018 \r
4019 /*---------------------------------------------------------------------------*\\r
4020 | CLIENT PAINT PROCEDURE\r
4021 |   This is the main event-handler for the WM_PAINT message.\r
4022 |\r
4023 \*---------------------------------------------------------------------------*/\r
4024 VOID\r
4025 PaintProc(HWND hwnd)\r
4026 {\r
4027   HDC         hdc;\r
4028   PAINTSTRUCT ps;\r
4029   HFONT       oldFont;\r
4030 \r
4031   if((hdc = BeginPaint(hwnd, &ps))) {\r
4032     if (IsIconic(hwnd)) {\r
4033       DrawIcon(hdc, 2, 2, iconCurrent);\r
4034     } else {\r
4035       if (!appData.monoMode) {\r
4036         SelectPalette(hdc, hPal, FALSE);\r
4037         RealizePalette(hdc);\r
4038       }\r
4039       HDCDrawPosition(hdc, 1, NULL);\r
4040       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4041         flipView = !flipView; partnerUp = !partnerUp;\r
4042         HDCDrawPosition(hdc, 1, NULL);\r
4043         flipView = !flipView; partnerUp = !partnerUp;\r
4044       }\r
4045       oldFont =\r
4046         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4047       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4048                  ETO_CLIPPED|ETO_OPAQUE,\r
4049                  &messageRect, messageText, strlen(messageText), NULL);\r
4050       SelectObject(hdc, oldFont);\r
4051       DisplayBothClocks();\r
4052       DisplayLogos();\r
4053     }\r
4054     EndPaint(hwnd,&ps);\r
4055   }\r
4056 \r
4057   return;\r
4058 }\r
4059 \r
4060 \r
4061 /*\r
4062  * If the user selects on a border boundary, return -1; if off the board,\r
4063  *   return -2.  Otherwise map the event coordinate to the square.\r
4064  * The offset boardRect.left or boardRect.top must already have been\r
4065  *   subtracted from x.\r
4066  */\r
4067 int EventToSquare(x, limit)\r
4068      int x, limit;\r
4069 {\r
4070   if (x <= 0)\r
4071     return -2;\r
4072   if (x < lineGap)\r
4073     return -1;\r
4074   x -= lineGap;\r
4075   if ((x % (squareSize + lineGap)) >= squareSize)\r
4076     return -1;\r
4077   x /= (squareSize + lineGap);\r
4078     if (x >= limit)\r
4079     return -2;\r
4080   return x;\r
4081 }\r
4082 \r
4083 typedef struct {\r
4084   char piece;\r
4085   int command;\r
4086   char* name;\r
4087 } DropEnable;\r
4088 \r
4089 DropEnable dropEnables[] = {\r
4090   { 'P', DP_Pawn, N_("Pawn") },\r
4091   { 'N', DP_Knight, N_("Knight") },\r
4092   { 'B', DP_Bishop, N_("Bishop") },\r
4093   { 'R', DP_Rook, N_("Rook") },\r
4094   { 'Q', DP_Queen, N_("Queen") },\r
4095 };\r
4096 \r
4097 VOID\r
4098 SetupDropMenu(HMENU hmenu)\r
4099 {\r
4100   int i, count, enable;\r
4101   char *p;\r
4102   extern char white_holding[], black_holding[];\r
4103   char item[MSG_SIZ];\r
4104 \r
4105   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4106     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4107                dropEnables[i].piece);\r
4108     count = 0;\r
4109     while (p && *p++ == dropEnables[i].piece) count++;\r
4110       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4111     enable = count > 0 || !appData.testLegality\r
4112       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4113                       && !appData.icsActive);\r
4114     ModifyMenu(hmenu, dropEnables[i].command,\r
4115                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4116                dropEnables[i].command, item);\r
4117   }\r
4118 }\r
4119 \r
4120 void DragPieceBegin(int x, int y, Boolean instantly)\r
4121 {\r
4122       dragInfo.lastpos.x = boardRect.left + x;\r
4123       dragInfo.lastpos.y = boardRect.top + y;\r
4124       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4125       dragInfo.from.x = fromX;\r
4126       dragInfo.from.y = fromY;\r
4127       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4128       dragInfo.start = dragInfo.from;\r
4129       SetCapture(hwndMain);\r
4130 }\r
4131 \r
4132 void DragPieceEnd(int x, int y)\r
4133 {\r
4134     ReleaseCapture();\r
4135     dragInfo.start.x = dragInfo.start.y = -1;\r
4136     dragInfo.from = dragInfo.start;\r
4137     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4138 }\r
4139 \r
4140 void ChangeDragPiece(ChessSquare piece)\r
4141 {\r
4142     dragInfo.piece = piece;\r
4143 }\r
4144 \r
4145 /* Event handler for mouse messages */\r
4146 VOID\r
4147 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4148 {\r
4149   int x, y, menuNr;\r
4150   POINT pt;\r
4151   static int recursive = 0;\r
4152   HMENU hmenu;\r
4153   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4154 \r
4155   if (recursive) {\r
4156     if (message == WM_MBUTTONUP) {\r
4157       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4158          to the middle button: we simulate pressing the left button too!\r
4159          */\r
4160       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4161       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4162     }\r
4163     return;\r
4164   }\r
4165   recursive++;\r
4166   \r
4167   pt.x = LOWORD(lParam);\r
4168   pt.y = HIWORD(lParam);\r
4169   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4170   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4171   if (!flipView && y >= 0) {\r
4172     y = BOARD_HEIGHT - 1 - y;\r
4173   }\r
4174   if (flipView && x >= 0) {\r
4175     x = BOARD_WIDTH - 1 - x;\r
4176   }\r
4177 \r
4178   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4179 \r
4180   switch (message) {\r
4181   case WM_LBUTTONDOWN:\r
4182       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4183         ClockClick(flipClock); break;\r
4184       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4185         ClockClick(!flipClock); break;\r
4186       }\r
4187       dragInfo.start.x = dragInfo.start.y = -1;\r
4188       dragInfo.from = dragInfo.start;\r
4189     if(fromX == -1 && frozen) { // not sure where this is for\r
4190                 fromX = fromY = -1; \r
4191       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4192       break;\r
4193     }\r
4194       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4195       DrawPosition(TRUE, NULL);\r
4196     break;\r
4197 \r
4198   case WM_LBUTTONUP:\r
4199       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4200       DrawPosition(TRUE, NULL);\r
4201     break;\r
4202 \r
4203   case WM_MOUSEMOVE:\r
4204     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4205     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4206     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4207     if ((appData.animateDragging || appData.highlightDragging)\r
4208         && (wParam & MK_LBUTTON)\r
4209         && dragInfo.from.x >= 0) \r
4210     {\r
4211       BOOL full_repaint = FALSE;\r
4212 \r
4213       if (appData.animateDragging) {\r
4214         dragInfo.pos = pt;\r
4215       }\r
4216       if (appData.highlightDragging) {\r
4217         SetHighlights(fromX, fromY, x, y);\r
4218         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4219             full_repaint = TRUE;\r
4220         }\r
4221       }\r
4222       \r
4223       DrawPosition( full_repaint, NULL);\r
4224       \r
4225       dragInfo.lastpos = dragInfo.pos;\r
4226     }\r
4227     break;\r
4228 \r
4229   case WM_MOUSEWHEEL: // [DM]\r
4230     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4231        /* Mouse Wheel is being rolled forward\r
4232         * Play moves forward\r
4233         */\r
4234        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4235                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4236        /* Mouse Wheel is being rolled backward\r
4237         * Play moves backward\r
4238         */\r
4239        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4240                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4241     }\r
4242     break;\r
4243 \r
4244   case WM_MBUTTONUP:\r
4245   case WM_RBUTTONUP:\r
4246     ReleaseCapture();\r
4247     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4248     break;\r
4249  \r
4250   case WM_MBUTTONDOWN:\r
4251   case WM_RBUTTONDOWN:\r
4252     ErrorPopDown();\r
4253     ReleaseCapture();\r
4254     fromX = fromY = -1;\r
4255     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4256     dragInfo.start.x = dragInfo.start.y = -1;\r
4257     dragInfo.from = dragInfo.start;\r
4258     dragInfo.lastpos = dragInfo.pos;\r
4259     if (appData.highlightDragging) {\r
4260       ClearHighlights();\r
4261     }\r
4262     if(y == -2) {\r
4263       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4264       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4265           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4266       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4267           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4268       }\r
4269       break;\r
4270     }\r
4271     DrawPosition(TRUE, NULL);\r
4272 \r
4273     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4274     switch (menuNr) {\r
4275     case 0:\r
4276       if (message == WM_MBUTTONDOWN) {\r
4277         buttonCount = 3;  /* even if system didn't think so */\r
4278         if (wParam & MK_SHIFT) \r
4279           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4280         else\r
4281           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4282       } else { /* message == WM_RBUTTONDOWN */\r
4283         /* Just have one menu, on the right button.  Windows users don't\r
4284            think to try the middle one, and sometimes other software steals\r
4285            it, or it doesn't really exist. */\r
4286         if(gameInfo.variant != VariantShogi)\r
4287             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4288         else\r
4289             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4290       }\r
4291       break;\r
4292     case 2:\r
4293       SetCapture(hwndMain);\r
4294       break;\r
4295     case 1:\r
4296       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4297       SetupDropMenu(hmenu);\r
4298       MenuPopup(hwnd, pt, hmenu, -1);\r
4299     default:\r
4300       break;\r
4301     }\r
4302     break;\r
4303   }\r
4304 \r
4305   recursive--;\r
4306 }\r
4307 \r
4308 /* Preprocess messages for buttons in main window */\r
4309 LRESULT CALLBACK\r
4310 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4311 {\r
4312   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4313   int i, dir;\r
4314 \r
4315   for (i=0; i<N_BUTTONS; i++) {\r
4316     if (buttonDesc[i].id == id) break;\r
4317   }\r
4318   if (i == N_BUTTONS) return 0;\r
4319   switch (message) {\r
4320   case WM_KEYDOWN:\r
4321     switch (wParam) {\r
4322     case VK_LEFT:\r
4323     case VK_RIGHT:\r
4324       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4325       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4326       return TRUE;\r
4327     }\r
4328     break;\r
4329   case WM_CHAR:\r
4330     switch (wParam) {\r
4331     case '\r':\r
4332       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4333       return TRUE;\r
4334     default:\r
4335       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4336         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4337         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4338         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4339         SetFocus(h);\r
4340         SendMessage(h, WM_CHAR, wParam, lParam);\r
4341         return TRUE;\r
4342       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4343         TypeInEvent((char)wParam);\r
4344       }\r
4345       break;\r
4346     }\r
4347     break;\r
4348   }\r
4349   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4350 }\r
4351 \r
4352 /* Process messages for Promotion dialog box */\r
4353 LRESULT CALLBACK\r
4354 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4355 {\r
4356   char promoChar;\r
4357 \r
4358   switch (message) {\r
4359   case WM_INITDIALOG: /* message: initialize dialog box */\r
4360     /* Center the dialog over the application window */\r
4361     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4362     Translate(hDlg, DLG_PromotionKing);\r
4363     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4364       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4365        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4366        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4367                SW_SHOW : SW_HIDE);\r
4368     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4369     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4370        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4371          PieceToChar(WhiteAngel) != '~') ||\r
4372         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4373          PieceToChar(BlackAngel) != '~')   ) ?\r
4374                SW_SHOW : SW_HIDE);\r
4375     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4376        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4377          PieceToChar(WhiteMarshall) != '~') ||\r
4378         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4379          PieceToChar(BlackMarshall) != '~')   ) ?\r
4380                SW_SHOW : SW_HIDE);\r
4381     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4382     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4383        gameInfo.variant != VariantShogi ?\r
4384                SW_SHOW : SW_HIDE);\r
4385     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4386        gameInfo.variant != VariantShogi ?\r
4387                SW_SHOW : SW_HIDE);\r
4388     if(gameInfo.variant == VariantShogi) {\r
4389         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4390         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4391         SetWindowText(hDlg, "Promote?");\r
4392     }\r
4393     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4394        gameInfo.variant == VariantSuper ?\r
4395                SW_SHOW : SW_HIDE);\r
4396     return TRUE;\r
4397 \r
4398   case WM_COMMAND: /* message: received a command */\r
4399     switch (LOWORD(wParam)) {\r
4400     case IDCANCEL:\r
4401       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4402       ClearHighlights();\r
4403       DrawPosition(FALSE, NULL);\r
4404       return TRUE;\r
4405     case PB_King:\r
4406       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4407       break;\r
4408     case PB_Queen:\r
4409       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4410       break;\r
4411     case PB_Rook:\r
4412       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4413       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4414       break;\r
4415     case PB_Bishop:\r
4416       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4417       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4418       break;\r
4419     case PB_Chancellor:\r
4420       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4421       break;\r
4422     case PB_Archbishop:\r
4423       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4424       break;\r
4425     case PB_Knight:\r
4426       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4427       break;\r
4428     default:\r
4429       return FALSE;\r
4430     }\r
4431     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4432     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4433     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4434     fromX = fromY = -1;\r
4435     if (!appData.highlightLastMove) {\r
4436       ClearHighlights();\r
4437       DrawPosition(FALSE, NULL);\r
4438     }\r
4439     return TRUE;\r
4440   }\r
4441   return FALSE;\r
4442 }\r
4443 \r
4444 /* Pop up promotion dialog */\r
4445 VOID\r
4446 PromotionPopup(HWND hwnd)\r
4447 {\r
4448   FARPROC lpProc;\r
4449 \r
4450   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4451   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4452     hwnd, (DLGPROC)lpProc);\r
4453   FreeProcInstance(lpProc);\r
4454 }\r
4455 \r
4456 void\r
4457 PromotionPopUp()\r
4458 {\r
4459   DrawPosition(TRUE, NULL);\r
4460   PromotionPopup(hwndMain);\r
4461 }\r
4462 \r
4463 /* Toggle ShowThinking */\r
4464 VOID\r
4465 ToggleShowThinking()\r
4466 {\r
4467   appData.showThinking = !appData.showThinking;\r
4468   ShowThinkingEvent();\r
4469 }\r
4470 \r
4471 VOID\r
4472 LoadGameDialog(HWND hwnd, char* title)\r
4473 {\r
4474   UINT number = 0;\r
4475   FILE *f;\r
4476   char fileTitle[MSG_SIZ];\r
4477   f = OpenFileDialog(hwnd, "rb", "",\r
4478                      appData.oldSaveStyle ? "gam" : "pgn",\r
4479                      GAME_FILT,\r
4480                      title, &number, fileTitle, NULL);\r
4481   if (f != NULL) {\r
4482     cmailMsgLoaded = FALSE;\r
4483     if (number == 0) {\r
4484       int error = GameListBuild(f);\r
4485       if (error) {\r
4486         DisplayError(_("Cannot build game list"), error);\r
4487       } else if (!ListEmpty(&gameList) &&\r
4488                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4489         GameListPopUp(f, fileTitle);\r
4490         return;\r
4491       }\r
4492       GameListDestroy();\r
4493       number = 1;\r
4494     }\r
4495     LoadGame(f, number, fileTitle, FALSE);\r
4496   }\r
4497 }\r
4498 \r
4499 int get_term_width()\r
4500 {\r
4501     HDC hdc;\r
4502     TEXTMETRIC tm;\r
4503     RECT rc;\r
4504     HFONT hfont, hold_font;\r
4505     LOGFONT lf;\r
4506     HWND hText;\r
4507 \r
4508     if (hwndConsole)\r
4509         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4510     else\r
4511         return 79;\r
4512 \r
4513     // get the text metrics\r
4514     hdc = GetDC(hText);\r
4515     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4516     if (consoleCF.dwEffects & CFE_BOLD)\r
4517         lf.lfWeight = FW_BOLD;\r
4518     if (consoleCF.dwEffects & CFE_ITALIC)\r
4519         lf.lfItalic = TRUE;\r
4520     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4521         lf.lfStrikeOut = TRUE;\r
4522     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4523         lf.lfUnderline = TRUE;\r
4524     hfont = CreateFontIndirect(&lf);\r
4525     hold_font = SelectObject(hdc, hfont);\r
4526     GetTextMetrics(hdc, &tm);\r
4527     SelectObject(hdc, hold_font);\r
4528     DeleteObject(hfont);\r
4529     ReleaseDC(hText, hdc);\r
4530 \r
4531     // get the rectangle\r
4532     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4533 \r
4534     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4535 }\r
4536 \r
4537 void UpdateICSWidth(HWND hText)\r
4538 {\r
4539     LONG old_width, new_width;\r
4540 \r
4541     new_width = get_term_width(hText, FALSE);\r
4542     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4543     if (new_width != old_width)\r
4544     {\r
4545         ics_update_width(new_width);\r
4546         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4547     }\r
4548 }\r
4549 \r
4550 VOID\r
4551 ChangedConsoleFont()\r
4552 {\r
4553   CHARFORMAT cfmt;\r
4554   CHARRANGE tmpsel, sel;\r
4555   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4556   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4557   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4558   PARAFORMAT paraf;\r
4559 \r
4560   cfmt.cbSize = sizeof(CHARFORMAT);\r
4561   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4562     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4563                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4564   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4565    * size.  This was undocumented in the version of MSVC++ that I had\r
4566    * when I wrote the code, but is apparently documented now.\r
4567    */\r
4568   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4569   cfmt.bCharSet = f->lf.lfCharSet;\r
4570   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4571   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4572   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4573   /* Why are the following seemingly needed too? */\r
4574   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4575   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4576   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4577   tmpsel.cpMin = 0;\r
4578   tmpsel.cpMax = -1; /*999999?*/\r
4579   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4580   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4581   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4582    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4583    */\r
4584   paraf.cbSize = sizeof(paraf);\r
4585   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4586   paraf.dxStartIndent = 0;\r
4587   paraf.dxOffset = WRAP_INDENT;\r
4588   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4589   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4590   UpdateICSWidth(hText);\r
4591 }\r
4592 \r
4593 /*---------------------------------------------------------------------------*\\r
4594  *\r
4595  * Window Proc for main window\r
4596  *\r
4597 \*---------------------------------------------------------------------------*/\r
4598 \r
4599 /* Process messages for main window, etc. */\r
4600 LRESULT CALLBACK\r
4601 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4602 {\r
4603   FARPROC lpProc;\r
4604   int wmId, wmEvent;\r
4605   char *defName;\r
4606   FILE *f;\r
4607   UINT number;\r
4608   char fileTitle[MSG_SIZ];\r
4609   char buf[MSG_SIZ];\r
4610   static SnapData sd;\r
4611   static int peek=0;\r
4612 \r
4613   switch (message) {\r
4614 \r
4615   case WM_PAINT: /* message: repaint portion of window */\r
4616     PaintProc(hwnd);\r
4617     break;\r
4618 \r
4619   case WM_ERASEBKGND:\r
4620     if (IsIconic(hwnd)) {\r
4621       /* Cheat; change the message */\r
4622       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4623     } else {\r
4624       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4625     }\r
4626     break;\r
4627 \r
4628   case WM_LBUTTONDOWN:\r
4629   case WM_MBUTTONDOWN:\r
4630   case WM_RBUTTONDOWN:\r
4631   case WM_LBUTTONUP:\r
4632   case WM_MBUTTONUP:\r
4633   case WM_RBUTTONUP:\r
4634   case WM_MOUSEMOVE:\r
4635   case WM_MOUSEWHEEL:\r
4636     MouseEvent(hwnd, message, wParam, lParam);\r
4637     break;\r
4638 \r
4639   case WM_KEYUP:\r
4640     if((char)wParam == '\b') {\r
4641       ForwardEvent(); peek = 0;\r
4642     }\r
4643 \r
4644     JAWS_KBUP_NAVIGATION\r
4645 \r
4646     break;\r
4647 \r
4648   case WM_KEYDOWN:\r
4649     if((char)wParam == '\b') {\r
4650       if(!peek) BackwardEvent(), peek = 1;\r
4651     }\r
4652 \r
4653     JAWS_KBDOWN_NAVIGATION\r
4654 \r
4655     break;\r
4656 \r
4657   case WM_CHAR:\r
4658     \r
4659     JAWS_ALT_INTERCEPT\r
4660 \r
4661     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4662         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4663         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4664         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4665         SetFocus(h);\r
4666         SendMessage(h, message, wParam, lParam);\r
4667     } else if(lParam != KF_REPEAT) {\r
4668         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4669                 TypeInEvent((char)wParam);\r
4670         } else if((char)wParam == 003) CopyGameToClipboard();\r
4671          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4672     }\r
4673 \r
4674     break;\r
4675 \r
4676   case WM_PALETTECHANGED:\r
4677     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4678       int nnew;\r
4679       HDC hdc = GetDC(hwndMain);\r
4680       SelectPalette(hdc, hPal, TRUE);\r
4681       nnew = RealizePalette(hdc);\r
4682       if (nnew > 0) {\r
4683         paletteChanged = TRUE;\r
4684         InvalidateRect(hwnd, &boardRect, FALSE);\r
4685       }\r
4686       ReleaseDC(hwnd, hdc);\r
4687     }\r
4688     break;\r
4689 \r
4690   case WM_QUERYNEWPALETTE:\r
4691     if (!appData.monoMode /*&& paletteChanged*/) {\r
4692       int nnew;\r
4693       HDC hdc = GetDC(hwndMain);\r
4694       paletteChanged = FALSE;\r
4695       SelectPalette(hdc, hPal, FALSE);\r
4696       nnew = RealizePalette(hdc);\r
4697       if (nnew > 0) {\r
4698         InvalidateRect(hwnd, &boardRect, FALSE);\r
4699       }\r
4700       ReleaseDC(hwnd, hdc);\r
4701       return TRUE;\r
4702     }\r
4703     return FALSE;\r
4704 \r
4705   case WM_COMMAND: /* message: command from application menu */\r
4706     wmId    = LOWORD(wParam);\r
4707     wmEvent = HIWORD(wParam);\r
4708 \r
4709     switch (wmId) {\r
4710     case IDM_NewGame:\r
4711       ResetGameEvent();\r
4712       SAY("new game enter a move to play against the computer with white");\r
4713       break;\r
4714 \r
4715     case IDM_NewGameFRC:\r
4716       if( NewGameFRC() == 0 ) {\r
4717         ResetGameEvent();\r
4718       }\r
4719       break;\r
4720 \r
4721     case IDM_NewVariant:\r
4722       NewVariantPopup(hwnd);\r
4723       break;\r
4724 \r
4725     case IDM_LoadGame:\r
4726       LoadGameDialog(hwnd, _("Load Game from File"));\r
4727       break;\r
4728 \r
4729     case IDM_LoadNextGame:\r
4730       ReloadGame(1);\r
4731       break;\r
4732 \r
4733     case IDM_LoadPrevGame:\r
4734       ReloadGame(-1);\r
4735       break;\r
4736 \r
4737     case IDM_ReloadGame:\r
4738       ReloadGame(0);\r
4739       break;\r
4740 \r
4741     case IDM_LoadPosition:\r
4742       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4743         Reset(FALSE, TRUE);\r
4744       }\r
4745       number = 1;\r
4746       f = OpenFileDialog(hwnd, "rb", "",\r
4747                          appData.oldSaveStyle ? "pos" : "fen",\r
4748                          POSITION_FILT,\r
4749                          _("Load Position from File"), &number, fileTitle, NULL);\r
4750       if (f != NULL) {\r
4751         LoadPosition(f, number, fileTitle);\r
4752       }\r
4753       break;\r
4754 \r
4755     case IDM_LoadNextPosition:\r
4756       ReloadPosition(1);\r
4757       break;\r
4758 \r
4759     case IDM_LoadPrevPosition:\r
4760       ReloadPosition(-1);\r
4761       break;\r
4762 \r
4763     case IDM_ReloadPosition:\r
4764       ReloadPosition(0);\r
4765       break;\r
4766 \r
4767     case IDM_SaveGame:\r
4768       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4769       f = OpenFileDialog(hwnd, "a", defName,\r
4770                          appData.oldSaveStyle ? "gam" : "pgn",\r
4771                          GAME_FILT,\r
4772                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4773       if (f != NULL) {\r
4774         SaveGame(f, 0, "");\r
4775       }\r
4776       break;\r
4777 \r
4778     case IDM_SavePosition:\r
4779       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4780       f = OpenFileDialog(hwnd, "a", defName,\r
4781                          appData.oldSaveStyle ? "pos" : "fen",\r
4782                          POSITION_FILT,\r
4783                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4784       if (f != NULL) {\r
4785         SavePosition(f, 0, "");\r
4786       }\r
4787       break;\r
4788 \r
4789     case IDM_SaveDiagram:\r
4790       defName = "diagram";\r
4791       f = OpenFileDialog(hwnd, "wb", defName,\r
4792                          "bmp",\r
4793                          DIAGRAM_FILT,\r
4794                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4795       if (f != NULL) {\r
4796         SaveDiagram(f);\r
4797       }\r
4798       break;\r
4799 \r
4800     case IDM_CopyGame:\r
4801       CopyGameToClipboard();\r
4802       break;\r
4803 \r
4804     case IDM_PasteGame:\r
4805       PasteGameFromClipboard();\r
4806       break;\r
4807 \r
4808     case IDM_CopyGameListToClipboard:\r
4809       CopyGameListToClipboard();\r
4810       break;\r
4811 \r
4812     /* [AS] Autodetect FEN or PGN data */\r
4813     case IDM_PasteAny:\r
4814       PasteGameOrFENFromClipboard();\r
4815       break;\r
4816 \r
4817     /* [AS] Move history */\r
4818     case IDM_ShowMoveHistory:\r
4819         if( MoveHistoryIsUp() ) {\r
4820             MoveHistoryPopDown();\r
4821         }\r
4822         else {\r
4823             MoveHistoryPopUp();\r
4824         }\r
4825         break;\r
4826 \r
4827     /* [AS] Eval graph */\r
4828     case IDM_ShowEvalGraph:\r
4829         if( EvalGraphIsUp() ) {\r
4830             EvalGraphPopDown();\r
4831         }\r
4832         else {\r
4833             EvalGraphPopUp();\r
4834             SetFocus(hwndMain);\r
4835         }\r
4836         break;\r
4837 \r
4838     /* [AS] Engine output */\r
4839     case IDM_ShowEngineOutput:\r
4840         if( EngineOutputIsUp() ) {\r
4841             EngineOutputPopDown();\r
4842         }\r
4843         else {\r
4844             EngineOutputPopUp();\r
4845         }\r
4846         break;\r
4847 \r
4848     /* [AS] User adjudication */\r
4849     case IDM_UserAdjudication_White:\r
4850         UserAdjudicationEvent( +1 );\r
4851         break;\r
4852 \r
4853     case IDM_UserAdjudication_Black:\r
4854         UserAdjudicationEvent( -1 );\r
4855         break;\r
4856 \r
4857     case IDM_UserAdjudication_Draw:\r
4858         UserAdjudicationEvent( 0 );\r
4859         break;\r
4860 \r
4861     /* [AS] Game list options dialog */\r
4862     case IDM_GameListOptions:\r
4863       GameListOptions();\r
4864       break;\r
4865 \r
4866     case IDM_NewChat:\r
4867       ChatPopUp(NULL);\r
4868       break;\r
4869 \r
4870     case IDM_CopyPosition:\r
4871       CopyFENToClipboard();\r
4872       break;\r
4873 \r
4874     case IDM_PastePosition:\r
4875       PasteFENFromClipboard();\r
4876       break;\r
4877 \r
4878     case IDM_MailMove:\r
4879       MailMoveEvent();\r
4880       break;\r
4881 \r
4882     case IDM_ReloadCMailMsg:\r
4883       Reset(TRUE, TRUE);\r
4884       ReloadCmailMsgEvent(FALSE);\r
4885       break;\r
4886 \r
4887     case IDM_Minimize:\r
4888       ShowWindow(hwnd, SW_MINIMIZE);\r
4889       break;\r
4890 \r
4891     case IDM_Exit:\r
4892       ExitEvent(0);\r
4893       break;\r
4894 \r
4895     case IDM_MachineWhite:\r
4896       MachineWhiteEvent();\r
4897       /*\r
4898        * refresh the tags dialog only if it's visible\r
4899        */\r
4900       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4901           char *tags;\r
4902           tags = PGNTags(&gameInfo);\r
4903           TagsPopUp(tags, CmailMsg());\r
4904           free(tags);\r
4905       }\r
4906       SAY("computer starts playing white");\r
4907       break;\r
4908 \r
4909     case IDM_MachineBlack:\r
4910       MachineBlackEvent();\r
4911       /*\r
4912        * refresh the tags dialog only if it's visible\r
4913        */\r
4914       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4915           char *tags;\r
4916           tags = PGNTags(&gameInfo);\r
4917           TagsPopUp(tags, CmailMsg());\r
4918           free(tags);\r
4919       }\r
4920       SAY("computer starts playing black");\r
4921       break;\r
4922 \r
4923     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4924       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4925       break;\r
4926 \r
4927     case IDM_TwoMachines:\r
4928       TwoMachinesEvent();\r
4929       /*\r
4930        * refresh the tags dialog only if it's visible\r
4931        */\r
4932       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4933           char *tags;\r
4934           tags = PGNTags(&gameInfo);\r
4935           TagsPopUp(tags, CmailMsg());\r
4936           free(tags);\r
4937       }\r
4938       SAY("computer starts playing both sides");\r
4939       break;\r
4940 \r
4941     case IDM_AnalysisMode:\r
4942       if (!first.analysisSupport) {\r
4943         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4944         DisplayError(buf, 0);\r
4945       } else {\r
4946         SAY("analyzing current position");\r
4947         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4948         if (appData.icsActive) {\r
4949                if (gameMode != IcsObserving) {\r
4950                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4951                        DisplayError(buf, 0);\r
4952                        /* secure check */\r
4953                        if (appData.icsEngineAnalyze) {\r
4954                                if (appData.debugMode) \r
4955                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4956                                ExitAnalyzeMode();\r
4957                                ModeHighlight();\r
4958                                break;\r
4959                        }\r
4960                        break;\r
4961                } else {\r
4962                        /* if enable, user want disable icsEngineAnalyze */\r
4963                        if (appData.icsEngineAnalyze) {\r
4964                                ExitAnalyzeMode();\r
4965                                ModeHighlight();\r
4966                                break;\r
4967                        }\r
4968                        appData.icsEngineAnalyze = TRUE;\r
4969                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4970                }\r
4971         } \r
4972         if (!appData.showThinking) ToggleShowThinking();\r
4973         AnalyzeModeEvent();\r
4974       }\r
4975       break;\r
4976 \r
4977     case IDM_AnalyzeFile:\r
4978       if (!first.analysisSupport) {\r
4979         char buf[MSG_SIZ];\r
4980           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4981         DisplayError(buf, 0);\r
4982       } else {\r
4983         if (!appData.showThinking) ToggleShowThinking();\r
4984         AnalyzeFileEvent();\r
4985 //      LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4986         AnalysisPeriodicEvent(1);\r
4987       }\r
4988       break;\r
4989 \r
4990     case IDM_IcsClient:\r
4991       IcsClientEvent();\r
4992       break;\r
4993 \r
4994     case IDM_EditGame:\r
4995     case IDM_EditGame2:\r
4996       EditGameEvent();\r
4997       SAY("edit game");\r
4998       break;\r
4999 \r
5000     case IDM_EditPosition:\r
5001     case IDM_EditPosition2:\r
5002       EditPositionEvent();\r
5003       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5004       break;\r
5005 \r
5006     case IDM_Training:\r
5007       TrainingEvent();\r
5008       break;\r
5009 \r
5010     case IDM_ShowGameList:\r
5011       ShowGameListProc();\r
5012       break;\r
5013 \r
5014     case IDM_EditProgs1:\r
5015       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5016       break;\r
5017 \r
5018     case IDM_LoadProg1:\r
5019      LoadEnginePopUp(hwndMain, 0);\r
5020       break;\r
5021 \r
5022     case IDM_LoadProg2:\r
5023      LoadEnginePopUp(hwndMain, 1);\r
5024       break;\r
5025 \r
5026     case IDM_EditServers:\r
5027       EditTagsPopUp(icsNames, &icsNames);\r
5028       break;\r
5029 \r
5030     case IDM_EditTags:\r
5031     case IDM_Tags:\r
5032       EditTagsProc();\r
5033       break;\r
5034 \r
5035     case IDM_EditBook:\r
5036       EditBookEvent();\r
5037       break;\r
5038 \r
5039     case IDM_EditComment:\r
5040     case IDM_Comment:\r
5041       if (commentUp && editComment) {\r
5042         CommentPopDown();\r
5043       } else {\r
5044         EditCommentEvent();\r
5045       }\r
5046       break;\r
5047 \r
5048     case IDM_Pause:\r
5049       PauseEvent();\r
5050       break;\r
5051 \r
5052     case IDM_Accept:\r
5053       AcceptEvent();\r
5054       break;\r
5055 \r
5056     case IDM_Decline:\r
5057       DeclineEvent();\r
5058       break;\r
5059 \r
5060     case IDM_Rematch:\r
5061       RematchEvent();\r
5062       break;\r
5063 \r
5064     case IDM_CallFlag:\r
5065       CallFlagEvent();\r
5066       break;\r
5067 \r
5068     case IDM_Draw:\r
5069       DrawEvent();\r
5070       break;\r
5071 \r
5072     case IDM_Adjourn:\r
5073       AdjournEvent();\r
5074       break;\r
5075 \r
5076     case IDM_Abort:\r
5077       AbortEvent();\r
5078       break;\r
5079 \r
5080     case IDM_Resign:\r
5081       ResignEvent();\r
5082       break;\r
5083 \r
5084     case IDM_StopObserving:\r
5085       StopObservingEvent();\r
5086       break;\r
5087 \r
5088     case IDM_StopExamining:\r
5089       StopExaminingEvent();\r
5090       break;\r
5091 \r
5092     case IDM_Upload:\r
5093       UploadGameEvent();\r
5094       break;\r
5095 \r
5096     case IDM_TypeInMove:\r
5097       TypeInEvent('\000');\r
5098       break;\r
5099 \r
5100     case IDM_TypeInName:\r
5101       PopUpNameDialog('\000');\r
5102       break;\r
5103 \r
5104     case IDM_Backward:\r
5105       BackwardEvent();\r
5106       SetFocus(hwndMain);\r
5107       break;\r
5108 \r
5109     JAWS_MENU_ITEMS\r
5110 \r
5111     case IDM_Forward:\r
5112       ForwardEvent();\r
5113       SetFocus(hwndMain);\r
5114       break;\r
5115 \r
5116     case IDM_ToStart:\r
5117       ToStartEvent();\r
5118       SetFocus(hwndMain);\r
5119       break;\r
5120 \r
5121     case IDM_ToEnd:\r
5122       ToEndEvent();\r
5123       SetFocus(hwndMain);\r
5124       break;\r
5125 \r
5126     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5127     case OPT_GameListPrev:\r
5128       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5129       break;\r
5130 \r
5131     case IDM_Revert:\r
5132       RevertEvent(FALSE);\r
5133       break;\r
5134 \r
5135     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5136       RevertEvent(TRUE);\r
5137       break;\r
5138 \r
5139     case IDM_TruncateGame:\r
5140       TruncateGameEvent();\r
5141       break;\r
5142 \r
5143     case IDM_MoveNow:\r
5144       MoveNowEvent();\r
5145       break;\r
5146 \r
5147     case IDM_RetractMove:\r
5148       RetractMoveEvent();\r
5149       break;\r
5150 \r
5151     case IDM_FlipView:\r
5152       flipView = !flipView;\r
5153       DrawPosition(FALSE, NULL);\r
5154       break;\r
5155 \r
5156     case IDM_FlipClock:\r
5157       flipClock = !flipClock;\r
5158       DisplayBothClocks();\r
5159       DisplayLogos();\r
5160       break;\r
5161 \r
5162     case IDM_MuteSounds:\r
5163       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5164       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5165                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5166       break;\r
5167 \r
5168     case IDM_GeneralOptions:\r
5169       GeneralOptionsPopup(hwnd);\r
5170       DrawPosition(TRUE, NULL);\r
5171       break;\r
5172 \r
5173     case IDM_BoardOptions:\r
5174       BoardOptionsPopup(hwnd);\r
5175       break;\r
5176 \r
5177     case IDM_EnginePlayOptions:\r
5178       EnginePlayOptionsPopup(hwnd);\r
5179       break;\r
5180 \r
5181     case IDM_Engine1Options:\r
5182       EngineOptionsPopup(hwnd, &first);\r
5183       break;\r
5184 \r
5185     case IDM_Engine2Options:\r
5186       savedHwnd = hwnd;\r
5187       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5188       EngineOptionsPopup(hwnd, &second);\r
5189       break;\r
5190 \r
5191     case IDM_OptionsUCI:\r
5192       UciOptionsPopup(hwnd);\r
5193       break;\r
5194 \r
5195     case IDM_Tourney:\r
5196       TourneyPopup(hwnd);\r
5197       break;\r
5198 \r
5199     case IDM_IcsOptions:\r
5200       IcsOptionsPopup(hwnd);\r
5201       break;\r
5202 \r
5203     case IDM_Fonts:\r
5204       FontsOptionsPopup(hwnd);\r
5205       break;\r
5206 \r
5207     case IDM_Sounds:\r
5208       SoundOptionsPopup(hwnd);\r
5209       break;\r
5210 \r
5211     case IDM_CommPort:\r
5212       CommPortOptionsPopup(hwnd);\r
5213       break;\r
5214 \r
5215     case IDM_LoadOptions:\r
5216       LoadOptionsPopup(hwnd);\r
5217       break;\r
5218 \r
5219     case IDM_SaveOptions:\r
5220       SaveOptionsPopup(hwnd);\r
5221       break;\r
5222 \r
5223     case IDM_TimeControl:\r
5224       TimeControlOptionsPopup(hwnd);\r
5225       break;\r
5226 \r
5227     case IDM_SaveSettings:\r
5228       SaveSettings(settingsFileName);\r
5229       break;\r
5230 \r
5231     case IDM_SaveSettingsOnExit:\r
5232       saveSettingsOnExit = !saveSettingsOnExit;\r
5233       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5234                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5235                                          MF_CHECKED : MF_UNCHECKED));\r
5236       break;\r
5237 \r
5238     case IDM_Hint:\r
5239       HintEvent();\r
5240       break;\r
5241 \r
5242     case IDM_Book:\r
5243       BookEvent();\r
5244       break;\r
5245 \r
5246     case IDM_AboutGame:\r
5247       AboutGameEvent();\r
5248       break;\r
5249 \r
5250     case IDM_Debug:\r
5251       appData.debugMode = !appData.debugMode;\r
5252       if (appData.debugMode) {\r
5253         char dir[MSG_SIZ];\r
5254         GetCurrentDirectory(MSG_SIZ, dir);\r
5255         SetCurrentDirectory(installDir);\r
5256         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5257         SetCurrentDirectory(dir);\r
5258         setbuf(debugFP, NULL);\r
5259       } else {\r
5260         fclose(debugFP);\r
5261         debugFP = NULL;\r
5262       }\r
5263       break;\r
5264 \r
5265     case IDM_HELPCONTENTS:\r
5266       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5267           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5268           MessageBox (GetFocus(),\r
5269                     _("Unable to activate help"),\r
5270                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5271       }\r
5272       break;\r
5273 \r
5274     case IDM_HELPSEARCH:\r
5275         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5276             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5277         MessageBox (GetFocus(),\r
5278                     _("Unable to activate help"),\r
5279                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5280       }\r
5281       break;\r
5282 \r
5283     case IDM_HELPHELP:\r
5284       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5285         MessageBox (GetFocus(),\r
5286                     _("Unable to activate help"),\r
5287                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5288       }\r
5289       break;\r
5290 \r
5291     case IDM_ABOUT:\r
5292       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5293       DialogBox(hInst, \r
5294         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5295         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5296       FreeProcInstance(lpProc);\r
5297       break;\r
5298 \r
5299     case IDM_DirectCommand1:\r
5300       AskQuestionEvent(_("Direct Command"),\r
5301                        _("Send to chess program:"), "", "1");\r
5302       break;\r
5303     case IDM_DirectCommand2:\r
5304       AskQuestionEvent(_("Direct Command"),\r
5305                        _("Send to second chess program:"), "", "2");\r
5306       break;\r
5307 \r
5308     case EP_WhitePawn:\r
5309       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5310       fromX = fromY = -1;\r
5311       break;\r
5312 \r
5313     case EP_WhiteKnight:\r
5314       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5315       fromX = fromY = -1;\r
5316       break;\r
5317 \r
5318     case EP_WhiteBishop:\r
5319       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5320       fromX = fromY = -1;\r
5321       break;\r
5322 \r
5323     case EP_WhiteRook:\r
5324       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5325       fromX = fromY = -1;\r
5326       break;\r
5327 \r
5328     case EP_WhiteQueen:\r
5329       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5330       fromX = fromY = -1;\r
5331       break;\r
5332 \r
5333     case EP_WhiteFerz:\r
5334       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5335       fromX = fromY = -1;\r
5336       break;\r
5337 \r
5338     case EP_WhiteWazir:\r
5339       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5340       fromX = fromY = -1;\r
5341       break;\r
5342 \r
5343     case EP_WhiteAlfil:\r
5344       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5345       fromX = fromY = -1;\r
5346       break;\r
5347 \r
5348     case EP_WhiteCannon:\r
5349       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5350       fromX = fromY = -1;\r
5351       break;\r
5352 \r
5353     case EP_WhiteCardinal:\r
5354       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5355       fromX = fromY = -1;\r
5356       break;\r
5357 \r
5358     case EP_WhiteMarshall:\r
5359       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5360       fromX = fromY = -1;\r
5361       break;\r
5362 \r
5363     case EP_WhiteKing:\r
5364       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5365       fromX = fromY = -1;\r
5366       break;\r
5367 \r
5368     case EP_BlackPawn:\r
5369       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5370       fromX = fromY = -1;\r
5371       break;\r
5372 \r
5373     case EP_BlackKnight:\r
5374       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5375       fromX = fromY = -1;\r
5376       break;\r
5377 \r
5378     case EP_BlackBishop:\r
5379       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5380       fromX = fromY = -1;\r
5381       break;\r
5382 \r
5383     case EP_BlackRook:\r
5384       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5385       fromX = fromY = -1;\r
5386       break;\r
5387 \r
5388     case EP_BlackQueen:\r
5389       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5390       fromX = fromY = -1;\r
5391       break;\r
5392 \r
5393     case EP_BlackFerz:\r
5394       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5395       fromX = fromY = -1;\r
5396       break;\r
5397 \r
5398     case EP_BlackWazir:\r
5399       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5400       fromX = fromY = -1;\r
5401       break;\r
5402 \r
5403     case EP_BlackAlfil:\r
5404       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5405       fromX = fromY = -1;\r
5406       break;\r
5407 \r
5408     case EP_BlackCannon:\r
5409       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5410       fromX = fromY = -1;\r
5411       break;\r
5412 \r
5413     case EP_BlackCardinal:\r
5414       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5415       fromX = fromY = -1;\r
5416       break;\r
5417 \r
5418     case EP_BlackMarshall:\r
5419       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5420       fromX = fromY = -1;\r
5421       break;\r
5422 \r
5423     case EP_BlackKing:\r
5424       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5425       fromX = fromY = -1;\r
5426       break;\r
5427 \r
5428     case EP_EmptySquare:\r
5429       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5430       fromX = fromY = -1;\r
5431       break;\r
5432 \r
5433     case EP_ClearBoard:\r
5434       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5435       fromX = fromY = -1;\r
5436       break;\r
5437 \r
5438     case EP_White:\r
5439       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5440       fromX = fromY = -1;\r
5441       break;\r
5442 \r
5443     case EP_Black:\r
5444       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5445       fromX = fromY = -1;\r
5446       break;\r
5447 \r
5448     case EP_Promote:\r
5449       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5450       fromX = fromY = -1;\r
5451       break;\r
5452 \r
5453     case EP_Demote:\r
5454       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5455       fromX = fromY = -1;\r
5456       break;\r
5457 \r
5458     case DP_Pawn:\r
5459       DropMenuEvent(WhitePawn, fromX, fromY);\r
5460       fromX = fromY = -1;\r
5461       break;\r
5462 \r
5463     case DP_Knight:\r
5464       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5465       fromX = fromY = -1;\r
5466       break;\r
5467 \r
5468     case DP_Bishop:\r
5469       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5470       fromX = fromY = -1;\r
5471       break;\r
5472 \r
5473     case DP_Rook:\r
5474       DropMenuEvent(WhiteRook, fromX, fromY);\r
5475       fromX = fromY = -1;\r
5476       break;\r
5477 \r
5478     case DP_Queen:\r
5479       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5480       fromX = fromY = -1;\r
5481       break;\r
5482 \r
5483     case IDM_English:\r
5484       barbaric = 0; appData.language = "";\r
5485       TranslateMenus(0);\r
5486       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5487       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5488       lastChecked = wmId;\r
5489       break;\r
5490 \r
5491     default:\r
5492       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5493           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5494       else\r
5495       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5496           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5497           TranslateMenus(0);\r
5498           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5499           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5500           lastChecked = wmId;\r
5501           break;\r
5502       }\r
5503       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5504     }\r
5505     break;\r
5506 \r
5507   case WM_TIMER:\r
5508     switch (wParam) {\r
5509     case CLOCK_TIMER_ID:\r
5510       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5511       clockTimerEvent = 0;\r
5512       DecrementClocks(); /* call into back end */\r
5513       break;\r
5514     case LOAD_GAME_TIMER_ID:\r
5515       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5516       loadGameTimerEvent = 0;\r
5517       AutoPlayGameLoop(); /* call into back end */\r
5518       break;\r
5519     case ANALYSIS_TIMER_ID:\r
5520       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5521                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5522         AnalysisPeriodicEvent(0);\r
5523       } else {\r
5524         KillTimer(hwnd, analysisTimerEvent);\r
5525         analysisTimerEvent = 0;\r
5526       }\r
5527       break;\r
5528     case DELAYED_TIMER_ID:\r
5529       KillTimer(hwnd, delayedTimerEvent);\r
5530       delayedTimerEvent = 0;\r
5531       delayedTimerCallback();\r
5532       break;\r
5533     }\r
5534     break;\r
5535 \r
5536   case WM_USER_Input:\r
5537     InputEvent(hwnd, message, wParam, lParam);\r
5538     break;\r
5539 \r
5540   /* [AS] Also move "attached" child windows */\r
5541   case WM_WINDOWPOSCHANGING:\r
5542 \r
5543     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5544         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5545 \r
5546         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5547             /* Window is moving */\r
5548             RECT rcMain;\r
5549 \r
5550 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5551             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5552             rcMain.right  = wpMain.x + wpMain.width;\r
5553             rcMain.top    = wpMain.y;\r
5554             rcMain.bottom = wpMain.y + wpMain.height;\r
5555             \r
5556             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5557             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5558             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5559             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5560             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5561             wpMain.x = lpwp->x;\r
5562             wpMain.y = lpwp->y;\r
5563         }\r
5564     }\r
5565     break;\r
5566 \r
5567   /* [AS] Snapping */\r
5568   case WM_ENTERSIZEMOVE:\r
5569     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5570     if (hwnd == hwndMain) {\r
5571       doingSizing = TRUE;\r
5572       lastSizing = 0;\r
5573     }\r
5574     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5575     break;\r
5576 \r
5577   case WM_SIZING:\r
5578     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5579     if (hwnd == hwndMain) {\r
5580       lastSizing = wParam;\r
5581     }\r
5582     break;\r
5583 \r
5584   case WM_MOVING:\r
5585     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5586       return OnMoving( &sd, hwnd, wParam, lParam );\r
5587 \r
5588   case WM_EXITSIZEMOVE:\r
5589     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5590     if (hwnd == hwndMain) {\r
5591       RECT client;\r
5592       doingSizing = FALSE;\r
5593       InvalidateRect(hwnd, &boardRect, FALSE);\r
5594       GetClientRect(hwnd, &client);\r
5595       ResizeBoard(client.right, client.bottom, lastSizing);\r
5596       lastSizing = 0;\r
5597       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5598     }\r
5599     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5600     break;\r
5601 \r
5602   case WM_DESTROY: /* message: window being destroyed */\r
5603     PostQuitMessage(0);\r
5604     break;\r
5605 \r
5606   case WM_CLOSE:\r
5607     if (hwnd == hwndMain) {\r
5608       ExitEvent(0);\r
5609     }\r
5610     break;\r
5611 \r
5612   default:      /* Passes it on if unprocessed */\r
5613     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5614   }\r
5615   return 0;\r
5616 }\r
5617 \r
5618 /*---------------------------------------------------------------------------*\\r
5619  *\r
5620  * Misc utility routines\r
5621  *\r
5622 \*---------------------------------------------------------------------------*/\r
5623 \r
5624 /*\r
5625  * Decent random number generator, at least not as bad as Windows\r
5626  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5627  */\r
5628 unsigned int randstate;\r
5629 \r
5630 int\r
5631 myrandom(void)\r
5632 {\r
5633   randstate = randstate * 1664525 + 1013904223;\r
5634   return (int) randstate & 0x7fffffff;\r
5635 }\r
5636 \r
5637 void\r
5638 mysrandom(unsigned int seed)\r
5639 {\r
5640   randstate = seed;\r
5641 }\r
5642 \r
5643 \r
5644 /* \r
5645  * returns TRUE if user selects a different color, FALSE otherwise \r
5646  */\r
5647 \r
5648 BOOL\r
5649 ChangeColor(HWND hwnd, COLORREF *which)\r
5650 {\r
5651   static BOOL firstTime = TRUE;\r
5652   static DWORD customColors[16];\r
5653   CHOOSECOLOR cc;\r
5654   COLORREF newcolor;\r
5655   int i;\r
5656   ColorClass ccl;\r
5657 \r
5658   if (firstTime) {\r
5659     /* Make initial colors in use available as custom colors */\r
5660     /* Should we put the compiled-in defaults here instead? */\r
5661     i = 0;\r
5662     customColors[i++] = lightSquareColor & 0xffffff;\r
5663     customColors[i++] = darkSquareColor & 0xffffff;\r
5664     customColors[i++] = whitePieceColor & 0xffffff;\r
5665     customColors[i++] = blackPieceColor & 0xffffff;\r
5666     customColors[i++] = highlightSquareColor & 0xffffff;\r
5667     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5668 \r
5669     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5670       customColors[i++] = textAttribs[ccl].color;\r
5671     }\r
5672     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5673     firstTime = FALSE;\r
5674   }\r
5675 \r
5676   cc.lStructSize = sizeof(cc);\r
5677   cc.hwndOwner = hwnd;\r
5678   cc.hInstance = NULL;\r
5679   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5680   cc.lpCustColors = (LPDWORD) customColors;\r
5681   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5682 \r
5683   if (!ChooseColor(&cc)) return FALSE;\r
5684 \r
5685   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5686   if (newcolor == *which) return FALSE;\r
5687   *which = newcolor;\r
5688   return TRUE;\r
5689 \r
5690   /*\r
5691   InitDrawingColors();\r
5692   InvalidateRect(hwnd, &boardRect, FALSE);\r
5693   */\r
5694 }\r
5695 \r
5696 BOOLEAN\r
5697 MyLoadSound(MySound *ms)\r
5698 {\r
5699   BOOL ok = FALSE;\r
5700   struct stat st;\r
5701   FILE *f;\r
5702 \r
5703   if (ms->data && ms->flag) free(ms->data);\r
5704   ms->data = NULL;\r
5705 \r
5706   switch (ms->name[0]) {\r
5707   case NULLCHAR:\r
5708     /* Silence */\r
5709     ok = TRUE;\r
5710     break;\r
5711   case '$':\r
5712     /* System sound from Control Panel.  Don't preload here. */\r
5713     ok = TRUE;\r
5714     break;\r
5715   case '!':\r
5716     if (ms->name[1] == NULLCHAR) {\r
5717       /* "!" alone = silence */\r
5718       ok = TRUE;\r
5719     } else {\r
5720       /* Builtin wave resource.  Error if not found. */\r
5721       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5722       if (h == NULL) break;\r
5723       ms->data = (void *)LoadResource(hInst, h);\r
5724       ms->flag = 0; // not maloced, so cannot be freed!\r
5725       if (h == NULL) break;\r
5726       ok = TRUE;\r
5727     }\r
5728     break;\r
5729   default:\r
5730     /* .wav file.  Error if not found. */\r
5731     f = fopen(ms->name, "rb");\r
5732     if (f == NULL) break;\r
5733     if (fstat(fileno(f), &st) < 0) break;\r
5734     ms->data = malloc(st.st_size);\r
5735     ms->flag = 1;\r
5736     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5737     fclose(f);\r
5738     ok = TRUE;\r
5739     break;\r
5740   }\r
5741   if (!ok) {\r
5742     char buf[MSG_SIZ];\r
5743       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5744     DisplayError(buf, GetLastError());\r
5745   }\r
5746   return ok;\r
5747 }\r
5748 \r
5749 BOOLEAN\r
5750 MyPlaySound(MySound *ms)\r
5751 {\r
5752   BOOLEAN ok = FALSE;\r
5753 \r
5754   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5755   switch (ms->name[0]) {\r
5756   case NULLCHAR:\r
5757         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5758     /* Silence */\r
5759     ok = TRUE;\r
5760     break;\r
5761   case '$':\r
5762     /* System sound from Control Panel (deprecated feature).\r
5763        "$" alone or an unset sound name gets default beep (still in use). */\r
5764     if (ms->name[1]) {\r
5765       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5766     }\r
5767     if (!ok) ok = MessageBeep(MB_OK);\r
5768     break; \r
5769   case '!':\r
5770     /* Builtin wave resource, or "!" alone for silence */\r
5771     if (ms->name[1]) {\r
5772       if (ms->data == NULL) return FALSE;\r
5773       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5774     } else {\r
5775       ok = TRUE;\r
5776     }\r
5777     break;\r
5778   default:\r
5779     /* .wav file.  Error if not found. */\r
5780     if (ms->data == NULL) return FALSE;\r
5781     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5782     break;\r
5783   }\r
5784   /* Don't print an error: this can happen innocently if the sound driver\r
5785      is busy; for instance, if another instance of WinBoard is playing\r
5786      a sound at about the same time. */\r
5787   return ok;\r
5788 }\r
5789 \r
5790 \r
5791 LRESULT CALLBACK\r
5792 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5793 {\r
5794   BOOL ok;\r
5795   OPENFILENAME *ofn;\r
5796   static UINT *number; /* gross that this is static */\r
5797 \r
5798   switch (message) {\r
5799   case WM_INITDIALOG: /* message: initialize dialog box */\r
5800     /* Center the dialog over the application window */\r
5801     ofn = (OPENFILENAME *) lParam;\r
5802     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5803       number = (UINT *) ofn->lCustData;\r
5804       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5805     } else {\r
5806       number = NULL;\r
5807     }\r
5808     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5809     Translate(hDlg, 1536);\r
5810     return FALSE;  /* Allow for further processing */\r
5811 \r
5812   case WM_COMMAND:\r
5813     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5814       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5815     }\r
5816     return FALSE;  /* Allow for further processing */\r
5817   }\r
5818   return FALSE;\r
5819 }\r
5820 \r
5821 UINT APIENTRY\r
5822 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5823 {\r
5824   static UINT *number;\r
5825   OPENFILENAME *ofname;\r
5826   OFNOTIFY *ofnot;\r
5827   switch (uiMsg) {\r
5828   case WM_INITDIALOG:\r
5829     Translate(hdlg, DLG_IndexNumber);\r
5830     ofname = (OPENFILENAME *)lParam;\r
5831     number = (UINT *)(ofname->lCustData);\r
5832     break;\r
5833   case WM_NOTIFY:\r
5834     ofnot = (OFNOTIFY *)lParam;\r
5835     if (ofnot->hdr.code == CDN_FILEOK) {\r
5836       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5837     }\r
5838     break;\r
5839   }\r
5840   return 0;\r
5841 }\r
5842 \r
5843 \r
5844 FILE *\r
5845 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5846                char *nameFilt, char *dlgTitle, UINT *number,\r
5847                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5848 {\r
5849   OPENFILENAME openFileName;\r
5850   char buf1[MSG_SIZ];\r
5851   FILE *f;\r
5852 \r
5853   if (fileName == NULL) fileName = buf1;\r
5854   if (defName == NULL) {\r
5855     safeStrCpy(fileName, "*.", 3 );\r
5856     strcat(fileName, defExt);\r
5857   } else {\r
5858     safeStrCpy(fileName, defName, MSG_SIZ );\r
5859   }\r
5860     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5861   if (number) *number = 0;\r
5862 \r
5863   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5864   openFileName.hwndOwner         = hwnd;\r
5865   openFileName.hInstance         = (HANDLE) hInst;\r
5866   openFileName.lpstrFilter       = nameFilt;\r
5867   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5868   openFileName.nMaxCustFilter    = 0L;\r
5869   openFileName.nFilterIndex      = 1L;\r
5870   openFileName.lpstrFile         = fileName;\r
5871   openFileName.nMaxFile          = MSG_SIZ;\r
5872   openFileName.lpstrFileTitle    = fileTitle;\r
5873   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5874   openFileName.lpstrInitialDir   = NULL;\r
5875   openFileName.lpstrTitle        = dlgTitle;\r
5876   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5877     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5878     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5879     | (oldDialog ? 0 : OFN_EXPLORER);\r
5880   openFileName.nFileOffset       = 0;\r
5881   openFileName.nFileExtension    = 0;\r
5882   openFileName.lpstrDefExt       = defExt;\r
5883   openFileName.lCustData         = (LONG) number;\r
5884   openFileName.lpfnHook          = oldDialog ?\r
5885     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5886   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5887 \r
5888   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5889                         GetOpenFileName(&openFileName)) {\r
5890     /* open the file */\r
5891     f = fopen(openFileName.lpstrFile, write);\r
5892     if (f == NULL) {\r
5893       MessageBox(hwnd, _("File open failed"), NULL,\r
5894                  MB_OK|MB_ICONEXCLAMATION);\r
5895       return NULL;\r
5896     }\r
5897   } else {\r
5898     int err = CommDlgExtendedError();\r
5899     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5900     return FALSE;\r
5901   }\r
5902   return f;\r
5903 }\r
5904 \r
5905 \r
5906 \r
5907 VOID APIENTRY\r
5908 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5909 {\r
5910   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5911 \r
5912   /*\r
5913    * Get the first pop-up menu in the menu template. This is the\r
5914    * menu that TrackPopupMenu displays.\r
5915    */\r
5916   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5917   TranslateOneMenu(10, hmenuTrackPopup);\r
5918 \r
5919   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5920 \r
5921   /*\r
5922    * TrackPopup uses screen coordinates, so convert the\r
5923    * coordinates of the mouse click to screen coordinates.\r
5924    */\r
5925   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5926 \r
5927   /* Draw and track the floating pop-up menu. */\r
5928   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5929                  pt.x, pt.y, 0, hwnd, NULL);\r
5930 \r
5931   /* Destroy the menu.*/\r
5932   DestroyMenu(hmenu);\r
5933 }\r
5934    \r
5935 typedef struct {\r
5936   HWND hDlg, hText;\r
5937   int sizeX, sizeY, newSizeX, newSizeY;\r
5938   HDWP hdwp;\r
5939 } ResizeEditPlusButtonsClosure;\r
5940 \r
5941 BOOL CALLBACK\r
5942 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5943 {\r
5944   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5945   RECT rect;\r
5946   POINT pt;\r
5947 \r
5948   if (hChild == cl->hText) return TRUE;\r
5949   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5950   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5951   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5952   ScreenToClient(cl->hDlg, &pt);\r
5953   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5954     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5955   return TRUE;\r
5956 }\r
5957 \r
5958 /* Resize a dialog that has a (rich) edit field filling most of\r
5959    the top, with a row of buttons below */\r
5960 VOID\r
5961 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5962 {\r
5963   RECT rectText;\r
5964   int newTextHeight, newTextWidth;\r
5965   ResizeEditPlusButtonsClosure cl;\r
5966   \r
5967   /*if (IsIconic(hDlg)) return;*/\r
5968   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5969   \r
5970   cl.hdwp = BeginDeferWindowPos(8);\r
5971 \r
5972   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5973   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5974   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5975   if (newTextHeight < 0) {\r
5976     newSizeY += -newTextHeight;\r
5977     newTextHeight = 0;\r
5978   }\r
5979   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5980     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5981 \r
5982   cl.hDlg = hDlg;\r
5983   cl.hText = hText;\r
5984   cl.sizeX = sizeX;\r
5985   cl.sizeY = sizeY;\r
5986   cl.newSizeX = newSizeX;\r
5987   cl.newSizeY = newSizeY;\r
5988   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5989 \r
5990   EndDeferWindowPos(cl.hdwp);\r
5991 }\r
5992 \r
5993 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5994 {\r
5995     RECT    rChild, rParent;\r
5996     int     wChild, hChild, wParent, hParent;\r
5997     int     wScreen, hScreen, xNew, yNew;\r
5998     HDC     hdc;\r
5999 \r
6000     /* Get the Height and Width of the child window */\r
6001     GetWindowRect (hwndChild, &rChild);\r
6002     wChild = rChild.right - rChild.left;\r
6003     hChild = rChild.bottom - rChild.top;\r
6004 \r
6005     /* Get the Height and Width of the parent window */\r
6006     GetWindowRect (hwndParent, &rParent);\r
6007     wParent = rParent.right - rParent.left;\r
6008     hParent = rParent.bottom - rParent.top;\r
6009 \r
6010     /* Get the display limits */\r
6011     hdc = GetDC (hwndChild);\r
6012     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6013     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6014     ReleaseDC(hwndChild, hdc);\r
6015 \r
6016     /* Calculate new X position, then adjust for screen */\r
6017     xNew = rParent.left + ((wParent - wChild) /2);\r
6018     if (xNew < 0) {\r
6019         xNew = 0;\r
6020     } else if ((xNew+wChild) > wScreen) {\r
6021         xNew = wScreen - wChild;\r
6022     }\r
6023 \r
6024     /* Calculate new Y position, then adjust for screen */\r
6025     if( mode == 0 ) {\r
6026         yNew = rParent.top  + ((hParent - hChild) /2);\r
6027     }\r
6028     else {\r
6029         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6030     }\r
6031 \r
6032     if (yNew < 0) {\r
6033         yNew = 0;\r
6034     } else if ((yNew+hChild) > hScreen) {\r
6035         yNew = hScreen - hChild;\r
6036     }\r
6037 \r
6038     /* Set it, and return */\r
6039     return SetWindowPos (hwndChild, NULL,\r
6040                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6041 }\r
6042 \r
6043 /* Center one window over another */\r
6044 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6045 {\r
6046     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6047 }\r
6048 \r
6049 /*---------------------------------------------------------------------------*\\r
6050  *\r
6051  * Startup Dialog functions\r
6052  *\r
6053 \*---------------------------------------------------------------------------*/\r
6054 void\r
6055 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6056 {\r
6057   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6058 \r
6059   while (*cd != NULL) {\r
6060     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6061     cd++;\r
6062   }\r
6063 }\r
6064 \r
6065 void\r
6066 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6067 {\r
6068   char buf1[MAX_ARG_LEN];\r
6069   int len;\r
6070 \r
6071   if (str[0] == '@') {\r
6072     FILE* f = fopen(str + 1, "r");\r
6073     if (f == NULL) {\r
6074       DisplayFatalError(str + 1, errno, 2);\r
6075       return;\r
6076     }\r
6077     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6078     fclose(f);\r
6079     buf1[len] = NULLCHAR;\r
6080     str = buf1;\r
6081   }\r
6082 \r
6083   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6084 \r
6085   for (;;) {\r
6086     char buf[MSG_SIZ];\r
6087     char *end = strchr(str, '\n');\r
6088     if (end == NULL) return;\r
6089     memcpy(buf, str, end - str);\r
6090     buf[end - str] = NULLCHAR;\r
6091     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6092     str = end + 1;\r
6093   }\r
6094 }\r
6095 \r
6096 void\r
6097 SetStartupDialogEnables(HWND hDlg)\r
6098 {\r
6099   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6100     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6101     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6102   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6103     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6104   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6105     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6106   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6107     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6108   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6109     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6110     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6111     IsDlgButtonChecked(hDlg, OPT_View));\r
6112 }\r
6113 \r
6114 char *\r
6115 QuoteForFilename(char *filename)\r
6116 {\r
6117   int dquote, space;\r
6118   dquote = strchr(filename, '"') != NULL;\r
6119   space = strchr(filename, ' ') != NULL;\r
6120   if (dquote || space) {\r
6121     if (dquote) {\r
6122       return "'";\r
6123     } else {\r
6124       return "\"";\r
6125     }\r
6126   } else {\r
6127     return "";\r
6128   }\r
6129 }\r
6130 \r
6131 VOID\r
6132 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6133 {\r
6134   char buf[MSG_SIZ];\r
6135   char *q;\r
6136 \r
6137   InitComboStringsFromOption(hwndCombo, nthnames);\r
6138   q = QuoteForFilename(nthcp);\r
6139     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6140   if (*nthdir != NULLCHAR) {\r
6141     q = QuoteForFilename(nthdir);\r
6142       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6143   }\r
6144   if (*nthcp == NULLCHAR) {\r
6145     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6146   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6147     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6148     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6149   }\r
6150 }\r
6151 \r
6152 LRESULT CALLBACK\r
6153 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6154 {\r
6155   char buf[MSG_SIZ];\r
6156   HANDLE hwndCombo;\r
6157   char *p;\r
6158 \r
6159   switch (message) {\r
6160   case WM_INITDIALOG:\r
6161     /* Center the dialog */\r
6162     CenterWindow (hDlg, GetDesktopWindow());\r
6163     Translate(hDlg, DLG_Startup);\r
6164     /* Initialize the dialog items */\r
6165     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6166                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6167                   firstChessProgramNames);\r
6168     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6169                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6170                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6171     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6172     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6173       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6174     if (*appData.icsHelper != NULLCHAR) {\r
6175       char *q = QuoteForFilename(appData.icsHelper);\r
6176       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6177     }\r
6178     if (*appData.icsHost == NULLCHAR) {\r
6179       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6180       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6181     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6182       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6183       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6184     }\r
6185 \r
6186     if (appData.icsActive) {\r
6187       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6188     }\r
6189     else if (appData.noChessProgram) {\r
6190       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6191     }\r
6192     else {\r
6193       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6194     }\r
6195 \r
6196     SetStartupDialogEnables(hDlg);\r
6197     return TRUE;\r
6198 \r
6199   case WM_COMMAND:\r
6200     switch (LOWORD(wParam)) {\r
6201     case IDOK:\r
6202       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6203         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6204         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6205         p = buf;\r
6206         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6207         ParseArgs(StringGet, &p);\r
6208         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6209         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6210         p = buf;\r
6211         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6212         ParseArgs(StringGet, &p);\r
6213         SwapEngines(singleList); // ... and then make it 'second'\r
6214         appData.noChessProgram = FALSE;\r
6215         appData.icsActive = FALSE;\r
6216       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6217         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6218         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6219         p = buf;\r
6220         ParseArgs(StringGet, &p);\r
6221         if (appData.zippyPlay) {\r
6222           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6223           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6224           p = buf;\r
6225           ParseArgs(StringGet, &p);\r
6226         }\r
6227       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6228         appData.noChessProgram = TRUE;\r
6229         appData.icsActive = FALSE;\r
6230       } else {\r
6231         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6232                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6233         return TRUE;\r
6234       }\r
6235       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6236         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6237         p = buf;\r
6238         ParseArgs(StringGet, &p);\r
6239       }\r
6240       EndDialog(hDlg, TRUE);\r
6241       return TRUE;\r
6242 \r
6243     case IDCANCEL:\r
6244       ExitEvent(0);\r
6245       return TRUE;\r
6246 \r
6247     case IDM_HELPCONTENTS:\r
6248       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6249         MessageBox (GetFocus(),\r
6250                     _("Unable to activate help"),\r
6251                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6252       }\r
6253       break;\r
6254 \r
6255     default:\r
6256       SetStartupDialogEnables(hDlg);\r
6257       break;\r
6258     }\r
6259     break;\r
6260   }\r
6261   return FALSE;\r
6262 }\r
6263 \r
6264 /*---------------------------------------------------------------------------*\\r
6265  *\r
6266  * About box dialog functions\r
6267  *\r
6268 \*---------------------------------------------------------------------------*/\r
6269 \r
6270 /* Process messages for "About" dialog box */\r
6271 LRESULT CALLBACK\r
6272 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6273 {\r
6274   switch (message) {\r
6275   case WM_INITDIALOG: /* message: initialize dialog box */\r
6276     /* Center the dialog over the application window */\r
6277     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6278     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6279     Translate(hDlg, ABOUTBOX);\r
6280     JAWS_COPYRIGHT\r
6281     return (TRUE);\r
6282 \r
6283   case WM_COMMAND: /* message: received a command */\r
6284     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6285         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6286       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6287       return (TRUE);\r
6288     }\r
6289     break;\r
6290   }\r
6291   return (FALSE);\r
6292 }\r
6293 \r
6294 /*---------------------------------------------------------------------------*\\r
6295  *\r
6296  * Comment Dialog functions\r
6297  *\r
6298 \*---------------------------------------------------------------------------*/\r
6299 \r
6300 LRESULT CALLBACK\r
6301 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6302 {\r
6303   static HANDLE hwndText = NULL;\r
6304   int len, newSizeX, newSizeY, flags;\r
6305   static int sizeX, sizeY;\r
6306   char *str;\r
6307   RECT rect;\r
6308   MINMAXINFO *mmi;\r
6309 \r
6310   switch (message) {\r
6311   case WM_INITDIALOG: /* message: initialize dialog box */\r
6312     /* Initialize the dialog items */\r
6313     Translate(hDlg, DLG_EditComment);\r
6314     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6315     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6316     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6317     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6318     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6319     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6320     SetWindowText(hDlg, commentTitle);\r
6321     if (editComment) {\r
6322       SetFocus(hwndText);\r
6323     } else {\r
6324       SetFocus(GetDlgItem(hDlg, IDOK));\r
6325     }\r
6326     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6327                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6328                 MAKELPARAM(FALSE, 0));\r
6329     /* Size and position the dialog */\r
6330     if (!commentDialog) {\r
6331       commentDialog = hDlg;\r
6332       flags = SWP_NOZORDER;\r
6333       GetClientRect(hDlg, &rect);\r
6334       sizeX = rect.right;\r
6335       sizeY = rect.bottom;\r
6336       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6337           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6338         WINDOWPLACEMENT wp;\r
6339         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6340         wp.length = sizeof(WINDOWPLACEMENT);\r
6341         wp.flags = 0;\r
6342         wp.showCmd = SW_SHOW;\r
6343         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6344         wp.rcNormalPosition.left = wpComment.x;\r
6345         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6346         wp.rcNormalPosition.top = wpComment.y;\r
6347         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6348         SetWindowPlacement(hDlg, &wp);\r
6349 \r
6350         GetClientRect(hDlg, &rect);\r
6351         newSizeX = rect.right;\r
6352         newSizeY = rect.bottom;\r
6353         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6354                               newSizeX, newSizeY);\r
6355         sizeX = newSizeX;\r
6356         sizeY = newSizeY;\r
6357       }\r
6358     }\r
6359     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6360     return FALSE;\r
6361 \r
6362   case WM_COMMAND: /* message: received a command */\r
6363     switch (LOWORD(wParam)) {\r
6364     case IDOK:\r
6365       if (editComment) {\r
6366         char *p, *q;\r
6367         /* Read changed options from the dialog box */\r
6368         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6369         len = GetWindowTextLength(hwndText);\r
6370         str = (char *) malloc(len + 1);\r
6371         GetWindowText(hwndText, str, len + 1);\r
6372         p = q = str;\r
6373         while (*q) {\r
6374           if (*q == '\r')\r
6375             q++;\r
6376           else\r
6377             *p++ = *q++;\r
6378         }\r
6379         *p = NULLCHAR;\r
6380         ReplaceComment(commentIndex, str);\r
6381         free(str);\r
6382       }\r
6383       CommentPopDown();\r
6384       return TRUE;\r
6385 \r
6386     case IDCANCEL:\r
6387     case OPT_CancelComment:\r
6388       CommentPopDown();\r
6389       return TRUE;\r
6390 \r
6391     case OPT_ClearComment:\r
6392       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6393       break;\r
6394 \r
6395     case OPT_EditComment:\r
6396       EditCommentEvent();\r
6397       return TRUE;\r
6398 \r
6399     default:\r
6400       break;\r
6401     }\r
6402     break;\r
6403 \r
6404   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6405         if( wParam == OPT_CommentText ) {\r
6406             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6407 \r
6408             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6409                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6410                 POINTL pt;\r
6411                 LRESULT index;\r
6412 \r
6413                 pt.x = LOWORD( lpMF->lParam );\r
6414                 pt.y = HIWORD( lpMF->lParam );\r
6415 \r
6416                 if(lpMF->msg == WM_CHAR) {\r
6417                         CHARRANGE sel;\r
6418                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6419                         index = sel.cpMin;\r
6420                 } else\r
6421                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6422 \r
6423                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6424                 len = GetWindowTextLength(hwndText);\r
6425                 str = (char *) malloc(len + 1);\r
6426                 GetWindowText(hwndText, str, len + 1);\r
6427                 ReplaceComment(commentIndex, str);\r
6428                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6429                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6430                 free(str);\r
6431 \r
6432                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6433                 lpMF->msg = WM_USER;\r
6434 \r
6435                 return TRUE;\r
6436             }\r
6437         }\r
6438         break;\r
6439 \r
6440   case WM_SIZE:\r
6441     newSizeX = LOWORD(lParam);\r
6442     newSizeY = HIWORD(lParam);\r
6443     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6444     sizeX = newSizeX;\r
6445     sizeY = newSizeY;\r
6446     break;\r
6447 \r
6448   case WM_GETMINMAXINFO:\r
6449     /* Prevent resizing window too small */\r
6450     mmi = (MINMAXINFO *) lParam;\r
6451     mmi->ptMinTrackSize.x = 100;\r
6452     mmi->ptMinTrackSize.y = 100;\r
6453     break;\r
6454   }\r
6455   return FALSE;\r
6456 }\r
6457 \r
6458 VOID\r
6459 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6460 {\r
6461   FARPROC lpProc;\r
6462   char *p, *q;\r
6463 \r
6464   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6465 \r
6466   if (str == NULL) str = "";\r
6467   p = (char *) malloc(2 * strlen(str) + 2);\r
6468   q = p;\r
6469   while (*str) {\r
6470     if (*str == '\n') *q++ = '\r';\r
6471     *q++ = *str++;\r
6472   }\r
6473   *q = NULLCHAR;\r
6474   if (commentText != NULL) free(commentText);\r
6475 \r
6476   commentIndex = index;\r
6477   commentTitle = title;\r
6478   commentText = p;\r
6479   editComment = edit;\r
6480 \r
6481   if (commentDialog) {\r
6482     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6483     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6484   } else {\r
6485     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6486     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6487                  hwndMain, (DLGPROC)lpProc);\r
6488     FreeProcInstance(lpProc);\r
6489   }\r
6490   commentUp = TRUE;\r
6491 }\r
6492 \r
6493 \r
6494 /*---------------------------------------------------------------------------*\\r
6495  *\r
6496  * Type-in move dialog functions\r
6497  * \r
6498 \*---------------------------------------------------------------------------*/\r
6499 \r
6500 LRESULT CALLBACK\r
6501 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6502 {\r
6503   char move[MSG_SIZ];\r
6504   HWND hInput;\r
6505 \r
6506   switch (message) {\r
6507   case WM_INITDIALOG:\r
6508     move[0] = (char) lParam;\r
6509     move[1] = NULLCHAR;\r
6510     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6511     Translate(hDlg, DLG_TypeInMove);\r
6512     hInput = GetDlgItem(hDlg, OPT_Move);\r
6513     SetWindowText(hInput, move);\r
6514     SetFocus(hInput);\r
6515     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6516     return FALSE;\r
6517 \r
6518   case WM_COMMAND:\r
6519     switch (LOWORD(wParam)) {\r
6520     case IDOK:\r
6521 \r
6522       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6523       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6524       TypeInDoneEvent(move);\r
6525       EndDialog(hDlg, TRUE);\r
6526       return TRUE;\r
6527     case IDCANCEL:\r
6528       EndDialog(hDlg, FALSE);\r
6529       return TRUE;\r
6530     default:\r
6531       break;\r
6532     }\r
6533     break;\r
6534   }\r
6535   return FALSE;\r
6536 }\r
6537 \r
6538 VOID\r
6539 PopUpMoveDialog(char firstchar)\r
6540 {\r
6541     FARPROC lpProc;\r
6542 \r
6543       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6544       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6545         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6546       FreeProcInstance(lpProc);\r
6547 }\r
6548 \r
6549 /*---------------------------------------------------------------------------*\\r
6550  *\r
6551  * Type-in name dialog functions\r
6552  * \r
6553 \*---------------------------------------------------------------------------*/\r
6554 \r
6555 LRESULT CALLBACK\r
6556 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6557 {\r
6558   char move[MSG_SIZ];\r
6559   HWND hInput;\r
6560 \r
6561   switch (message) {\r
6562   case WM_INITDIALOG:\r
6563     move[0] = (char) lParam;\r
6564     move[1] = NULLCHAR;\r
6565     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6566     Translate(hDlg, DLG_TypeInName);\r
6567     hInput = GetDlgItem(hDlg, OPT_Name);\r
6568     SetWindowText(hInput, move);\r
6569     SetFocus(hInput);\r
6570     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6571     return FALSE;\r
6572 \r
6573   case WM_COMMAND:\r
6574     switch (LOWORD(wParam)) {\r
6575     case IDOK:\r
6576       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6577       appData.userName = strdup(move);\r
6578       SetUserLogo();\r
6579       SetGameInfo();\r
6580       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6581         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6582         DisplayTitle(move);\r
6583       }\r
6584 \r
6585 \r
6586       EndDialog(hDlg, TRUE);\r
6587       return TRUE;\r
6588     case IDCANCEL:\r
6589       EndDialog(hDlg, FALSE);\r
6590       return TRUE;\r
6591     default:\r
6592       break;\r
6593     }\r
6594     break;\r
6595   }\r
6596   return FALSE;\r
6597 }\r
6598 \r
6599 VOID\r
6600 PopUpNameDialog(char firstchar)\r
6601 {\r
6602     FARPROC lpProc;\r
6603     \r
6604       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6605       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6606         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6607       FreeProcInstance(lpProc);\r
6608 }\r
6609 \r
6610 /*---------------------------------------------------------------------------*\\r
6611  *\r
6612  *  Error dialogs\r
6613  * \r
6614 \*---------------------------------------------------------------------------*/\r
6615 \r
6616 /* Nonmodal error box */\r
6617 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6618                              WPARAM wParam, LPARAM lParam);\r
6619 \r
6620 VOID\r
6621 ErrorPopUp(char *title, char *content)\r
6622 {\r
6623   FARPROC lpProc;\r
6624   char *p, *q;\r
6625   BOOLEAN modal = hwndMain == NULL;\r
6626 \r
6627   p = content;\r
6628   q = errorMessage;\r
6629   while (*p) {\r
6630     if (*p == '\n') {\r
6631       if (modal) {\r
6632         *q++ = ' ';\r
6633         p++;\r
6634       } else {\r
6635         *q++ = '\r';\r
6636         *q++ = *p++;\r
6637       }\r
6638     } else {\r
6639       *q++ = *p++;\r
6640     }\r
6641   }\r
6642   *q = NULLCHAR;\r
6643   strncpy(errorTitle, title, sizeof(errorTitle));\r
6644   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6645   \r
6646   if (modal) {\r
6647     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6648   } else {\r
6649     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6650     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6651                  hwndMain, (DLGPROC)lpProc);\r
6652     FreeProcInstance(lpProc);\r
6653   }\r
6654 }\r
6655 \r
6656 VOID\r
6657 ErrorPopDown()\r
6658 {\r
6659   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6660   if (errorDialog == NULL) return;\r
6661   DestroyWindow(errorDialog);\r
6662   errorDialog = NULL;\r
6663   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6664 }\r
6665 \r
6666 LRESULT CALLBACK\r
6667 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6668 {\r
6669   HANDLE hwndText;\r
6670   RECT rChild;\r
6671 \r
6672   switch (message) {\r
6673   case WM_INITDIALOG:\r
6674     GetWindowRect(hDlg, &rChild);\r
6675 \r
6676     /*\r
6677     SetWindowPos(hDlg, NULL, rChild.left,\r
6678       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6679       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6680     */\r
6681 \r
6682     /* \r
6683         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6684         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6685         and it doesn't work when you resize the dialog.\r
6686         For now, just give it a default position.\r
6687     */\r
6688     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6689     Translate(hDlg, DLG_Error);\r
6690 \r
6691     errorDialog = hDlg;\r
6692     SetWindowText(hDlg, errorTitle);\r
6693     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6694     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6695     return FALSE;\r
6696 \r
6697   case WM_COMMAND:\r
6698     switch (LOWORD(wParam)) {\r
6699     case IDOK:\r
6700     case IDCANCEL:\r
6701       if (errorDialog == hDlg) errorDialog = NULL;\r
6702       DestroyWindow(hDlg);\r
6703       return TRUE;\r
6704 \r
6705     default:\r
6706       break;\r
6707     }\r
6708     break;\r
6709   }\r
6710   return FALSE;\r
6711 }\r
6712 \r
6713 #ifdef GOTHIC\r
6714 HWND gothicDialog = NULL;\r
6715 \r
6716 LRESULT CALLBACK\r
6717 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6718 {\r
6719   HANDLE hwndText;\r
6720   RECT rChild;\r
6721   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6722 \r
6723   switch (message) {\r
6724   case WM_INITDIALOG:\r
6725     GetWindowRect(hDlg, &rChild);\r
6726 \r
6727     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6728                                                              SWP_NOZORDER);\r
6729 \r
6730     /* \r
6731         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6732         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6733         and it doesn't work when you resize the dialog.\r
6734         For now, just give it a default position.\r
6735     */\r
6736     gothicDialog = hDlg;\r
6737     SetWindowText(hDlg, errorTitle);\r
6738     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6739     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6740     return FALSE;\r
6741 \r
6742   case WM_COMMAND:\r
6743     switch (LOWORD(wParam)) {\r
6744     case IDOK:\r
6745     case IDCANCEL:\r
6746       if (errorDialog == hDlg) errorDialog = NULL;\r
6747       DestroyWindow(hDlg);\r
6748       return TRUE;\r
6749 \r
6750     default:\r
6751       break;\r
6752     }\r
6753     break;\r
6754   }\r
6755   return FALSE;\r
6756 }\r
6757 \r
6758 VOID\r
6759 GothicPopUp(char *title, VariantClass variant)\r
6760 {\r
6761   FARPROC lpProc;\r
6762   static char *lastTitle;\r
6763 \r
6764   strncpy(errorTitle, title, sizeof(errorTitle));\r
6765   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6766 \r
6767   if(lastTitle != title && gothicDialog != NULL) {\r
6768     DestroyWindow(gothicDialog);\r
6769     gothicDialog = NULL;\r
6770   }\r
6771   if(variant != VariantNormal && gothicDialog == NULL) {\r
6772     title = lastTitle;\r
6773     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6774     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6775                  hwndMain, (DLGPROC)lpProc);\r
6776     FreeProcInstance(lpProc);\r
6777   }\r
6778 }\r
6779 #endif\r
6780 \r
6781 /*---------------------------------------------------------------------------*\\r
6782  *\r
6783  *  Ics Interaction console functions\r
6784  *\r
6785 \*---------------------------------------------------------------------------*/\r
6786 \r
6787 #define HISTORY_SIZE 64\r
6788 static char *history[HISTORY_SIZE];\r
6789 int histIn = 0, histP = 0;\r
6790 \r
6791 VOID\r
6792 SaveInHistory(char *cmd)\r
6793 {\r
6794   if (history[histIn] != NULL) {\r
6795     free(history[histIn]);\r
6796     history[histIn] = NULL;\r
6797   }\r
6798   if (*cmd == NULLCHAR) return;\r
6799   history[histIn] = StrSave(cmd);\r
6800   histIn = (histIn + 1) % HISTORY_SIZE;\r
6801   if (history[histIn] != NULL) {\r
6802     free(history[histIn]);\r
6803     history[histIn] = NULL;\r
6804   }\r
6805   histP = histIn;\r
6806 }\r
6807 \r
6808 char *\r
6809 PrevInHistory(char *cmd)\r
6810 {\r
6811   int newhp;\r
6812   if (histP == histIn) {\r
6813     if (history[histIn] != NULL) free(history[histIn]);\r
6814     history[histIn] = StrSave(cmd);\r
6815   }\r
6816   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6817   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6818   histP = newhp;\r
6819   return history[histP];\r
6820 }\r
6821 \r
6822 char *\r
6823 NextInHistory()\r
6824 {\r
6825   if (histP == histIn) return NULL;\r
6826   histP = (histP + 1) % HISTORY_SIZE;\r
6827   return history[histP];   \r
6828 }\r
6829 \r
6830 HMENU\r
6831 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6832 {\r
6833   HMENU hmenu, h;\r
6834   int i = 0;\r
6835   hmenu = LoadMenu(hInst, "TextMenu");\r
6836   h = GetSubMenu(hmenu, 0);\r
6837   while (e->item) {\r
6838     if (strcmp(e->item, "-") == 0) {\r
6839       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6840     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6841       int flags = MF_STRING, j = 0;\r
6842       if (e->item[0] == '|') {\r
6843         flags |= MF_MENUBARBREAK;\r
6844         j++;\r
6845       }\r
6846       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6847       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6848     }\r
6849     e++;\r
6850     i++;\r
6851   } \r
6852   return hmenu;\r
6853 }\r
6854 \r
6855 WNDPROC consoleTextWindowProc;\r
6856 \r
6857 void\r
6858 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6859 {\r
6860   char buf[MSG_SIZ], name[MSG_SIZ];\r
6861   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6862   CHARRANGE sel;\r
6863 \r
6864   if (!getname) {\r
6865     SetWindowText(hInput, command);\r
6866     if (immediate) {\r
6867       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6868     } else {\r
6869       sel.cpMin = 999999;\r
6870       sel.cpMax = 999999;\r
6871       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6872       SetFocus(hInput);\r
6873     }\r
6874     return;\r
6875   }    \r
6876   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6877   if (sel.cpMin == sel.cpMax) {\r
6878     /* Expand to surrounding word */\r
6879     TEXTRANGE tr;\r
6880     do {\r
6881       tr.chrg.cpMax = sel.cpMin;\r
6882       tr.chrg.cpMin = --sel.cpMin;\r
6883       if (sel.cpMin < 0) break;\r
6884       tr.lpstrText = name;\r
6885       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6886     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6887     sel.cpMin++;\r
6888 \r
6889     do {\r
6890       tr.chrg.cpMin = sel.cpMax;\r
6891       tr.chrg.cpMax = ++sel.cpMax;\r
6892       tr.lpstrText = name;\r
6893       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6894     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6895     sel.cpMax--;\r
6896 \r
6897     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6898       MessageBeep(MB_ICONEXCLAMATION);\r
6899       return;\r
6900     }\r
6901     tr.chrg = sel;\r
6902     tr.lpstrText = name;\r
6903     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6904   } else {\r
6905     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6906       MessageBeep(MB_ICONEXCLAMATION);\r
6907       return;\r
6908     }\r
6909     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6910   }\r
6911   if (immediate) {\r
6912     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6913     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6914     SetWindowText(hInput, buf);\r
6915     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6916   } else {\r
6917     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6918       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6919     SetWindowText(hInput, buf);\r
6920     sel.cpMin = 999999;\r
6921     sel.cpMax = 999999;\r
6922     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6923     SetFocus(hInput);\r
6924   }\r
6925 }\r
6926 \r
6927 LRESULT CALLBACK \r
6928 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6929 {\r
6930   HWND hInput;\r
6931   CHARRANGE sel;\r
6932 \r
6933   switch (message) {\r
6934   case WM_KEYDOWN:\r
6935     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6936     if(wParam=='R') return 0;\r
6937     switch (wParam) {\r
6938     case VK_PRIOR:\r
6939       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6940       return 0;\r
6941     case VK_NEXT:\r
6942       sel.cpMin = 999999;\r
6943       sel.cpMax = 999999;\r
6944       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6945       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6946       return 0;\r
6947     }\r
6948     break;\r
6949   case WM_CHAR:\r
6950    if(wParam != '\022') {\r
6951     if (wParam == '\t') {\r
6952       if (GetKeyState(VK_SHIFT) < 0) {\r
6953         /* shifted */\r
6954         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6955         if (buttonDesc[0].hwnd) {\r
6956           SetFocus(buttonDesc[0].hwnd);\r
6957         } else {\r
6958           SetFocus(hwndMain);\r
6959         }\r
6960       } else {\r
6961         /* unshifted */\r
6962         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6963       }\r
6964     } else {\r
6965       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6966       JAWS_DELETE( SetFocus(hInput); )\r
6967       SendMessage(hInput, message, wParam, lParam);\r
6968     }\r
6969     return 0;\r
6970    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6971    lParam = -1;\r
6972   case WM_RBUTTONDOWN:\r
6973     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6974       /* Move selection here if it was empty */\r
6975       POINT pt;\r
6976       pt.x = LOWORD(lParam);\r
6977       pt.y = HIWORD(lParam);\r
6978       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6979       if (sel.cpMin == sel.cpMax) {\r
6980         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6981         sel.cpMax = sel.cpMin;\r
6982         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6983       }\r
6984       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6985 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6986       POINT pt;\r
6987       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6988       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6989       if (sel.cpMin == sel.cpMax) {\r
6990         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6991         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6992       }\r
6993       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6994         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6995       }\r
6996       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
6997       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
6998       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
6999       MenuPopup(hwnd, pt, hmenu, -1);\r
7000 }\r
7001     }\r
7002     return 0;\r
7003   case WM_RBUTTONUP:\r
7004     if (GetKeyState(VK_SHIFT) & ~1) {\r
7005       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7006         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7007     }\r
7008     return 0;\r
7009   case WM_PASTE:\r
7010     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7011     SetFocus(hInput);\r
7012     return SendMessage(hInput, message, wParam, lParam);\r
7013   case WM_MBUTTONDOWN:\r
7014     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7015   case WM_COMMAND:\r
7016     switch (LOWORD(wParam)) {\r
7017     case IDM_QuickPaste:\r
7018       {\r
7019         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7020         if (sel.cpMin == sel.cpMax) {\r
7021           MessageBeep(MB_ICONEXCLAMATION);\r
7022           return 0;\r
7023         }\r
7024         SendMessage(hwnd, WM_COPY, 0, 0);\r
7025         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7026         SendMessage(hInput, WM_PASTE, 0, 0);\r
7027         SetFocus(hInput);\r
7028         return 0;\r
7029       }\r
7030     case IDM_Cut:\r
7031       SendMessage(hwnd, WM_CUT, 0, 0);\r
7032       return 0;\r
7033     case IDM_Paste:\r
7034       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7035       return 0;\r
7036     case IDM_Copy:\r
7037       SendMessage(hwnd, WM_COPY, 0, 0);\r
7038       return 0;\r
7039     default:\r
7040       {\r
7041         int i = LOWORD(wParam) - IDM_CommandX;\r
7042         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7043             icsTextMenuEntry[i].command != NULL) {\r
7044           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7045                    icsTextMenuEntry[i].getname,\r
7046                    icsTextMenuEntry[i].immediate);\r
7047           return 0;\r
7048         }\r
7049       }\r
7050       break;\r
7051     }\r
7052     break;\r
7053   }\r
7054   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7055 }\r
7056 \r
7057 WNDPROC consoleInputWindowProc;\r
7058 \r
7059 LRESULT CALLBACK\r
7060 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7061 {\r
7062   char buf[MSG_SIZ];\r
7063   char *p;\r
7064   static BOOL sendNextChar = FALSE;\r
7065   static BOOL quoteNextChar = FALSE;\r
7066   InputSource *is = consoleInputSource;\r
7067   CHARFORMAT cf;\r
7068   CHARRANGE sel;\r
7069 \r
7070   switch (message) {\r
7071   case WM_CHAR:\r
7072     if (!appData.localLineEditing || sendNextChar) {\r
7073       is->buf[0] = (CHAR) wParam;\r
7074       is->count = 1;\r
7075       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7076       sendNextChar = FALSE;\r
7077       return 0;\r
7078     }\r
7079     if (quoteNextChar) {\r
7080       buf[0] = (char) wParam;\r
7081       buf[1] = NULLCHAR;\r
7082       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7083       quoteNextChar = FALSE;\r
7084       return 0;\r
7085     }\r
7086     switch (wParam) {\r
7087     case '\r':   /* Enter key */\r
7088       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7089       if (consoleEcho) SaveInHistory(is->buf);\r
7090       is->buf[is->count++] = '\n';\r
7091       is->buf[is->count] = NULLCHAR;\r
7092       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7093       if (consoleEcho) {\r
7094         ConsoleOutput(is->buf, is->count, TRUE);\r
7095       } else if (appData.localLineEditing) {\r
7096         ConsoleOutput("\n", 1, TRUE);\r
7097       }\r
7098       /* fall thru */\r
7099     case '\033': /* Escape key */\r
7100       SetWindowText(hwnd, "");\r
7101       cf.cbSize = sizeof(CHARFORMAT);\r
7102       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7103       if (consoleEcho) {\r
7104         cf.crTextColor = textAttribs[ColorNormal].color;\r
7105       } else {\r
7106         cf.crTextColor = COLOR_ECHOOFF;\r
7107       }\r
7108       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7109       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7110       return 0;\r
7111     case '\t':   /* Tab key */\r
7112       if (GetKeyState(VK_SHIFT) < 0) {\r
7113         /* shifted */\r
7114         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7115       } else {\r
7116         /* unshifted */\r
7117         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7118         if (buttonDesc[0].hwnd) {\r
7119           SetFocus(buttonDesc[0].hwnd);\r
7120         } else {\r
7121           SetFocus(hwndMain);\r
7122         }\r
7123       }\r
7124       return 0;\r
7125     case '\023': /* Ctrl+S */\r
7126       sendNextChar = TRUE;\r
7127       return 0;\r
7128     case '\021': /* Ctrl+Q */\r
7129       quoteNextChar = TRUE;\r
7130       return 0;\r
7131     JAWS_REPLAY\r
7132     default:\r
7133       break;\r
7134     }\r
7135     break;\r
7136   case WM_KEYDOWN:\r
7137     switch (wParam) {\r
7138     case VK_UP:\r
7139       GetWindowText(hwnd, buf, MSG_SIZ);\r
7140       p = PrevInHistory(buf);\r
7141       if (p != NULL) {\r
7142         SetWindowText(hwnd, p);\r
7143         sel.cpMin = 999999;\r
7144         sel.cpMax = 999999;\r
7145         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7146         return 0;\r
7147       }\r
7148       break;\r
7149     case VK_DOWN:\r
7150       p = NextInHistory();\r
7151       if (p != NULL) {\r
7152         SetWindowText(hwnd, p);\r
7153         sel.cpMin = 999999;\r
7154         sel.cpMax = 999999;\r
7155         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7156         return 0;\r
7157       }\r
7158       break;\r
7159     case VK_HOME:\r
7160     case VK_END:\r
7161       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7162       /* fall thru */\r
7163     case VK_PRIOR:\r
7164     case VK_NEXT:\r
7165       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7166       return 0;\r
7167     }\r
7168     break;\r
7169   case WM_MBUTTONDOWN:\r
7170     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7171       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7172     break;\r
7173   case WM_RBUTTONUP:\r
7174     if (GetKeyState(VK_SHIFT) & ~1) {\r
7175       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7176         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7177     } else {\r
7178       POINT pt;\r
7179       HMENU hmenu;\r
7180       hmenu = LoadMenu(hInst, "InputMenu");\r
7181       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7182       if (sel.cpMin == sel.cpMax) {\r
7183         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7184         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7185       }\r
7186       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7187         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7188       }\r
7189       pt.x = LOWORD(lParam);\r
7190       pt.y = HIWORD(lParam);\r
7191       MenuPopup(hwnd, pt, hmenu, -1);\r
7192     }\r
7193     return 0;\r
7194   case WM_COMMAND:\r
7195     switch (LOWORD(wParam)) { \r
7196     case IDM_Undo:\r
7197       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7198       return 0;\r
7199     case IDM_SelectAll:\r
7200       sel.cpMin = 0;\r
7201       sel.cpMax = -1; /*999999?*/\r
7202       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7203       return 0;\r
7204     case IDM_Cut:\r
7205       SendMessage(hwnd, WM_CUT, 0, 0);\r
7206       return 0;\r
7207     case IDM_Paste:\r
7208       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7209       return 0;\r
7210     case IDM_Copy:\r
7211       SendMessage(hwnd, WM_COPY, 0, 0);\r
7212       return 0;\r
7213     }\r
7214     break;\r
7215   }\r
7216   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7217 }\r
7218 \r
7219 #define CO_MAX  100000\r
7220 #define CO_TRIM   1000\r
7221 \r
7222 LRESULT CALLBACK\r
7223 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7224 {\r
7225   static SnapData sd;\r
7226   HWND hText, hInput;\r
7227   RECT rect;\r
7228   static int sizeX, sizeY;\r
7229   int newSizeX, newSizeY;\r
7230   MINMAXINFO *mmi;\r
7231   WORD wMask;\r
7232 \r
7233   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7234   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7235 \r
7236   switch (message) {\r
7237   case WM_NOTIFY:\r
7238     if (((NMHDR*)lParam)->code == EN_LINK)\r
7239     {\r
7240       ENLINK *pLink = (ENLINK*)lParam;\r
7241       if (pLink->msg == WM_LBUTTONUP)\r
7242       {\r
7243         TEXTRANGE tr;\r
7244 \r
7245         tr.chrg = pLink->chrg;\r
7246         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7247         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7248         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7249         free(tr.lpstrText);\r
7250       }\r
7251     }\r
7252     break;\r
7253   case WM_INITDIALOG: /* message: initialize dialog box */\r
7254     hwndConsole = hDlg;\r
7255     SetFocus(hInput);\r
7256     consoleTextWindowProc = (WNDPROC)\r
7257       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7258     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7259     consoleInputWindowProc = (WNDPROC)\r
7260       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7261     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7262     Colorize(ColorNormal, TRUE);\r
7263     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7264     ChangedConsoleFont();\r
7265     GetClientRect(hDlg, &rect);\r
7266     sizeX = rect.right;\r
7267     sizeY = rect.bottom;\r
7268     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7269         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7270       WINDOWPLACEMENT wp;\r
7271       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7272       wp.length = sizeof(WINDOWPLACEMENT);\r
7273       wp.flags = 0;\r
7274       wp.showCmd = SW_SHOW;\r
7275       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7276       wp.rcNormalPosition.left = wpConsole.x;\r
7277       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7278       wp.rcNormalPosition.top = wpConsole.y;\r
7279       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7280       SetWindowPlacement(hDlg, &wp);\r
7281     }\r
7282 \r
7283    // [HGM] Chessknight's change 2004-07-13\r
7284    else { /* Determine Defaults */\r
7285        WINDOWPLACEMENT wp;\r
7286        wpConsole.x = wpMain.width + 1;\r
7287        wpConsole.y = wpMain.y;\r
7288        wpConsole.width = screenWidth -  wpMain.width;\r
7289        wpConsole.height = wpMain.height;\r
7290        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7291        wp.length = sizeof(WINDOWPLACEMENT);\r
7292        wp.flags = 0;\r
7293        wp.showCmd = SW_SHOW;\r
7294        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7295        wp.rcNormalPosition.left = wpConsole.x;\r
7296        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7297        wp.rcNormalPosition.top = wpConsole.y;\r
7298        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7299        SetWindowPlacement(hDlg, &wp);\r
7300     }\r
7301 \r
7302    // Allow hText to highlight URLs and send notifications on them\r
7303    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7304    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7305    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7306    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7307 \r
7308     return FALSE;\r
7309 \r
7310   case WM_SETFOCUS:\r
7311     SetFocus(hInput);\r
7312     return 0;\r
7313 \r
7314   case WM_CLOSE:\r
7315     ExitEvent(0);\r
7316     /* not reached */\r
7317     break;\r
7318 \r
7319   case WM_SIZE:\r
7320     if (IsIconic(hDlg)) break;\r
7321     newSizeX = LOWORD(lParam);\r
7322     newSizeY = HIWORD(lParam);\r
7323     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7324       RECT rectText, rectInput;\r
7325       POINT pt;\r
7326       int newTextHeight, newTextWidth;\r
7327       GetWindowRect(hText, &rectText);\r
7328       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7329       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7330       if (newTextHeight < 0) {\r
7331         newSizeY += -newTextHeight;\r
7332         newTextHeight = 0;\r
7333       }\r
7334       SetWindowPos(hText, NULL, 0, 0,\r
7335         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7336       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7337       pt.x = rectInput.left;\r
7338       pt.y = rectInput.top + newSizeY - sizeY;\r
7339       ScreenToClient(hDlg, &pt);\r
7340       SetWindowPos(hInput, NULL, \r
7341         pt.x, pt.y, /* needs client coords */   \r
7342         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7343         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7344     }\r
7345     sizeX = newSizeX;\r
7346     sizeY = newSizeY;\r
7347     break;\r
7348 \r
7349   case WM_GETMINMAXINFO:\r
7350     /* Prevent resizing window too small */\r
7351     mmi = (MINMAXINFO *) lParam;\r
7352     mmi->ptMinTrackSize.x = 100;\r
7353     mmi->ptMinTrackSize.y = 100;\r
7354     break;\r
7355 \r
7356   /* [AS] Snapping */\r
7357   case WM_ENTERSIZEMOVE:\r
7358     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7359 \r
7360   case WM_SIZING:\r
7361     return OnSizing( &sd, hDlg, wParam, lParam );\r
7362 \r
7363   case WM_MOVING:\r
7364     return OnMoving( &sd, hDlg, wParam, lParam );\r
7365 \r
7366   case WM_EXITSIZEMOVE:\r
7367         UpdateICSWidth(hText);\r
7368     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7369   }\r
7370 \r
7371   return DefWindowProc(hDlg, message, wParam, lParam);\r
7372 }\r
7373 \r
7374 \r
7375 VOID\r
7376 ConsoleCreate()\r
7377 {\r
7378   HWND hCons;\r
7379   if (hwndConsole) return;\r
7380   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7381   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7382 }\r
7383 \r
7384 \r
7385 VOID\r
7386 ConsoleOutput(char* data, int length, int forceVisible)\r
7387 {\r
7388   HWND hText;\r
7389   int trim, exlen;\r
7390   char *p, *q;\r
7391   char buf[CO_MAX+1];\r
7392   POINT pEnd;\r
7393   RECT rect;\r
7394   static int delayLF = 0;\r
7395   CHARRANGE savesel, sel;\r
7396 \r
7397   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7398   p = data;\r
7399   q = buf;\r
7400   if (delayLF) {\r
7401     *q++ = '\r';\r
7402     *q++ = '\n';\r
7403     delayLF = 0;\r
7404   }\r
7405   while (length--) {\r
7406     if (*p == '\n') {\r
7407       if (*++p) {\r
7408         *q++ = '\r';\r
7409         *q++ = '\n';\r
7410       } else {\r
7411         delayLF = 1;\r
7412       }\r
7413     } else if (*p == '\007') {\r
7414        MyPlaySound(&sounds[(int)SoundBell]);\r
7415        p++;\r
7416     } else {\r
7417       *q++ = *p++;\r
7418     }\r
7419   }\r
7420   *q = NULLCHAR;\r
7421   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7422   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7423   /* Save current selection */\r
7424   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7425   exlen = GetWindowTextLength(hText);\r
7426   /* Find out whether current end of text is visible */\r
7427   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7428   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7429   /* Trim existing text if it's too long */\r
7430   if (exlen + (q - buf) > CO_MAX) {\r
7431     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7432     sel.cpMin = 0;\r
7433     sel.cpMax = trim;\r
7434     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7435     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7436     exlen -= trim;\r
7437     savesel.cpMin -= trim;\r
7438     savesel.cpMax -= trim;\r
7439     if (exlen < 0) exlen = 0;\r
7440     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7441     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7442   }\r
7443   /* Append the new text */\r
7444   sel.cpMin = exlen;\r
7445   sel.cpMax = exlen;\r
7446   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7447   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7448   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7449   if (forceVisible || exlen == 0 ||\r
7450       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7451        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7452     /* Scroll to make new end of text visible if old end of text\r
7453        was visible or new text is an echo of user typein */\r
7454     sel.cpMin = 9999999;\r
7455     sel.cpMax = 9999999;\r
7456     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7457     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7458     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7459     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7460   }\r
7461   if (savesel.cpMax == exlen || forceVisible) {\r
7462     /* Move insert point to new end of text if it was at the old\r
7463        end of text or if the new text is an echo of user typein */\r
7464     sel.cpMin = 9999999;\r
7465     sel.cpMax = 9999999;\r
7466     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7467   } else {\r
7468     /* Restore previous selection */\r
7469     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7470   }\r
7471   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7472 }\r
7473 \r
7474 /*---------*/\r
7475 \r
7476 \r
7477 void\r
7478 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7479 {\r
7480   char buf[100];\r
7481   char *str;\r
7482   COLORREF oldFg, oldBg;\r
7483   HFONT oldFont;\r
7484   RECT rect;\r
7485 \r
7486   if(copyNumber > 1)\r
7487     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7488 \r
7489   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7490   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7491   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7492 \r
7493   rect.left = x;\r
7494   rect.right = x + squareSize;\r
7495   rect.top  = y;\r
7496   rect.bottom = y + squareSize;\r
7497   str = buf;\r
7498 \r
7499   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7500                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7501              y, ETO_CLIPPED|ETO_OPAQUE,\r
7502              &rect, str, strlen(str), NULL);\r
7503 \r
7504   (void) SetTextColor(hdc, oldFg);\r
7505   (void) SetBkColor(hdc, oldBg);\r
7506   (void) SelectObject(hdc, oldFont);\r
7507 }\r
7508 \r
7509 void\r
7510 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7511               RECT *rect, char *color, char *flagFell)\r
7512 {\r
7513   char buf[100];\r
7514   char *str;\r
7515   COLORREF oldFg, oldBg;\r
7516   HFONT oldFont;\r
7517 \r
7518   if (twoBoards && partnerUp) return;\r
7519   if (appData.clockMode) {\r
7520     if (tinyLayout)\r
7521       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7522     else\r
7523       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7524     str = buf;\r
7525   } else {\r
7526     str = color;\r
7527   }\r
7528 \r
7529   if (highlight) {\r
7530     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7531     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7532   } else {\r
7533     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7534     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7535   }\r
7536   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7537 \r
7538   JAWS_SILENCE\r
7539 \r
7540   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7541              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7542              rect, str, strlen(str), NULL);\r
7543   if(logoHeight > 0 && appData.clockMode) {\r
7544       RECT r;\r
7545       str += strlen(color)+2;\r
7546       r.top = rect->top + logoHeight/2;\r
7547       r.left = rect->left;\r
7548       r.right = rect->right;\r
7549       r.bottom = rect->bottom;\r
7550       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7551                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7552                  &r, str, strlen(str), NULL);\r
7553   }\r
7554   (void) SetTextColor(hdc, oldFg);\r
7555   (void) SetBkColor(hdc, oldBg);\r
7556   (void) SelectObject(hdc, oldFont);\r
7557 }\r
7558 \r
7559 \r
7560 int\r
7561 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7562            OVERLAPPED *ovl)\r
7563 {\r
7564   int ok, err;\r
7565 \r
7566   /* [AS]  */\r
7567   if( count <= 0 ) {\r
7568     if (appData.debugMode) {\r
7569       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7570     }\r
7571 \r
7572     return ERROR_INVALID_USER_BUFFER;\r
7573   }\r
7574 \r
7575   ResetEvent(ovl->hEvent);\r
7576   ovl->Offset = ovl->OffsetHigh = 0;\r
7577   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7578   if (ok) {\r
7579     err = NO_ERROR;\r
7580   } else {\r
7581     err = GetLastError();\r
7582     if (err == ERROR_IO_PENDING) {\r
7583       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7584       if (ok)\r
7585         err = NO_ERROR;\r
7586       else\r
7587         err = GetLastError();\r
7588     }\r
7589   }\r
7590   return err;\r
7591 }\r
7592 \r
7593 int\r
7594 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7595             OVERLAPPED *ovl)\r
7596 {\r
7597   int ok, err;\r
7598 \r
7599   ResetEvent(ovl->hEvent);\r
7600   ovl->Offset = ovl->OffsetHigh = 0;\r
7601   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7602   if (ok) {\r
7603     err = NO_ERROR;\r
7604   } else {\r
7605     err = GetLastError();\r
7606     if (err == ERROR_IO_PENDING) {\r
7607       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7608       if (ok)\r
7609         err = NO_ERROR;\r
7610       else\r
7611         err = GetLastError();\r
7612     }\r
7613   }\r
7614   return err;\r
7615 }\r
7616 \r
7617 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7618 void CheckForInputBufferFull( InputSource * is )\r
7619 {\r
7620     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7621         /* Look for end of line */\r
7622         char * p = is->buf;\r
7623         \r
7624         while( p < is->next && *p != '\n' ) {\r
7625             p++;\r
7626         }\r
7627 \r
7628         if( p >= is->next ) {\r
7629             if (appData.debugMode) {\r
7630                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7631             }\r
7632 \r
7633             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7634             is->count = (DWORD) -1;\r
7635             is->next = is->buf;\r
7636         }\r
7637     }\r
7638 }\r
7639 \r
7640 DWORD\r
7641 InputThread(LPVOID arg)\r
7642 {\r
7643   InputSource *is;\r
7644   OVERLAPPED ovl;\r
7645 \r
7646   is = (InputSource *) arg;\r
7647   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7648   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7649   while (is->hThread != NULL) {\r
7650     is->error = DoReadFile(is->hFile, is->next,\r
7651                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7652                            &is->count, &ovl);\r
7653     if (is->error == NO_ERROR) {\r
7654       is->next += is->count;\r
7655     } else {\r
7656       if (is->error == ERROR_BROKEN_PIPE) {\r
7657         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7658         is->count = 0;\r
7659       } else {\r
7660         is->count = (DWORD) -1;\r
7661         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7662         break; \r
7663       }\r
7664     }\r
7665 \r
7666     CheckForInputBufferFull( is );\r
7667 \r
7668     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7669 \r
7670     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7671 \r
7672     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7673   }\r
7674 \r
7675   CloseHandle(ovl.hEvent);\r
7676   CloseHandle(is->hFile);\r
7677 \r
7678   if (appData.debugMode) {\r
7679     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7680   }\r
7681 \r
7682   return 0;\r
7683 }\r
7684 \r
7685 \r
7686 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7687 DWORD\r
7688 NonOvlInputThread(LPVOID arg)\r
7689 {\r
7690   InputSource *is;\r
7691   char *p, *q;\r
7692   int i;\r
7693   char prev;\r
7694 \r
7695   is = (InputSource *) arg;\r
7696   while (is->hThread != NULL) {\r
7697     is->error = ReadFile(is->hFile, is->next,\r
7698                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7699                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7700     if (is->error == NO_ERROR) {\r
7701       /* Change CRLF to LF */\r
7702       if (is->next > is->buf) {\r
7703         p = is->next - 1;\r
7704         i = is->count + 1;\r
7705       } else {\r
7706         p = is->next;\r
7707         i = is->count;\r
7708       }\r
7709       q = p;\r
7710       prev = NULLCHAR;\r
7711       while (i > 0) {\r
7712         if (prev == '\r' && *p == '\n') {\r
7713           *(q-1) = '\n';\r
7714           is->count--;\r
7715         } else { \r
7716           *q++ = *p;\r
7717         }\r
7718         prev = *p++;\r
7719         i--;\r
7720       }\r
7721       *q = NULLCHAR;\r
7722       is->next = q;\r
7723     } else {\r
7724       if (is->error == ERROR_BROKEN_PIPE) {\r
7725         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7726         is->count = 0; \r
7727       } else {\r
7728         is->count = (DWORD) -1;\r
7729       }\r
7730     }\r
7731 \r
7732     CheckForInputBufferFull( is );\r
7733 \r
7734     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7735 \r
7736     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7737 \r
7738     if (is->count < 0) break;  /* Quit on error */\r
7739   }\r
7740   CloseHandle(is->hFile);\r
7741   return 0;\r
7742 }\r
7743 \r
7744 DWORD\r
7745 SocketInputThread(LPVOID arg)\r
7746 {\r
7747   InputSource *is;\r
7748 \r
7749   is = (InputSource *) arg;\r
7750   while (is->hThread != NULL) {\r
7751     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7752     if ((int)is->count == SOCKET_ERROR) {\r
7753       is->count = (DWORD) -1;\r
7754       is->error = WSAGetLastError();\r
7755     } else {\r
7756       is->error = NO_ERROR;\r
7757       is->next += is->count;\r
7758       if (is->count == 0 && is->second == is) {\r
7759         /* End of file on stderr; quit with no message */\r
7760         break;\r
7761       }\r
7762     }\r
7763     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7764 \r
7765     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7766 \r
7767     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7768   }\r
7769   return 0;\r
7770 }\r
7771 \r
7772 VOID\r
7773 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7774 {\r
7775   InputSource *is;\r
7776 \r
7777   is = (InputSource *) lParam;\r
7778   if (is->lineByLine) {\r
7779     /* Feed in lines one by one */\r
7780     char *p = is->buf;\r
7781     char *q = p;\r
7782     while (q < is->next) {\r
7783       if (*q++ == '\n') {\r
7784         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7785         p = q;\r
7786       }\r
7787     }\r
7788     \r
7789     /* Move any partial line to the start of the buffer */\r
7790     q = is->buf;\r
7791     while (p < is->next) {\r
7792       *q++ = *p++;\r
7793     }\r
7794     is->next = q;\r
7795 \r
7796     if (is->error != NO_ERROR || is->count == 0) {\r
7797       /* Notify backend of the error.  Note: If there was a partial\r
7798          line at the end, it is not flushed through. */\r
7799       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7800     }\r
7801   } else {\r
7802     /* Feed in the whole chunk of input at once */\r
7803     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7804     is->next = is->buf;\r
7805   }\r
7806 }\r
7807 \r
7808 /*---------------------------------------------------------------------------*\\r
7809  *\r
7810  *  Menu enables. Used when setting various modes.\r
7811  *\r
7812 \*---------------------------------------------------------------------------*/\r
7813 \r
7814 typedef struct {\r
7815   int item;\r
7816   int flags;\r
7817 } Enables;\r
7818 \r
7819 VOID\r
7820 GreyRevert(Boolean grey)\r
7821 { // [HGM] vari: for retracting variations in local mode\r
7822   HMENU hmenu = GetMenu(hwndMain);\r
7823   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7824   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7825 }\r
7826 \r
7827 VOID\r
7828 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7829 {\r
7830   while (enab->item > 0) {\r
7831     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7832     enab++;\r
7833   }\r
7834 }\r
7835 \r
7836 Enables gnuEnables[] = {\r
7837   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7850 \r
7851   // Needed to switch from ncp to GNU mode on Engine Load\r
7852   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7853   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7854   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7866   { -1, -1 }\r
7867 };\r
7868 \r
7869 Enables icsEnables[] = {\r
7870   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7871   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7873   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7874   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7875   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7878   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7886   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7890   { -1, -1 }\r
7891 };\r
7892 \r
7893 #if ZIPPY\r
7894 Enables zippyEnables[] = {\r
7895   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7896   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7897   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7898   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7899   { -1, -1 }\r
7900 };\r
7901 #endif\r
7902 \r
7903 Enables ncpEnables[] = {\r
7904   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7906   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7907   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7908   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7909   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7910   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7913   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7914   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7925   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7926   { -1, -1 }\r
7927 };\r
7928 \r
7929 Enables trainingOnEnables[] = {\r
7930   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7939   { -1, -1 }\r
7940 };\r
7941 \r
7942 Enables trainingOffEnables[] = {\r
7943   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7944   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7945   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7946   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7948   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7949   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7950   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7951   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7952   { -1, -1 }\r
7953 };\r
7954 \r
7955 /* These modify either ncpEnables or gnuEnables */\r
7956 Enables cmailEnables[] = {\r
7957   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7958   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7959   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7960   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7962   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7964   { -1, -1 }\r
7965 };\r
7966 \r
7967 Enables machineThinkingEnables[] = {\r
7968   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7981 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7984   { -1, -1 }\r
7985 };\r
7986 \r
7987 Enables userThinkingEnables[] = {\r
7988   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7989   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7990   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7991   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7992   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7993   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7994   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7995   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7996   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7997   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7998   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7999   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8000   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8001 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8002   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8003   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8004   { -1, -1 }\r
8005 };\r
8006 \r
8007 /*---------------------------------------------------------------------------*\\r
8008  *\r
8009  *  Front-end interface functions exported by XBoard.\r
8010  *  Functions appear in same order as prototypes in frontend.h.\r
8011  * \r
8012 \*---------------------------------------------------------------------------*/\r
8013 VOID\r
8014 CheckMark(UINT item, int state)\r
8015 {\r
8016     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8017 }\r
8018 \r
8019 VOID\r
8020 ModeHighlight()\r
8021 {\r
8022   static UINT prevChecked = 0;\r
8023   static int prevPausing = 0;\r
8024   UINT nowChecked;\r
8025 \r
8026   if (pausing != prevPausing) {\r
8027     prevPausing = pausing;\r
8028     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8029                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8030     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8031   }\r
8032 \r
8033   switch (gameMode) {\r
8034   case BeginningOfGame:\r
8035     if (appData.icsActive)\r
8036       nowChecked = IDM_IcsClient;\r
8037     else if (appData.noChessProgram)\r
8038       nowChecked = IDM_EditGame;\r
8039     else\r
8040       nowChecked = IDM_MachineBlack;\r
8041     break;\r
8042   case MachinePlaysBlack:\r
8043     nowChecked = IDM_MachineBlack;\r
8044     break;\r
8045   case MachinePlaysWhite:\r
8046     nowChecked = IDM_MachineWhite;\r
8047     break;\r
8048   case TwoMachinesPlay:\r
8049     nowChecked = IDM_TwoMachines;\r
8050     break;\r
8051   case AnalyzeMode:\r
8052     nowChecked = IDM_AnalysisMode;\r
8053     break;\r
8054   case AnalyzeFile:\r
8055     nowChecked = IDM_AnalyzeFile;\r
8056     break;\r
8057   case EditGame:\r
8058     nowChecked = IDM_EditGame;\r
8059     break;\r
8060   case PlayFromGameFile:\r
8061     nowChecked = IDM_LoadGame;\r
8062     break;\r
8063   case EditPosition:\r
8064     nowChecked = IDM_EditPosition;\r
8065     break;\r
8066   case Training:\r
8067     nowChecked = IDM_Training;\r
8068     break;\r
8069   case IcsPlayingWhite:\r
8070   case IcsPlayingBlack:\r
8071   case IcsObserving:\r
8072   case IcsIdle:\r
8073     nowChecked = IDM_IcsClient;\r
8074     break;\r
8075   default:\r
8076   case EndOfGame:\r
8077     nowChecked = 0;\r
8078     break;\r
8079   }\r
8080   CheckMark(prevChecked, MF_UNCHECKED);\r
8081   CheckMark(nowChecked, MF_CHECKED);\r
8082   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8083 \r
8084   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8085     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8086                           MF_BYCOMMAND|MF_ENABLED);\r
8087   } else {\r
8088     (void) EnableMenuItem(GetMenu(hwndMain), \r
8089                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8090   }\r
8091 \r
8092   prevChecked = nowChecked;\r
8093 \r
8094   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8095   if (appData.icsActive) {\r
8096        if (appData.icsEngineAnalyze) {\r
8097                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8098        } else {\r
8099                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8100        }\r
8101   }\r
8102   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8103 }\r
8104 \r
8105 VOID\r
8106 SetICSMode()\r
8107 {\r
8108   HMENU hmenu = GetMenu(hwndMain);\r
8109   SetMenuEnables(hmenu, icsEnables);\r
8110   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8111     MF_BYCOMMAND|MF_ENABLED);\r
8112 #if ZIPPY\r
8113   if (appData.zippyPlay) {\r
8114     SetMenuEnables(hmenu, zippyEnables);\r
8115     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8116          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8117           MF_BYCOMMAND|MF_ENABLED);\r
8118   }\r
8119 #endif\r
8120 }\r
8121 \r
8122 VOID\r
8123 SetGNUMode()\r
8124 {\r
8125   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8126 }\r
8127 \r
8128 VOID\r
8129 SetNCPMode()\r
8130 {\r
8131   HMENU hmenu = GetMenu(hwndMain);\r
8132   SetMenuEnables(hmenu, ncpEnables);\r
8133     DrawMenuBar(hwndMain);\r
8134 }\r
8135 \r
8136 VOID\r
8137 SetCmailMode()\r
8138 {\r
8139   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8140 }\r
8141 \r
8142 VOID \r
8143 SetTrainingModeOn()\r
8144 {\r
8145   int i;\r
8146   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8147   for (i = 0; i < N_BUTTONS; i++) {\r
8148     if (buttonDesc[i].hwnd != NULL)\r
8149       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8150   }\r
8151   CommentPopDown();\r
8152 }\r
8153 \r
8154 VOID SetTrainingModeOff()\r
8155 {\r
8156   int i;\r
8157   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8158   for (i = 0; i < N_BUTTONS; i++) {\r
8159     if (buttonDesc[i].hwnd != NULL)\r
8160       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8161   }\r
8162 }\r
8163 \r
8164 \r
8165 VOID\r
8166 SetUserThinkingEnables()\r
8167 {\r
8168   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8169 }\r
8170 \r
8171 VOID\r
8172 SetMachineThinkingEnables()\r
8173 {\r
8174   HMENU hMenu = GetMenu(hwndMain);\r
8175   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8176 \r
8177   SetMenuEnables(hMenu, machineThinkingEnables);\r
8178 \r
8179   if (gameMode == MachinePlaysBlack) {\r
8180     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8181   } else if (gameMode == MachinePlaysWhite) {\r
8182     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8183   } else if (gameMode == TwoMachinesPlay) {\r
8184     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8185   }\r
8186 }\r
8187 \r
8188 \r
8189 VOID\r
8190 DisplayTitle(char *str)\r
8191 {\r
8192   char title[MSG_SIZ], *host;\r
8193   if (str[0] != NULLCHAR) {\r
8194     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8195   } else if (appData.icsActive) {\r
8196     if (appData.icsCommPort[0] != NULLCHAR)\r
8197       host = "ICS";\r
8198     else \r
8199       host = appData.icsHost;\r
8200       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8201   } else if (appData.noChessProgram) {\r
8202     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8203   } else {\r
8204     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8205     strcat(title, ": ");\r
8206     strcat(title, first.tidy);\r
8207   }\r
8208   SetWindowText(hwndMain, title);\r
8209 }\r
8210 \r
8211 \r
8212 VOID\r
8213 DisplayMessage(char *str1, char *str2)\r
8214 {\r
8215   HDC hdc;\r
8216   HFONT oldFont;\r
8217   int remain = MESSAGE_TEXT_MAX - 1;\r
8218   int len;\r
8219 \r
8220   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8221   messageText[0] = NULLCHAR;\r
8222   if (*str1) {\r
8223     len = strlen(str1);\r
8224     if (len > remain) len = remain;\r
8225     strncpy(messageText, str1, len);\r
8226     messageText[len] = NULLCHAR;\r
8227     remain -= len;\r
8228   }\r
8229   if (*str2 && remain >= 2) {\r
8230     if (*str1) {\r
8231       strcat(messageText, "  ");\r
8232       remain -= 2;\r
8233     }\r
8234     len = strlen(str2);\r
8235     if (len > remain) len = remain;\r
8236     strncat(messageText, str2, len);\r
8237   }\r
8238   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8239   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8240 \r
8241   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8242 \r
8243   SAYMACHINEMOVE();\r
8244 \r
8245   hdc = GetDC(hwndMain);\r
8246   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8247   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8248              &messageRect, messageText, strlen(messageText), NULL);\r
8249   (void) SelectObject(hdc, oldFont);\r
8250   (void) ReleaseDC(hwndMain, hdc);\r
8251 }\r
8252 \r
8253 VOID\r
8254 DisplayError(char *str, int error)\r
8255 {\r
8256   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8257   int len;\r
8258 \r
8259   if (error == 0) {\r
8260     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8261   } else {\r
8262     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8263                         NULL, error, LANG_NEUTRAL,\r
8264                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8265     if (len > 0) {\r
8266       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8267     } else {\r
8268       ErrorMap *em = errmap;\r
8269       while (em->err != 0 && em->err != error) em++;\r
8270       if (em->err != 0) {\r
8271         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8272       } else {\r
8273         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8274       }\r
8275     }\r
8276   }\r
8277   \r
8278   ErrorPopUp(_("Error"), buf);\r
8279 }\r
8280 \r
8281 \r
8282 VOID\r
8283 DisplayMoveError(char *str)\r
8284 {\r
8285   fromX = fromY = -1;\r
8286   ClearHighlights();\r
8287   DrawPosition(FALSE, NULL);\r
8288   if (appData.popupMoveErrors) {\r
8289     ErrorPopUp(_("Error"), str);\r
8290   } else {\r
8291     DisplayMessage(str, "");\r
8292     moveErrorMessageUp = TRUE;\r
8293   }\r
8294 }\r
8295 \r
8296 VOID\r
8297 DisplayFatalError(char *str, int error, int exitStatus)\r
8298 {\r
8299   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8300   int len;\r
8301   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8302 \r
8303   if (error != 0) {\r
8304     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8305                         NULL, error, LANG_NEUTRAL,\r
8306                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8307     if (len > 0) {\r
8308       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8309     } else {\r
8310       ErrorMap *em = errmap;\r
8311       while (em->err != 0 && em->err != error) em++;\r
8312       if (em->err != 0) {\r
8313         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8314       } else {\r
8315         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8316       }\r
8317     }\r
8318     str = buf;\r
8319   }\r
8320   if (appData.debugMode) {\r
8321     fprintf(debugFP, "%s: %s\n", label, str);\r
8322   }\r
8323   if (appData.popupExitMessage) {\r
8324     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8325                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8326   }\r
8327   ExitEvent(exitStatus);\r
8328 }\r
8329 \r
8330 \r
8331 VOID\r
8332 DisplayInformation(char *str)\r
8333 {\r
8334   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8335 }\r
8336 \r
8337 \r
8338 VOID\r
8339 DisplayNote(char *str)\r
8340 {\r
8341   ErrorPopUp(_("Note"), str);\r
8342 }\r
8343 \r
8344 \r
8345 typedef struct {\r
8346   char *title, *question, *replyPrefix;\r
8347   ProcRef pr;\r
8348 } QuestionParams;\r
8349 \r
8350 LRESULT CALLBACK\r
8351 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8352 {\r
8353   static QuestionParams *qp;\r
8354   char reply[MSG_SIZ];\r
8355   int len, err;\r
8356 \r
8357   switch (message) {\r
8358   case WM_INITDIALOG:\r
8359     qp = (QuestionParams *) lParam;\r
8360     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8361     Translate(hDlg, DLG_Question);\r
8362     SetWindowText(hDlg, qp->title);\r
8363     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8364     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8365     return FALSE;\r
8366 \r
8367   case WM_COMMAND:\r
8368     switch (LOWORD(wParam)) {\r
8369     case IDOK:\r
8370       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8371       if (*reply) strcat(reply, " ");\r
8372       len = strlen(reply);\r
8373       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8374       strcat(reply, "\n");\r
8375       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8376       EndDialog(hDlg, TRUE);\r
8377       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8378       return TRUE;\r
8379     case IDCANCEL:\r
8380       EndDialog(hDlg, FALSE);\r
8381       return TRUE;\r
8382     default:\r
8383       break;\r
8384     }\r
8385     break;\r
8386   }\r
8387   return FALSE;\r
8388 }\r
8389 \r
8390 VOID\r
8391 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8392 {\r
8393     QuestionParams qp;\r
8394     FARPROC lpProc;\r
8395     \r
8396     qp.title = title;\r
8397     qp.question = question;\r
8398     qp.replyPrefix = replyPrefix;\r
8399     qp.pr = pr;\r
8400     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8401     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8402       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8403     FreeProcInstance(lpProc);\r
8404 }\r
8405 \r
8406 /* [AS] Pick FRC position */\r
8407 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8408 {\r
8409     static int * lpIndexFRC;\r
8410     BOOL index_is_ok;\r
8411     char buf[16];\r
8412 \r
8413     switch( message )\r
8414     {\r
8415     case WM_INITDIALOG:\r
8416         lpIndexFRC = (int *) lParam;\r
8417 \r
8418         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8419         Translate(hDlg, DLG_NewGameFRC);\r
8420 \r
8421         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8422         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8423         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8424         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8425 \r
8426         break;\r
8427 \r
8428     case WM_COMMAND:\r
8429         switch( LOWORD(wParam) ) {\r
8430         case IDOK:\r
8431             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8432             EndDialog( hDlg, 0 );\r
8433             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8434             return TRUE;\r
8435         case IDCANCEL:\r
8436             EndDialog( hDlg, 1 );   \r
8437             return TRUE;\r
8438         case IDC_NFG_Edit:\r
8439             if( HIWORD(wParam) == EN_CHANGE ) {\r
8440                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8441 \r
8442                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8443             }\r
8444             return TRUE;\r
8445         case IDC_NFG_Random:\r
8446           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8447             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8448             return TRUE;\r
8449         }\r
8450 \r
8451         break;\r
8452     }\r
8453 \r
8454     return FALSE;\r
8455 }\r
8456 \r
8457 int NewGameFRC()\r
8458 {\r
8459     int result;\r
8460     int index = appData.defaultFrcPosition;\r
8461     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8462 \r
8463     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8464 \r
8465     if( result == 0 ) {\r
8466         appData.defaultFrcPosition = index;\r
8467     }\r
8468 \r
8469     return result;\r
8470 }\r
8471 \r
8472 /* [AS] Game list options. Refactored by HGM */\r
8473 \r
8474 HWND gameListOptionsDialog;\r
8475 \r
8476 // low-level front-end: clear text edit / list widget\r
8477 void\r
8478 GLT_ClearList()\r
8479 {\r
8480     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8481 }\r
8482 \r
8483 // low-level front-end: clear text edit / list widget\r
8484 void\r
8485 GLT_DeSelectList()\r
8486 {\r
8487     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8488 }\r
8489 \r
8490 // low-level front-end: append line to text edit / list widget\r
8491 void\r
8492 GLT_AddToList( char *name )\r
8493 {\r
8494     if( name != 0 ) {\r
8495             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8496     }\r
8497 }\r
8498 \r
8499 // low-level front-end: get line from text edit / list widget\r
8500 Boolean\r
8501 GLT_GetFromList( int index, char *name )\r
8502 {\r
8503     if( name != 0 ) {\r
8504             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8505                 return TRUE;\r
8506     }\r
8507     return FALSE;\r
8508 }\r
8509 \r
8510 void GLT_MoveSelection( HWND hDlg, int delta )\r
8511 {\r
8512     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8513     int idx2 = idx1 + delta;\r
8514     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8515 \r
8516     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8517         char buf[128];\r
8518 \r
8519         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8520         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8521         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8522         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8523     }\r
8524 }\r
8525 \r
8526 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8527 {\r
8528     switch( message )\r
8529     {\r
8530     case WM_INITDIALOG:\r
8531         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8532         \r
8533         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8534         Translate(hDlg, DLG_GameListOptions);\r
8535 \r
8536         /* Initialize list */\r
8537         GLT_TagsToList( lpUserGLT );\r
8538 \r
8539         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8540 \r
8541         break;\r
8542 \r
8543     case WM_COMMAND:\r
8544         switch( LOWORD(wParam) ) {\r
8545         case IDOK:\r
8546             GLT_ParseList();\r
8547             EndDialog( hDlg, 0 );\r
8548             return TRUE;\r
8549         case IDCANCEL:\r
8550             EndDialog( hDlg, 1 );\r
8551             return TRUE;\r
8552 \r
8553         case IDC_GLT_Default:\r
8554             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8555             return TRUE;\r
8556 \r
8557         case IDC_GLT_Restore:\r
8558             GLT_TagsToList( appData.gameListTags );\r
8559             return TRUE;\r
8560 \r
8561         case IDC_GLT_Up:\r
8562             GLT_MoveSelection( hDlg, -1 );\r
8563             return TRUE;\r
8564 \r
8565         case IDC_GLT_Down:\r
8566             GLT_MoveSelection( hDlg, +1 );\r
8567             return TRUE;\r
8568         }\r
8569 \r
8570         break;\r
8571     }\r
8572 \r
8573     return FALSE;\r
8574 }\r
8575 \r
8576 int GameListOptions()\r
8577 {\r
8578     int result;\r
8579     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8580 \r
8581       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8582 \r
8583     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8584 \r
8585     if( result == 0 ) {\r
8586         /* [AS] Memory leak here! */\r
8587         appData.gameListTags = strdup( lpUserGLT ); \r
8588     }\r
8589 \r
8590     return result;\r
8591 }\r
8592 \r
8593 VOID\r
8594 DisplayIcsInteractionTitle(char *str)\r
8595 {\r
8596   char consoleTitle[MSG_SIZ];\r
8597 \r
8598     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8599     SetWindowText(hwndConsole, consoleTitle);\r
8600 \r
8601     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8602       char buf[MSG_SIZ], *p = buf, *q;\r
8603         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8604       do {\r
8605         q = strchr(p, ';');\r
8606         if(q) *q++ = 0;\r
8607         if(*p) ChatPopUp(p);\r
8608       } while(p=q);\r
8609     }\r
8610 \r
8611     SetActiveWindow(hwndMain);\r
8612 }\r
8613 \r
8614 void\r
8615 DrawPosition(int fullRedraw, Board board)\r
8616 {\r
8617   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8618 }\r
8619 \r
8620 void NotifyFrontendLogin()\r
8621 {\r
8622         if (hwndConsole)\r
8623                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8624 }\r
8625 \r
8626 VOID\r
8627 ResetFrontEnd()\r
8628 {\r
8629   fromX = fromY = -1;\r
8630   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8631     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8632     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8633     dragInfo.lastpos = dragInfo.pos;\r
8634     dragInfo.start.x = dragInfo.start.y = -1;\r
8635     dragInfo.from = dragInfo.start;\r
8636     ReleaseCapture();\r
8637     DrawPosition(TRUE, NULL);\r
8638   }\r
8639   TagsPopDown();\r
8640 }\r
8641 \r
8642 \r
8643 VOID\r
8644 CommentPopUp(char *title, char *str)\r
8645 {\r
8646   HWND hwnd = GetActiveWindow();\r
8647   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8648   SAY(str);\r
8649   SetActiveWindow(hwnd);\r
8650 }\r
8651 \r
8652 VOID\r
8653 CommentPopDown(void)\r
8654 {\r
8655   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8656   if (commentDialog) {\r
8657     ShowWindow(commentDialog, SW_HIDE);\r
8658   }\r
8659   commentUp = FALSE;\r
8660 }\r
8661 \r
8662 VOID\r
8663 EditCommentPopUp(int index, char *title, char *str)\r
8664 {\r
8665   EitherCommentPopUp(index, title, str, TRUE);\r
8666 }\r
8667 \r
8668 \r
8669 VOID\r
8670 RingBell()\r
8671 {\r
8672   MyPlaySound(&sounds[(int)SoundMove]);\r
8673 }\r
8674 \r
8675 VOID PlayIcsWinSound()\r
8676 {\r
8677   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8678 }\r
8679 \r
8680 VOID PlayIcsLossSound()\r
8681 {\r
8682   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8683 }\r
8684 \r
8685 VOID PlayIcsDrawSound()\r
8686 {\r
8687   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8688 }\r
8689 \r
8690 VOID PlayIcsUnfinishedSound()\r
8691 {\r
8692   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8693 }\r
8694 \r
8695 VOID\r
8696 PlayAlarmSound()\r
8697 {\r
8698   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8699 }\r
8700 \r
8701 VOID\r
8702 PlayTellSound()\r
8703 {\r
8704   MyPlaySound(&textAttribs[ColorTell].sound);\r
8705 }\r
8706 \r
8707 \r
8708 VOID\r
8709 EchoOn()\r
8710 {\r
8711   HWND hInput;\r
8712   consoleEcho = TRUE;\r
8713   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8714   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8715   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8716 }\r
8717 \r
8718 \r
8719 VOID\r
8720 EchoOff()\r
8721 {\r
8722   CHARFORMAT cf;\r
8723   HWND hInput;\r
8724   consoleEcho = FALSE;\r
8725   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8726   /* This works OK: set text and background both to the same color */\r
8727   cf = consoleCF;\r
8728   cf.crTextColor = COLOR_ECHOOFF;\r
8729   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8730   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8731 }\r
8732 \r
8733 /* No Raw()...? */\r
8734 \r
8735 void Colorize(ColorClass cc, int continuation)\r
8736 {\r
8737   currentColorClass = cc;\r
8738   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8739   consoleCF.crTextColor = textAttribs[cc].color;\r
8740   consoleCF.dwEffects = textAttribs[cc].effects;\r
8741   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8742 }\r
8743 \r
8744 char *\r
8745 UserName()\r
8746 {\r
8747   static char buf[MSG_SIZ];\r
8748   DWORD bufsiz = MSG_SIZ;\r
8749 \r
8750   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8751         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8752   }\r
8753   if (!GetUserName(buf, &bufsiz)) {\r
8754     /*DisplayError("Error getting user name", GetLastError());*/\r
8755     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8756   }\r
8757   return buf;\r
8758 }\r
8759 \r
8760 char *\r
8761 HostName()\r
8762 {\r
8763   static char buf[MSG_SIZ];\r
8764   DWORD bufsiz = MSG_SIZ;\r
8765 \r
8766   if (!GetComputerName(buf, &bufsiz)) {\r
8767     /*DisplayError("Error getting host name", GetLastError());*/\r
8768     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8769   }\r
8770   return buf;\r
8771 }\r
8772 \r
8773 \r
8774 int\r
8775 ClockTimerRunning()\r
8776 {\r
8777   return clockTimerEvent != 0;\r
8778 }\r
8779 \r
8780 int\r
8781 StopClockTimer()\r
8782 {\r
8783   if (clockTimerEvent == 0) return FALSE;\r
8784   KillTimer(hwndMain, clockTimerEvent);\r
8785   clockTimerEvent = 0;\r
8786   return TRUE;\r
8787 }\r
8788 \r
8789 void\r
8790 StartClockTimer(long millisec)\r
8791 {\r
8792   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8793                              (UINT) millisec, NULL);\r
8794 }\r
8795 \r
8796 void\r
8797 DisplayWhiteClock(long timeRemaining, int highlight)\r
8798 {\r
8799   HDC hdc;\r
8800   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8801 \r
8802   if(appData.noGUI) return;\r
8803   hdc = GetDC(hwndMain);\r
8804   if (!IsIconic(hwndMain)) {\r
8805     DisplayAClock(hdc, timeRemaining, highlight, \r
8806                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8807   }\r
8808   if (highlight && iconCurrent == iconBlack) {\r
8809     iconCurrent = iconWhite;\r
8810     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8811     if (IsIconic(hwndMain)) {\r
8812       DrawIcon(hdc, 2, 2, iconCurrent);\r
8813     }\r
8814   }\r
8815   (void) ReleaseDC(hwndMain, hdc);\r
8816   if (hwndConsole)\r
8817     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8818 }\r
8819 \r
8820 void\r
8821 DisplayBlackClock(long timeRemaining, int highlight)\r
8822 {\r
8823   HDC hdc;\r
8824   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8825 \r
8826   if(appData.noGUI) return;\r
8827   hdc = GetDC(hwndMain);\r
8828   if (!IsIconic(hwndMain)) {\r
8829     DisplayAClock(hdc, timeRemaining, highlight, \r
8830                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8831   }\r
8832   if (highlight && iconCurrent == iconWhite) {\r
8833     iconCurrent = iconBlack;\r
8834     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8835     if (IsIconic(hwndMain)) {\r
8836       DrawIcon(hdc, 2, 2, iconCurrent);\r
8837     }\r
8838   }\r
8839   (void) ReleaseDC(hwndMain, hdc);\r
8840   if (hwndConsole)\r
8841     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8842 }\r
8843 \r
8844 \r
8845 int\r
8846 LoadGameTimerRunning()\r
8847 {\r
8848   return loadGameTimerEvent != 0;\r
8849 }\r
8850 \r
8851 int\r
8852 StopLoadGameTimer()\r
8853 {\r
8854   if (loadGameTimerEvent == 0) return FALSE;\r
8855   KillTimer(hwndMain, loadGameTimerEvent);\r
8856   loadGameTimerEvent = 0;\r
8857   return TRUE;\r
8858 }\r
8859 \r
8860 void\r
8861 StartLoadGameTimer(long millisec)\r
8862 {\r
8863   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8864                                 (UINT) millisec, NULL);\r
8865 }\r
8866 \r
8867 void\r
8868 AutoSaveGame()\r
8869 {\r
8870   char *defName;\r
8871   FILE *f;\r
8872   char fileTitle[MSG_SIZ];\r
8873 \r
8874   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8875   f = OpenFileDialog(hwndMain, "a", defName,\r
8876                      appData.oldSaveStyle ? "gam" : "pgn",\r
8877                      GAME_FILT, \r
8878                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8879   if (f != NULL) {\r
8880     SaveGame(f, 0, "");\r
8881     fclose(f);\r
8882   }\r
8883 }\r
8884 \r
8885 \r
8886 void\r
8887 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8888 {\r
8889   if (delayedTimerEvent != 0) {\r
8890     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8891       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8892     }\r
8893     KillTimer(hwndMain, delayedTimerEvent);\r
8894     delayedTimerEvent = 0;\r
8895     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8896     delayedTimerCallback();\r
8897   }\r
8898   delayedTimerCallback = cb;\r
8899   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8900                                 (UINT) millisec, NULL);\r
8901 }\r
8902 \r
8903 DelayedEventCallback\r
8904 GetDelayedEvent()\r
8905 {\r
8906   if (delayedTimerEvent) {\r
8907     return delayedTimerCallback;\r
8908   } else {\r
8909     return NULL;\r
8910   }\r
8911 }\r
8912 \r
8913 void\r
8914 CancelDelayedEvent()\r
8915 {\r
8916   if (delayedTimerEvent) {\r
8917     KillTimer(hwndMain, delayedTimerEvent);\r
8918     delayedTimerEvent = 0;\r
8919   }\r
8920 }\r
8921 \r
8922 DWORD GetWin32Priority(int nice)\r
8923 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8924 /*\r
8925 REALTIME_PRIORITY_CLASS     0x00000100\r
8926 HIGH_PRIORITY_CLASS         0x00000080\r
8927 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8928 NORMAL_PRIORITY_CLASS       0x00000020\r
8929 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8930 IDLE_PRIORITY_CLASS         0x00000040\r
8931 */\r
8932         if (nice < -15) return 0x00000080;\r
8933         if (nice < 0)   return 0x00008000;\r
8934         if (nice == 0)  return 0x00000020;\r
8935         if (nice < 15)  return 0x00004000;\r
8936         return 0x00000040;\r
8937 }\r
8938 \r
8939 void RunCommand(char *cmdLine)\r
8940 {\r
8941   /* Now create the child process. */\r
8942   STARTUPINFO siStartInfo;\r
8943   PROCESS_INFORMATION piProcInfo;\r
8944 \r
8945   siStartInfo.cb = sizeof(STARTUPINFO);\r
8946   siStartInfo.lpReserved = NULL;\r
8947   siStartInfo.lpDesktop = NULL;\r
8948   siStartInfo.lpTitle = NULL;\r
8949   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8950   siStartInfo.cbReserved2 = 0;\r
8951   siStartInfo.lpReserved2 = NULL;\r
8952   siStartInfo.hStdInput = NULL;\r
8953   siStartInfo.hStdOutput = NULL;\r
8954   siStartInfo.hStdError = NULL;\r
8955 \r
8956   CreateProcess(NULL,\r
8957                 cmdLine,           /* command line */\r
8958                 NULL,      /* process security attributes */\r
8959                 NULL,      /* primary thread security attrs */\r
8960                 TRUE,      /* handles are inherited */\r
8961                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8962                 NULL,      /* use parent's environment */\r
8963                 NULL,\r
8964                 &siStartInfo, /* STARTUPINFO pointer */\r
8965                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8966 \r
8967   CloseHandle(piProcInfo.hThread);\r
8968 }\r
8969 \r
8970 /* Start a child process running the given program.\r
8971    The process's standard output can be read from "from", and its\r
8972    standard input can be written to "to".\r
8973    Exit with fatal error if anything goes wrong.\r
8974    Returns an opaque pointer that can be used to destroy the process\r
8975    later.\r
8976 */\r
8977 int\r
8978 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8979 {\r
8980 #define BUFSIZE 4096\r
8981 \r
8982   HANDLE hChildStdinRd, hChildStdinWr,\r
8983     hChildStdoutRd, hChildStdoutWr;\r
8984   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8985   SECURITY_ATTRIBUTES saAttr;\r
8986   BOOL fSuccess;\r
8987   PROCESS_INFORMATION piProcInfo;\r
8988   STARTUPINFO siStartInfo;\r
8989   ChildProc *cp;\r
8990   char buf[MSG_SIZ];\r
8991   DWORD err;\r
8992 \r
8993   if (appData.debugMode) {\r
8994     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8995   }\r
8996 \r
8997   *pr = NoProc;\r
8998 \r
8999   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9000   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9001   saAttr.bInheritHandle = TRUE;\r
9002   saAttr.lpSecurityDescriptor = NULL;\r
9003 \r
9004   /*\r
9005    * The steps for redirecting child's STDOUT:\r
9006    *     1. Create anonymous pipe to be STDOUT for child.\r
9007    *     2. Create a noninheritable duplicate of read handle,\r
9008    *         and close the inheritable read handle.\r
9009    */\r
9010 \r
9011   /* Create a pipe for the child's STDOUT. */\r
9012   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9013     return GetLastError();\r
9014   }\r
9015 \r
9016   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9017   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9018                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9019                              FALSE,     /* not inherited */\r
9020                              DUPLICATE_SAME_ACCESS);\r
9021   if (! fSuccess) {\r
9022     return GetLastError();\r
9023   }\r
9024   CloseHandle(hChildStdoutRd);\r
9025 \r
9026   /*\r
9027    * The steps for redirecting child's STDIN:\r
9028    *     1. Create anonymous pipe to be STDIN for child.\r
9029    *     2. Create a noninheritable duplicate of write handle,\r
9030    *         and close the inheritable write handle.\r
9031    */\r
9032 \r
9033   /* Create a pipe for the child's STDIN. */\r
9034   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9035     return GetLastError();\r
9036   }\r
9037 \r
9038   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9039   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9040                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9041                              FALSE,     /* not inherited */\r
9042                              DUPLICATE_SAME_ACCESS);\r
9043   if (! fSuccess) {\r
9044     return GetLastError();\r
9045   }\r
9046   CloseHandle(hChildStdinWr);\r
9047 \r
9048   /* Arrange to (1) look in dir for the child .exe file, and\r
9049    * (2) have dir be the child's working directory.  Interpret\r
9050    * dir relative to the directory WinBoard loaded from. */\r
9051   GetCurrentDirectory(MSG_SIZ, buf);\r
9052   SetCurrentDirectory(installDir);\r
9053   SetCurrentDirectory(dir);\r
9054 \r
9055   /* Now create the child process. */\r
9056 \r
9057   siStartInfo.cb = sizeof(STARTUPINFO);\r
9058   siStartInfo.lpReserved = NULL;\r
9059   siStartInfo.lpDesktop = NULL;\r
9060   siStartInfo.lpTitle = NULL;\r
9061   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9062   siStartInfo.cbReserved2 = 0;\r
9063   siStartInfo.lpReserved2 = NULL;\r
9064   siStartInfo.hStdInput = hChildStdinRd;\r
9065   siStartInfo.hStdOutput = hChildStdoutWr;\r
9066   siStartInfo.hStdError = hChildStdoutWr;\r
9067 \r
9068   fSuccess = CreateProcess(NULL,\r
9069                            cmdLine,        /* command line */\r
9070                            NULL,           /* process security attributes */\r
9071                            NULL,           /* primary thread security attrs */\r
9072                            TRUE,           /* handles are inherited */\r
9073                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9074                            NULL,           /* use parent's environment */\r
9075                            NULL,\r
9076                            &siStartInfo, /* STARTUPINFO pointer */\r
9077                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9078 \r
9079   err = GetLastError();\r
9080   SetCurrentDirectory(buf); /* return to prev directory */\r
9081   if (! fSuccess) {\r
9082     return err;\r
9083   }\r
9084 \r
9085   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9086     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9087     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9088   }\r
9089 \r
9090   /* Close the handles we don't need in the parent */\r
9091   CloseHandle(piProcInfo.hThread);\r
9092   CloseHandle(hChildStdinRd);\r
9093   CloseHandle(hChildStdoutWr);\r
9094 \r
9095   /* Prepare return value */\r
9096   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9097   cp->kind = CPReal;\r
9098   cp->hProcess = piProcInfo.hProcess;\r
9099   cp->pid = piProcInfo.dwProcessId;\r
9100   cp->hFrom = hChildStdoutRdDup;\r
9101   cp->hTo = hChildStdinWrDup;\r
9102 \r
9103   *pr = (void *) cp;\r
9104 \r
9105   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9106      2000 where engines sometimes don't see the initial command(s)\r
9107      from WinBoard and hang.  I don't understand how that can happen,\r
9108      but the Sleep is harmless, so I've put it in.  Others have also\r
9109      reported what may be the same problem, so hopefully this will fix\r
9110      it for them too.  */\r
9111   Sleep(500);\r
9112 \r
9113   return NO_ERROR;\r
9114 }\r
9115 \r
9116 \r
9117 void\r
9118 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9119 {\r
9120   ChildProc *cp; int result;\r
9121 \r
9122   cp = (ChildProc *) pr;\r
9123   if (cp == NULL) return;\r
9124 \r
9125   switch (cp->kind) {\r
9126   case CPReal:\r
9127     /* TerminateProcess is considered harmful, so... */\r
9128     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9129     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9130     /* The following doesn't work because the chess program\r
9131        doesn't "have the same console" as WinBoard.  Maybe\r
9132        we could arrange for this even though neither WinBoard\r
9133        nor the chess program uses a console for stdio? */\r
9134     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9135 \r
9136     /* [AS] Special termination modes for misbehaving programs... */\r
9137     if( signal == 9 ) { \r
9138         result = TerminateProcess( cp->hProcess, 0 );\r
9139 \r
9140         if ( appData.debugMode) {\r
9141             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9142         }\r
9143     }\r
9144     else if( signal == 10 ) {\r
9145         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9146 \r
9147         if( dw != WAIT_OBJECT_0 ) {\r
9148             result = TerminateProcess( cp->hProcess, 0 );\r
9149 \r
9150             if ( appData.debugMode) {\r
9151                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9152             }\r
9153 \r
9154         }\r
9155     }\r
9156 \r
9157     CloseHandle(cp->hProcess);\r
9158     break;\r
9159 \r
9160   case CPComm:\r
9161     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9162     break;\r
9163 \r
9164   case CPSock:\r
9165     closesocket(cp->sock);\r
9166     WSACleanup();\r
9167     break;\r
9168 \r
9169   case CPRcmd:\r
9170     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9171     closesocket(cp->sock);\r
9172     closesocket(cp->sock2);\r
9173     WSACleanup();\r
9174     break;\r
9175   }\r
9176   free(cp);\r
9177 }\r
9178 \r
9179 void\r
9180 InterruptChildProcess(ProcRef pr)\r
9181 {\r
9182   ChildProc *cp;\r
9183 \r
9184   cp = (ChildProc *) pr;\r
9185   if (cp == NULL) return;\r
9186   switch (cp->kind) {\r
9187   case CPReal:\r
9188     /* The following doesn't work because the chess program\r
9189        doesn't "have the same console" as WinBoard.  Maybe\r
9190        we could arrange for this even though neither WinBoard\r
9191        nor the chess program uses a console for stdio */\r
9192     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9193     break;\r
9194 \r
9195   case CPComm:\r
9196   case CPSock:\r
9197     /* Can't interrupt */\r
9198     break;\r
9199 \r
9200   case CPRcmd:\r
9201     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9202     break;\r
9203   }\r
9204 }\r
9205 \r
9206 \r
9207 int\r
9208 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9209 {\r
9210   char cmdLine[MSG_SIZ];\r
9211 \r
9212   if (port[0] == NULLCHAR) {\r
9213     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9214   } else {\r
9215     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9216   }\r
9217   return StartChildProcess(cmdLine, "", pr);\r
9218 }\r
9219 \r
9220 \r
9221 /* Code to open TCP sockets */\r
9222 \r
9223 int\r
9224 OpenTCP(char *host, char *port, ProcRef *pr)\r
9225 {\r
9226   ChildProc *cp;\r
9227   int err;\r
9228   SOCKET s;\r
9229 \r
9230   struct sockaddr_in sa, mysa;\r
9231   struct hostent FAR *hp;\r
9232   unsigned short uport;\r
9233   WORD wVersionRequested;\r
9234   WSADATA wsaData;\r
9235 \r
9236   /* Initialize socket DLL */\r
9237   wVersionRequested = MAKEWORD(1, 1);\r
9238   err = WSAStartup(wVersionRequested, &wsaData);\r
9239   if (err != 0) return err;\r
9240 \r
9241   /* Make socket */\r
9242   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9243     err = WSAGetLastError();\r
9244     WSACleanup();\r
9245     return err;\r
9246   }\r
9247 \r
9248   /* Bind local address using (mostly) don't-care values.\r
9249    */\r
9250   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9251   mysa.sin_family = AF_INET;\r
9252   mysa.sin_addr.s_addr = INADDR_ANY;\r
9253   uport = (unsigned short) 0;\r
9254   mysa.sin_port = htons(uport);\r
9255   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9256       == SOCKET_ERROR) {\r
9257     err = WSAGetLastError();\r
9258     WSACleanup();\r
9259     return err;\r
9260   }\r
9261 \r
9262   /* Resolve remote host name */\r
9263   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9264   if (!(hp = gethostbyname(host))) {\r
9265     unsigned int b0, b1, b2, b3;\r
9266 \r
9267     err = WSAGetLastError();\r
9268 \r
9269     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9270       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9271       hp->h_addrtype = AF_INET;\r
9272       hp->h_length = 4;\r
9273       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9274       hp->h_addr_list[0] = (char *) malloc(4);\r
9275       hp->h_addr_list[0][0] = (char) b0;\r
9276       hp->h_addr_list[0][1] = (char) b1;\r
9277       hp->h_addr_list[0][2] = (char) b2;\r
9278       hp->h_addr_list[0][3] = (char) b3;\r
9279     } else {\r
9280       WSACleanup();\r
9281       return err;\r
9282     }\r
9283   }\r
9284   sa.sin_family = hp->h_addrtype;\r
9285   uport = (unsigned short) atoi(port);\r
9286   sa.sin_port = htons(uport);\r
9287   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9288 \r
9289   /* Make connection */\r
9290   if (connect(s, (struct sockaddr *) &sa,\r
9291               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9292     err = WSAGetLastError();\r
9293     WSACleanup();\r
9294     return err;\r
9295   }\r
9296 \r
9297   /* Prepare return value */\r
9298   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9299   cp->kind = CPSock;\r
9300   cp->sock = s;\r
9301   *pr = (ProcRef *) cp;\r
9302 \r
9303   return NO_ERROR;\r
9304 }\r
9305 \r
9306 int\r
9307 OpenCommPort(char *name, ProcRef *pr)\r
9308 {\r
9309   HANDLE h;\r
9310   COMMTIMEOUTS ct;\r
9311   ChildProc *cp;\r
9312   char fullname[MSG_SIZ];\r
9313 \r
9314   if (*name != '\\')\r
9315     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9316   else\r
9317     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9318 \r
9319   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9320                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9321   if (h == (HANDLE) -1) {\r
9322     return GetLastError();\r
9323   }\r
9324   hCommPort = h;\r
9325 \r
9326   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9327 \r
9328   /* Accumulate characters until a 100ms pause, then parse */\r
9329   ct.ReadIntervalTimeout = 100;\r
9330   ct.ReadTotalTimeoutMultiplier = 0;\r
9331   ct.ReadTotalTimeoutConstant = 0;\r
9332   ct.WriteTotalTimeoutMultiplier = 0;\r
9333   ct.WriteTotalTimeoutConstant = 0;\r
9334   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9335 \r
9336   /* Prepare return value */\r
9337   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9338   cp->kind = CPComm;\r
9339   cp->hFrom = h;\r
9340   cp->hTo = h;\r
9341   *pr = (ProcRef *) cp;\r
9342 \r
9343   return NO_ERROR;\r
9344 }\r
9345 \r
9346 int\r
9347 OpenLoopback(ProcRef *pr)\r
9348 {\r
9349   DisplayFatalError(_("Not implemented"), 0, 1);\r
9350   return NO_ERROR;\r
9351 }\r
9352 \r
9353 \r
9354 int\r
9355 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9356 {\r
9357   ChildProc *cp;\r
9358   int err;\r
9359   SOCKET s, s2, s3;\r
9360   struct sockaddr_in sa, mysa;\r
9361   struct hostent FAR *hp;\r
9362   unsigned short uport;\r
9363   WORD wVersionRequested;\r
9364   WSADATA wsaData;\r
9365   int fromPort;\r
9366   char stderrPortStr[MSG_SIZ];\r
9367 \r
9368   /* Initialize socket DLL */\r
9369   wVersionRequested = MAKEWORD(1, 1);\r
9370   err = WSAStartup(wVersionRequested, &wsaData);\r
9371   if (err != 0) return err;\r
9372 \r
9373   /* Resolve remote host name */\r
9374   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9375   if (!(hp = gethostbyname(host))) {\r
9376     unsigned int b0, b1, b2, b3;\r
9377 \r
9378     err = WSAGetLastError();\r
9379 \r
9380     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9381       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9382       hp->h_addrtype = AF_INET;\r
9383       hp->h_length = 4;\r
9384       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9385       hp->h_addr_list[0] = (char *) malloc(4);\r
9386       hp->h_addr_list[0][0] = (char) b0;\r
9387       hp->h_addr_list[0][1] = (char) b1;\r
9388       hp->h_addr_list[0][2] = (char) b2;\r
9389       hp->h_addr_list[0][3] = (char) b3;\r
9390     } else {\r
9391       WSACleanup();\r
9392       return err;\r
9393     }\r
9394   }\r
9395   sa.sin_family = hp->h_addrtype;\r
9396   uport = (unsigned short) 514;\r
9397   sa.sin_port = htons(uport);\r
9398   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9399 \r
9400   /* Bind local socket to unused "privileged" port address\r
9401    */\r
9402   s = INVALID_SOCKET;\r
9403   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9404   mysa.sin_family = AF_INET;\r
9405   mysa.sin_addr.s_addr = INADDR_ANY;\r
9406   for (fromPort = 1023;; fromPort--) {\r
9407     if (fromPort < 0) {\r
9408       WSACleanup();\r
9409       return WSAEADDRINUSE;\r
9410     }\r
9411     if (s == INVALID_SOCKET) {\r
9412       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9413         err = WSAGetLastError();\r
9414         WSACleanup();\r
9415         return err;\r
9416       }\r
9417     }\r
9418     uport = (unsigned short) fromPort;\r
9419     mysa.sin_port = htons(uport);\r
9420     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9421         == SOCKET_ERROR) {\r
9422       err = WSAGetLastError();\r
9423       if (err == WSAEADDRINUSE) continue;\r
9424       WSACleanup();\r
9425       return err;\r
9426     }\r
9427     if (connect(s, (struct sockaddr *) &sa,\r
9428       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9429       err = WSAGetLastError();\r
9430       if (err == WSAEADDRINUSE) {\r
9431         closesocket(s);\r
9432         s = -1;\r
9433         continue;\r
9434       }\r
9435       WSACleanup();\r
9436       return err;\r
9437     }\r
9438     break;\r
9439   }\r
9440 \r
9441   /* Bind stderr local socket to unused "privileged" port address\r
9442    */\r
9443   s2 = INVALID_SOCKET;\r
9444   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9445   mysa.sin_family = AF_INET;\r
9446   mysa.sin_addr.s_addr = INADDR_ANY;\r
9447   for (fromPort = 1023;; fromPort--) {\r
9448     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9449     if (fromPort < 0) {\r
9450       (void) closesocket(s);\r
9451       WSACleanup();\r
9452       return WSAEADDRINUSE;\r
9453     }\r
9454     if (s2 == INVALID_SOCKET) {\r
9455       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9456         err = WSAGetLastError();\r
9457         closesocket(s);\r
9458         WSACleanup();\r
9459         return err;\r
9460       }\r
9461     }\r
9462     uport = (unsigned short) fromPort;\r
9463     mysa.sin_port = htons(uport);\r
9464     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9465         == SOCKET_ERROR) {\r
9466       err = WSAGetLastError();\r
9467       if (err == WSAEADDRINUSE) continue;\r
9468       (void) closesocket(s);\r
9469       WSACleanup();\r
9470       return err;\r
9471     }\r
9472     if (listen(s2, 1) == SOCKET_ERROR) {\r
9473       err = WSAGetLastError();\r
9474       if (err == WSAEADDRINUSE) {\r
9475         closesocket(s2);\r
9476         s2 = INVALID_SOCKET;\r
9477         continue;\r
9478       }\r
9479       (void) closesocket(s);\r
9480       (void) closesocket(s2);\r
9481       WSACleanup();\r
9482       return err;\r
9483     }\r
9484     break;\r
9485   }\r
9486   prevStderrPort = fromPort; // remember port used\r
9487   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9488 \r
9489   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9490     err = WSAGetLastError();\r
9491     (void) closesocket(s);\r
9492     (void) closesocket(s2);\r
9493     WSACleanup();\r
9494     return err;\r
9495   }\r
9496 \r
9497   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9498     err = WSAGetLastError();\r
9499     (void) closesocket(s);\r
9500     (void) closesocket(s2);\r
9501     WSACleanup();\r
9502     return err;\r
9503   }\r
9504   if (*user == NULLCHAR) user = UserName();\r
9505   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9506     err = WSAGetLastError();\r
9507     (void) closesocket(s);\r
9508     (void) closesocket(s2);\r
9509     WSACleanup();\r
9510     return err;\r
9511   }\r
9512   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9513     err = WSAGetLastError();\r
9514     (void) closesocket(s);\r
9515     (void) closesocket(s2);\r
9516     WSACleanup();\r
9517     return err;\r
9518   }\r
9519 \r
9520   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9521     err = WSAGetLastError();\r
9522     (void) closesocket(s);\r
9523     (void) closesocket(s2);\r
9524     WSACleanup();\r
9525     return err;\r
9526   }\r
9527   (void) closesocket(s2);  /* Stop listening */\r
9528 \r
9529   /* Prepare return value */\r
9530   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9531   cp->kind = CPRcmd;\r
9532   cp->sock = s;\r
9533   cp->sock2 = s3;\r
9534   *pr = (ProcRef *) cp;\r
9535 \r
9536   return NO_ERROR;\r
9537 }\r
9538 \r
9539 \r
9540 InputSourceRef\r
9541 AddInputSource(ProcRef pr, int lineByLine,\r
9542                InputCallback func, VOIDSTAR closure)\r
9543 {\r
9544   InputSource *is, *is2 = NULL;\r
9545   ChildProc *cp = (ChildProc *) pr;\r
9546 \r
9547   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9548   is->lineByLine = lineByLine;\r
9549   is->func = func;\r
9550   is->closure = closure;\r
9551   is->second = NULL;\r
9552   is->next = is->buf;\r
9553   if (pr == NoProc) {\r
9554     is->kind = CPReal;\r
9555     consoleInputSource = is;\r
9556   } else {\r
9557     is->kind = cp->kind;\r
9558     /* \r
9559         [AS] Try to avoid a race condition if the thread is given control too early:\r
9560         we create all threads suspended so that the is->hThread variable can be\r
9561         safely assigned, then let the threads start with ResumeThread.\r
9562     */\r
9563     switch (cp->kind) {\r
9564     case CPReal:\r
9565       is->hFile = cp->hFrom;\r
9566       cp->hFrom = NULL; /* now owned by InputThread */\r
9567       is->hThread =\r
9568         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9569                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9570       break;\r
9571 \r
9572     case CPComm:\r
9573       is->hFile = cp->hFrom;\r
9574       cp->hFrom = NULL; /* now owned by InputThread */\r
9575       is->hThread =\r
9576         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9577                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9578       break;\r
9579 \r
9580     case CPSock:\r
9581       is->sock = cp->sock;\r
9582       is->hThread =\r
9583         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9584                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9585       break;\r
9586 \r
9587     case CPRcmd:\r
9588       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9589       *is2 = *is;\r
9590       is->sock = cp->sock;\r
9591       is->second = is2;\r
9592       is2->sock = cp->sock2;\r
9593       is2->second = is2;\r
9594       is->hThread =\r
9595         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9596                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9597       is2->hThread =\r
9598         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9599                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9600       break;\r
9601     }\r
9602 \r
9603     if( is->hThread != NULL ) {\r
9604         ResumeThread( is->hThread );\r
9605     }\r
9606 \r
9607     if( is2 != NULL && is2->hThread != NULL ) {\r
9608         ResumeThread( is2->hThread );\r
9609     }\r
9610   }\r
9611 \r
9612   return (InputSourceRef) is;\r
9613 }\r
9614 \r
9615 void\r
9616 RemoveInputSource(InputSourceRef isr)\r
9617 {\r
9618   InputSource *is;\r
9619 \r
9620   is = (InputSource *) isr;\r
9621   is->hThread = NULL;  /* tell thread to stop */\r
9622   CloseHandle(is->hThread);\r
9623   if (is->second != NULL) {\r
9624     is->second->hThread = NULL;\r
9625     CloseHandle(is->second->hThread);\r
9626   }\r
9627 }\r
9628 \r
9629 int no_wrap(char *message, int count)\r
9630 {\r
9631     ConsoleOutput(message, count, FALSE);\r
9632     return count;\r
9633 }\r
9634 \r
9635 int\r
9636 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9637 {\r
9638   DWORD dOutCount;\r
9639   int outCount = SOCKET_ERROR;\r
9640   ChildProc *cp = (ChildProc *) pr;\r
9641   static OVERLAPPED ovl;\r
9642   static int line = 0;\r
9643 \r
9644   if (pr == NoProc)\r
9645   {\r
9646     if (appData.noJoin || !appData.useInternalWrap)\r
9647       return no_wrap(message, count);\r
9648     else\r
9649     {\r
9650       int width = get_term_width();\r
9651       int len = wrap(NULL, message, count, width, &line);\r
9652       char *msg = malloc(len);\r
9653       int dbgchk;\r
9654 \r
9655       if (!msg)\r
9656         return no_wrap(message, count);\r
9657       else\r
9658       {\r
9659         dbgchk = wrap(msg, message, count, width, &line);\r
9660         if (dbgchk != len && appData.debugMode)\r
9661             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9662         ConsoleOutput(msg, len, FALSE);\r
9663         free(msg);\r
9664         return len;\r
9665       }\r
9666     }\r
9667   }\r
9668 \r
9669   if (ovl.hEvent == NULL) {\r
9670     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9671   }\r
9672   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9673 \r
9674   switch (cp->kind) {\r
9675   case CPSock:\r
9676   case CPRcmd:\r
9677     outCount = send(cp->sock, message, count, 0);\r
9678     if (outCount == SOCKET_ERROR) {\r
9679       *outError = WSAGetLastError();\r
9680     } else {\r
9681       *outError = NO_ERROR;\r
9682     }\r
9683     break;\r
9684 \r
9685   case CPReal:\r
9686     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9687                   &dOutCount, NULL)) {\r
9688       *outError = NO_ERROR;\r
9689       outCount = (int) dOutCount;\r
9690     } else {\r
9691       *outError = GetLastError();\r
9692     }\r
9693     break;\r
9694 \r
9695   case CPComm:\r
9696     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9697                             &dOutCount, &ovl);\r
9698     if (*outError == NO_ERROR) {\r
9699       outCount = (int) dOutCount;\r
9700     }\r
9701     break;\r
9702   }\r
9703   return outCount;\r
9704 }\r
9705 \r
9706 void\r
9707 DoSleep(int n)\r
9708 {\r
9709     if(n != 0) Sleep(n);\r
9710 }\r
9711 \r
9712 int\r
9713 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9714                        long msdelay)\r
9715 {\r
9716   /* Ignore delay, not implemented for WinBoard */\r
9717   return OutputToProcess(pr, message, count, outError);\r
9718 }\r
9719 \r
9720 \r
9721 void\r
9722 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9723                         char *buf, int count, int error)\r
9724 {\r
9725   DisplayFatalError(_("Not implemented"), 0, 1);\r
9726 }\r
9727 \r
9728 /* see wgamelist.c for Game List functions */\r
9729 /* see wedittags.c for Edit Tags functions */\r
9730 \r
9731 \r
9732 VOID\r
9733 ICSInitScript()\r
9734 {\r
9735   FILE *f;\r
9736   char buf[MSG_SIZ];\r
9737   char *dummy;\r
9738 \r
9739   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9740     f = fopen(buf, "r");\r
9741     if (f != NULL) {\r
9742       ProcessICSInitScript(f);\r
9743       fclose(f);\r
9744     }\r
9745   }\r
9746 }\r
9747 \r
9748 \r
9749 VOID\r
9750 StartAnalysisClock()\r
9751 {\r
9752   if (analysisTimerEvent) return;\r
9753   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9754                                         (UINT) 2000, NULL);\r
9755 }\r
9756 \r
9757 VOID\r
9758 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9759 {\r
9760   highlightInfo.sq[0].x = fromX;\r
9761   highlightInfo.sq[0].y = fromY;\r
9762   highlightInfo.sq[1].x = toX;\r
9763   highlightInfo.sq[1].y = toY;\r
9764 }\r
9765 \r
9766 VOID\r
9767 ClearHighlights()\r
9768 {\r
9769   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9770     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9771 }\r
9772 \r
9773 VOID\r
9774 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9775 {\r
9776   premoveHighlightInfo.sq[0].x = fromX;\r
9777   premoveHighlightInfo.sq[0].y = fromY;\r
9778   premoveHighlightInfo.sq[1].x = toX;\r
9779   premoveHighlightInfo.sq[1].y = toY;\r
9780 }\r
9781 \r
9782 VOID\r
9783 ClearPremoveHighlights()\r
9784 {\r
9785   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9786     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9787 }\r
9788 \r
9789 VOID\r
9790 ShutDownFrontEnd()\r
9791 {\r
9792   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9793   DeleteClipboardTempFiles();\r
9794 }\r
9795 \r
9796 void\r
9797 BoardToTop()\r
9798 {\r
9799     if (IsIconic(hwndMain))\r
9800       ShowWindow(hwndMain, SW_RESTORE);\r
9801 \r
9802     SetActiveWindow(hwndMain);\r
9803 }\r
9804 \r
9805 /*\r
9806  * Prototypes for animation support routines\r
9807  */\r
9808 static void ScreenSquare(int column, int row, POINT * pt);\r
9809 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9810      POINT frames[], int * nFrames);\r
9811 \r
9812 \r
9813 #define kFactor 4\r
9814 \r
9815 void\r
9816 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9817 {       // [HGM] atomic: animate blast wave\r
9818         int i;\r
9819 \r
9820         explodeInfo.fromX = fromX;\r
9821         explodeInfo.fromY = fromY;\r
9822         explodeInfo.toX = toX;\r
9823         explodeInfo.toY = toY;\r
9824         for(i=1; i<4*kFactor; i++) {\r
9825             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9826             DrawPosition(FALSE, board);\r
9827             Sleep(appData.animSpeed);\r
9828         }\r
9829         explodeInfo.radius = 0;\r
9830         DrawPosition(TRUE, board);\r
9831 }\r
9832 \r
9833 void\r
9834 AnimateMove(board, fromX, fromY, toX, toY)\r
9835      Board board;\r
9836      int fromX;\r
9837      int fromY;\r
9838      int toX;\r
9839      int toY;\r
9840 {\r
9841   ChessSquare piece;\r
9842   POINT start, finish, mid;\r
9843   POINT frames[kFactor * 2 + 1];\r
9844   int nFrames, n;\r
9845 \r
9846   if (!appData.animate) return;\r
9847   if (doingSizing) return;\r
9848   if (fromY < 0 || fromX < 0) return;\r
9849   piece = board[fromY][fromX];\r
9850   if (piece >= EmptySquare) return;\r
9851 \r
9852   ScreenSquare(fromX, fromY, &start);\r
9853   ScreenSquare(toX, toY, &finish);\r
9854 \r
9855   /* All moves except knight jumps move in straight line */\r
9856   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9857     mid.x = start.x + (finish.x - start.x) / 2;\r
9858     mid.y = start.y + (finish.y - start.y) / 2;\r
9859   } else {\r
9860     /* Knight: make straight movement then diagonal */\r
9861     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9862        mid.x = start.x + (finish.x - start.x) / 2;\r
9863        mid.y = start.y;\r
9864      } else {\r
9865        mid.x = start.x;\r
9866        mid.y = start.y + (finish.y - start.y) / 2;\r
9867      }\r
9868   }\r
9869   \r
9870   /* Don't use as many frames for very short moves */\r
9871   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9872     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9873   else\r
9874     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9875 \r
9876   animInfo.from.x = fromX;\r
9877   animInfo.from.y = fromY;\r
9878   animInfo.to.x = toX;\r
9879   animInfo.to.y = toY;\r
9880   animInfo.lastpos = start;\r
9881   animInfo.piece = piece;\r
9882   for (n = 0; n < nFrames; n++) {\r
9883     animInfo.pos = frames[n];\r
9884     DrawPosition(FALSE, NULL);\r
9885     animInfo.lastpos = animInfo.pos;\r
9886     Sleep(appData.animSpeed);\r
9887   }\r
9888   animInfo.pos = finish;\r
9889   DrawPosition(FALSE, NULL);\r
9890   animInfo.piece = EmptySquare;\r
9891   Explode(board, fromX, fromY, toX, toY);\r
9892 }\r
9893 \r
9894 /*      Convert board position to corner of screen rect and color       */\r
9895 \r
9896 static void\r
9897 ScreenSquare(column, row, pt)\r
9898      int column; int row; POINT * pt;\r
9899 {\r
9900   if (flipView) {\r
9901     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9902     pt->y = lineGap + row * (squareSize + lineGap);\r
9903   } else {\r
9904     pt->x = lineGap + column * (squareSize + lineGap);\r
9905     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9906   }\r
9907 }\r
9908 \r
9909 /*      Generate a series of frame coords from start->mid->finish.\r
9910         The movement rate doubles until the half way point is\r
9911         reached, then halves back down to the final destination,\r
9912         which gives a nice slow in/out effect. The algorithmn\r
9913         may seem to generate too many intermediates for short\r
9914         moves, but remember that the purpose is to attract the\r
9915         viewers attention to the piece about to be moved and\r
9916         then to where it ends up. Too few frames would be less\r
9917         noticeable.                                             */\r
9918 \r
9919 static void\r
9920 Tween(start, mid, finish, factor, frames, nFrames)\r
9921      POINT * start; POINT * mid;\r
9922      POINT * finish; int factor;\r
9923      POINT frames[]; int * nFrames;\r
9924 {\r
9925   int n, fraction = 1, count = 0;\r
9926 \r
9927   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9928   for (n = 0; n < factor; n++)\r
9929     fraction *= 2;\r
9930   for (n = 0; n < factor; n++) {\r
9931     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9932     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9933     count ++;\r
9934     fraction = fraction / 2;\r
9935   }\r
9936   \r
9937   /* Midpoint */\r
9938   frames[count] = *mid;\r
9939   count ++;\r
9940   \r
9941   /* Slow out, stepping 1/2, then 1/4, ... */\r
9942   fraction = 2;\r
9943   for (n = 0; n < factor; n++) {\r
9944     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9945     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9946     count ++;\r
9947     fraction = fraction * 2;\r
9948   }\r
9949   *nFrames = count;\r
9950 }\r
9951 \r
9952 void\r
9953 SettingsPopUp(ChessProgramState *cps)\r
9954 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9955       EngineOptionsPopup(savedHwnd, cps);\r
9956 }\r
9957 \r
9958 int flock(int fid, int code)\r
9959 {\r
9960     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9961     OVERLAPPED ov;\r
9962     ov.hEvent = NULL;\r
9963     ov.Offset = 0;\r
9964     ov.OffsetHigh = 0;\r
9965     switch(code) {\r
9966       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9967       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9968       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9969       default: return -1;\r
9970     }\r
9971     return 0;\r
9972 }\r