Fix re-rendering of svg on resize
[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 DrawSeekOpen()\r
3559 {\r
3560 }\r
3561 \r
3562 void DrawSeekClose()\r
3563 {\r
3564 }\r
3565 \r
3566 VOID\r
3567 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3568 {\r
3569   static Board lastReq[2], lastDrawn[2];\r
3570   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3571   static int lastDrawnFlipView = 0;\r
3572   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3573   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3574   HDC tmphdc;\r
3575   HDC hdcmem;\r
3576   HBITMAP bufferBitmap;\r
3577   HBITMAP oldBitmap;\r
3578   RECT Rect;\r
3579   HRGN clips[MAX_CLIPS];\r
3580   ChessSquare dragged_piece = EmptySquare;\r
3581   int nr = twoBoards*partnerUp;\r
3582 \r
3583   /* I'm undecided on this - this function figures out whether a full\r
3584    * repaint is necessary on its own, so there's no real reason to have the\r
3585    * caller tell it that.  I think this can safely be set to FALSE - but\r
3586    * if we trust the callers not to request full repaints unnessesarily, then\r
3587    * we could skip some clipping work.  In other words, only request a full\r
3588    * redraw when the majority of pieces have changed positions (ie. flip, \r
3589    * gamestart and similar)  --Hawk\r
3590    */\r
3591   Boolean fullrepaint = repaint;\r
3592 \r
3593   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3594 \r
3595   if( DrawPositionNeedsFullRepaint() ) {\r
3596       fullrepaint = TRUE;\r
3597   }\r
3598 \r
3599   if (board == NULL) {\r
3600     if (!lastReqValid[nr]) {\r
3601       return;\r
3602     }\r
3603     board = lastReq[nr];\r
3604   } else {\r
3605     CopyBoard(lastReq[nr], board);\r
3606     lastReqValid[nr] = 1;\r
3607   }\r
3608 \r
3609   if (doingSizing) {\r
3610     return;\r
3611   }\r
3612 \r
3613   if (IsIconic(hwndMain)) {\r
3614     return;\r
3615   }\r
3616 \r
3617   if (hdc == NULL) {\r
3618     hdc = GetDC(hwndMain);\r
3619     if (!appData.monoMode) {\r
3620       SelectPalette(hdc, hPal, FALSE);\r
3621       RealizePalette(hdc);\r
3622     }\r
3623     releaseDC = TRUE;\r
3624   } else {\r
3625     releaseDC = FALSE;\r
3626   }\r
3627 \r
3628   /* Create some work-DCs */\r
3629   hdcmem = CreateCompatibleDC(hdc);\r
3630   tmphdc = CreateCompatibleDC(hdc);\r
3631 \r
3632   /* If dragging is in progress, we temporarely remove the piece */\r
3633   /* [HGM] or temporarily decrease count if stacked              */\r
3634   /*       !! Moved to before board compare !!                   */\r
3635   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3636     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3637     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3638             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3639         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3640     } else \r
3641     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3642             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3643         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3644     } else \r
3645         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3646   }\r
3647 \r
3648   /* Figure out which squares need updating by comparing the \r
3649    * newest board with the last drawn board and checking if\r
3650    * flipping has changed.\r
3651    */\r
3652   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3653     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3654       for (column = 0; column < BOARD_WIDTH; column++) {\r
3655         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3656           SquareToPos(row, column, &x, &y);\r
3657           clips[num_clips++] =\r
3658             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3659         }\r
3660       }\r
3661     }\r
3662    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3663     for (i=0; i<2; i++) {\r
3664       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3665           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3666         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3667             lastDrawnHighlight.sq[i].y >= 0) {\r
3668           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3669                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3670           clips[num_clips++] =\r
3671             CreateRectRgn(x - lineGap, y - lineGap, \r
3672                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3673         }\r
3674         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3675           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3676           clips[num_clips++] =\r
3677             CreateRectRgn(x - lineGap, y - lineGap, \r
3678                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3679         }\r
3680       }\r
3681     }\r
3682     for (i=0; i<2; i++) {\r
3683       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3684           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3685         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3686             lastDrawnPremove.sq[i].y >= 0) {\r
3687           SquareToPos(lastDrawnPremove.sq[i].y,\r
3688                       lastDrawnPremove.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         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3694             premoveHighlightInfo.sq[i].y >= 0) {\r
3695           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3696                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3697           clips[num_clips++] =\r
3698             CreateRectRgn(x - lineGap, y - lineGap, \r
3699                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3700         }\r
3701       }\r
3702     }\r
3703    } else { // nr == 1\r
3704         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3705         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3706         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3707         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3708       for (i=0; i<2; i++) {\r
3709         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3710             partnerHighlightInfo.sq[i].y >= 0) {\r
3711           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3712                       partnerHighlightInfo.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         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3718             oldPartnerHighlight.sq[i].y >= 0) {\r
3719           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3720                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3721           clips[num_clips++] =\r
3722             CreateRectRgn(x - lineGap, y - lineGap, \r
3723                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3724         }\r
3725       }\r
3726    }\r
3727   } else {\r
3728     fullrepaint = TRUE;\r
3729   }\r
3730 \r
3731   /* Create a buffer bitmap - this is the actual bitmap\r
3732    * being written to.  When all the work is done, we can\r
3733    * copy it to the real DC (the screen).  This avoids\r
3734    * the problems with flickering.\r
3735    */\r
3736   GetClientRect(hwndMain, &Rect);\r
3737   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3738                                         Rect.bottom-Rect.top+1);\r
3739   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3740   if (!appData.monoMode) {\r
3741     SelectPalette(hdcmem, hPal, FALSE);\r
3742   }\r
3743 \r
3744   /* Create clips for dragging */\r
3745   if (!fullrepaint) {\r
3746     if (dragInfo.from.x >= 0) {\r
3747       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3748       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3749     }\r
3750     if (dragInfo.start.x >= 0) {\r
3751       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3752       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3753     }\r
3754     if (dragInfo.pos.x >= 0) {\r
3755       x = dragInfo.pos.x - squareSize / 2;\r
3756       y = dragInfo.pos.y - squareSize / 2;\r
3757       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3758     }\r
3759     if (dragInfo.lastpos.x >= 0) {\r
3760       x = dragInfo.lastpos.x - squareSize / 2;\r
3761       y = dragInfo.lastpos.y - squareSize / 2;\r
3762       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3763     }\r
3764   }\r
3765 \r
3766   /* Are we animating a move?  \r
3767    * If so, \r
3768    *   - remove the piece from the board (temporarely)\r
3769    *   - calculate the clipping region\r
3770    */\r
3771   if (!fullrepaint) {\r
3772     if (animInfo.piece != EmptySquare) {\r
3773       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3774       x = boardRect.left + animInfo.lastpos.x;\r
3775       y = boardRect.top + animInfo.lastpos.y;\r
3776       x2 = boardRect.left + animInfo.pos.x;\r
3777       y2 = boardRect.top + animInfo.pos.y;\r
3778       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3779       /* Slight kludge.  The real problem is that after AnimateMove is\r
3780          done, the position on the screen does not match lastDrawn.\r
3781          This currently causes trouble only on e.p. captures in\r
3782          atomic, where the piece moves to an empty square and then\r
3783          explodes.  The old and new positions both had an empty square\r
3784          at the destination, but animation has drawn a piece there and\r
3785          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3786       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3787     }\r
3788   }\r
3789 \r
3790   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3791   if (num_clips == 0)\r
3792     fullrepaint = TRUE;\r
3793 \r
3794   /* Set clipping on the memory DC */\r
3795   if (!fullrepaint) {\r
3796     SelectClipRgn(hdcmem, clips[0]);\r
3797     for (x = 1; x < num_clips; x++) {\r
3798       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3799         abort();  // this should never ever happen!\r
3800     }\r
3801   }\r
3802 \r
3803   /* Do all the drawing to the memory DC */\r
3804   if(explodeInfo.radius) { // [HGM] atomic\r
3805         HBRUSH oldBrush;\r
3806         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3807         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3808         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3809         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3810         x += squareSize/2;\r
3811         y += squareSize/2;\r
3812         if(!fullrepaint) {\r
3813           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3814           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3815         }\r
3816         DrawGridOnDC(hdcmem);\r
3817         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3818         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3819         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3820         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3821         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3822         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3823         SelectObject(hdcmem, oldBrush);\r
3824   } else {\r
3825     DrawGridOnDC(hdcmem);\r
3826     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3827         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3828         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3829     } else {\r
3830         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3831         oldPartnerHighlight = partnerHighlightInfo;\r
3832     }\r
3833     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3834   }\r
3835   if(nr == 0) // [HGM] dual: markers only on left board\r
3836   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3837     for (column = 0; column < BOARD_WIDTH; column++) {\r
3838         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3839             HBRUSH oldBrush = SelectObject(hdcmem, \r
3840                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3841             SquareToPos(row, column, &x, &y);\r
3842             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3843                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3844             SelectObject(hdcmem, oldBrush);\r
3845         }\r
3846     }\r
3847   }\r
3848 \r
3849   if( appData.highlightMoveWithArrow ) {\r
3850     DrawArrowHighlight(hdcmem);\r
3851   }\r
3852 \r
3853   DrawCoordsOnDC(hdcmem);\r
3854 \r
3855   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3856                  /* to make sure lastDrawn contains what is actually drawn */\r
3857 \r
3858   /* Put the dragged piece back into place and draw it (out of place!) */\r
3859     if (dragged_piece != EmptySquare) {\r
3860     /* [HGM] or restack */\r
3861     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3862                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3863     else\r
3864     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3865                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3866     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3867     x = dragInfo.pos.x - squareSize / 2;\r
3868     y = dragInfo.pos.y - squareSize / 2;\r
3869     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3870                   ((int) dragInfo.piece < (int) BlackPawn), \r
3871                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3872   }   \r
3873   \r
3874   /* Put the animated piece back into place and draw it */\r
3875   if (animInfo.piece != EmptySquare) {\r
3876     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3877     x = boardRect.left + animInfo.pos.x;\r
3878     y = boardRect.top + animInfo.pos.y;\r
3879     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3880                   ((int) animInfo.piece < (int) BlackPawn),\r
3881                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3882   }\r
3883 \r
3884   /* Release the bufferBitmap by selecting in the old bitmap \r
3885    * and delete the memory DC\r
3886    */\r
3887   SelectObject(hdcmem, oldBitmap);\r
3888   DeleteDC(hdcmem);\r
3889 \r
3890   /* Set clipping on the target DC */\r
3891   if (!fullrepaint) {\r
3892     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3893         RECT rect;\r
3894         GetRgnBox(clips[x], &rect);\r
3895         DeleteObject(clips[x]);\r
3896         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
3897                           rect.right + wpMain.width/2, rect.bottom);\r
3898     }\r
3899     SelectClipRgn(hdc, clips[0]);\r
3900     for (x = 1; x < num_clips; x++) {\r
3901       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3902         abort();   // this should never ever happen!\r
3903     } \r
3904   }\r
3905 \r
3906   /* Copy the new bitmap onto the screen in one go.\r
3907    * This way we avoid any flickering\r
3908    */\r
3909   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3910   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
3911          boardRect.right - boardRect.left,\r
3912          boardRect.bottom - boardRect.top,\r
3913          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3914   if(saveDiagFlag) { \r
3915     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
3916     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
3917 \r
3918     GetObject(bufferBitmap, sizeof(b), &b);\r
3919     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
3920         bih.biSize = sizeof(BITMAPINFOHEADER);\r
3921         bih.biWidth = b.bmWidth;\r
3922         bih.biHeight = b.bmHeight;\r
3923         bih.biPlanes = 1;\r
3924         bih.biBitCount = b.bmBitsPixel;\r
3925         bih.biCompression = 0;\r
3926         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
3927         bih.biXPelsPerMeter = 0;\r
3928         bih.biYPelsPerMeter = 0;\r
3929         bih.biClrUsed = 0;\r
3930         bih.biClrImportant = 0;\r
3931 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
3932 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
3933         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
3934 //      fprintf(diagFile, "%8x\n", (int) pData);\r
3935 \r
3936         wb = b.bmWidthBytes;\r
3937         // count colors\r
3938         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
3939                 int k = ((int*) pData)[i];\r
3940                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3941                 if(j >= 16) break;\r
3942                 color[j] = k;\r
3943                 if(j >= nrColors) nrColors = j+1;\r
3944         }\r
3945         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
3946                 INT p = 0;\r
3947                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
3948                     for(w=0; w<(wb>>2); w+=2) {\r
3949                         int k = ((int*) pData)[(wb*i>>2) + w];\r
3950                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
3951                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
3952                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
3953                         pData[p++] = m | j<<4;\r
3954                     }\r
3955                     while(p&3) pData[p++] = 0;\r
3956                 }\r
3957                 fac = 3;\r
3958                 wb = ((wb+31)>>5)<<2;\r
3959         }\r
3960         // write BITMAPFILEHEADER\r
3961         fprintf(diagFile, "BM");\r
3962         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
3963         fputDW(diagFile, 0);\r
3964         fputDW(diagFile, 0x36 + (fac?64:0));\r
3965         // write BITMAPINFOHEADER\r
3966         fputDW(diagFile, 40);\r
3967         fputDW(diagFile, b.bmWidth);\r
3968         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
3969         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
3970         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
3971         fputDW(diagFile, 0);\r
3972         fputDW(diagFile, 0);\r
3973         fputDW(diagFile, 0);\r
3974         fputDW(diagFile, 0);\r
3975         fputDW(diagFile, 0);\r
3976         fputDW(diagFile, 0);\r
3977         // write color table\r
3978         if(fac)\r
3979         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
3980         // write bitmap data\r
3981         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
3982                 fputc(pData[i], diagFile);\r
3983         free(pData);\r
3984      }\r
3985   }\r
3986 \r
3987   SelectObject(tmphdc, oldBitmap);\r
3988 \r
3989   /* Massive cleanup */\r
3990   for (x = 0; x < num_clips; x++)\r
3991     DeleteObject(clips[x]);\r
3992 \r
3993   DeleteDC(tmphdc);\r
3994   DeleteObject(bufferBitmap);\r
3995 \r
3996   if (releaseDC) \r
3997     ReleaseDC(hwndMain, hdc);\r
3998   \r
3999   if (lastDrawnFlipView != flipView && nr == 0) {\r
4000     if (flipView)\r
4001       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4002     else\r
4003       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4004   }\r
4005 \r
4006 /*  CopyBoard(lastDrawn, board);*/\r
4007   lastDrawnHighlight = highlightInfo;\r
4008   lastDrawnPremove   = premoveHighlightInfo;\r
4009   lastDrawnFlipView = flipView;\r
4010   lastDrawnValid[nr] = 1;\r
4011 }\r
4012 \r
4013 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4014 int\r
4015 SaveDiagram(f)\r
4016      FILE *f;\r
4017 {\r
4018     saveDiagFlag = 1; diagFile = f;\r
4019     HDCDrawPosition(NULL, TRUE, NULL);\r
4020     saveDiagFlag = 0;\r
4021 \r
4022     fclose(f);\r
4023     return TRUE;\r
4024 }\r
4025 \r
4026 \r
4027 /*---------------------------------------------------------------------------*\\r
4028 | CLIENT PAINT PROCEDURE\r
4029 |   This is the main event-handler for the WM_PAINT message.\r
4030 |\r
4031 \*---------------------------------------------------------------------------*/\r
4032 VOID\r
4033 PaintProc(HWND hwnd)\r
4034 {\r
4035   HDC         hdc;\r
4036   PAINTSTRUCT ps;\r
4037   HFONT       oldFont;\r
4038 \r
4039   if((hdc = BeginPaint(hwnd, &ps))) {\r
4040     if (IsIconic(hwnd)) {\r
4041       DrawIcon(hdc, 2, 2, iconCurrent);\r
4042     } else {\r
4043       if (!appData.monoMode) {\r
4044         SelectPalette(hdc, hPal, FALSE);\r
4045         RealizePalette(hdc);\r
4046       }\r
4047       HDCDrawPosition(hdc, 1, NULL);\r
4048       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4049         flipView = !flipView; partnerUp = !partnerUp;\r
4050         HDCDrawPosition(hdc, 1, NULL);\r
4051         flipView = !flipView; partnerUp = !partnerUp;\r
4052       }\r
4053       oldFont =\r
4054         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4055       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4056                  ETO_CLIPPED|ETO_OPAQUE,\r
4057                  &messageRect, messageText, strlen(messageText), NULL);\r
4058       SelectObject(hdc, oldFont);\r
4059       DisplayBothClocks();\r
4060       DisplayLogos();\r
4061     }\r
4062     EndPaint(hwnd,&ps);\r
4063   }\r
4064 \r
4065   return;\r
4066 }\r
4067 \r
4068 \r
4069 /*\r
4070  * If the user selects on a border boundary, return -1; if off the board,\r
4071  *   return -2.  Otherwise map the event coordinate to the square.\r
4072  * The offset boardRect.left or boardRect.top must already have been\r
4073  *   subtracted from x.\r
4074  */\r
4075 int EventToSquare(x, limit)\r
4076      int x, limit;\r
4077 {\r
4078   if (x <= 0)\r
4079     return -2;\r
4080   if (x < lineGap)\r
4081     return -1;\r
4082   x -= lineGap;\r
4083   if ((x % (squareSize + lineGap)) >= squareSize)\r
4084     return -1;\r
4085   x /= (squareSize + lineGap);\r
4086     if (x >= limit)\r
4087     return -2;\r
4088   return x;\r
4089 }\r
4090 \r
4091 typedef struct {\r
4092   char piece;\r
4093   int command;\r
4094   char* name;\r
4095 } DropEnable;\r
4096 \r
4097 DropEnable dropEnables[] = {\r
4098   { 'P', DP_Pawn, N_("Pawn") },\r
4099   { 'N', DP_Knight, N_("Knight") },\r
4100   { 'B', DP_Bishop, N_("Bishop") },\r
4101   { 'R', DP_Rook, N_("Rook") },\r
4102   { 'Q', DP_Queen, N_("Queen") },\r
4103 };\r
4104 \r
4105 VOID\r
4106 SetupDropMenu(HMENU hmenu)\r
4107 {\r
4108   int i, count, enable;\r
4109   char *p;\r
4110   extern char white_holding[], black_holding[];\r
4111   char item[MSG_SIZ];\r
4112 \r
4113   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4114     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4115                dropEnables[i].piece);\r
4116     count = 0;\r
4117     while (p && *p++ == dropEnables[i].piece) count++;\r
4118       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4119     enable = count > 0 || !appData.testLegality\r
4120       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4121                       && !appData.icsActive);\r
4122     ModifyMenu(hmenu, dropEnables[i].command,\r
4123                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4124                dropEnables[i].command, item);\r
4125   }\r
4126 }\r
4127 \r
4128 void DragPieceBegin(int x, int y, Boolean instantly)\r
4129 {\r
4130       dragInfo.lastpos.x = boardRect.left + x;\r
4131       dragInfo.lastpos.y = boardRect.top + y;\r
4132       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4133       dragInfo.from.x = fromX;\r
4134       dragInfo.from.y = fromY;\r
4135       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4136       dragInfo.start = dragInfo.from;\r
4137       SetCapture(hwndMain);\r
4138 }\r
4139 \r
4140 void DragPieceEnd(int x, int y)\r
4141 {\r
4142     ReleaseCapture();\r
4143     dragInfo.start.x = dragInfo.start.y = -1;\r
4144     dragInfo.from = dragInfo.start;\r
4145     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4146 }\r
4147 \r
4148 void ChangeDragPiece(ChessSquare piece)\r
4149 {\r
4150     dragInfo.piece = piece;\r
4151 }\r
4152 \r
4153 /* Event handler for mouse messages */\r
4154 VOID\r
4155 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4156 {\r
4157   int x, y, menuNr;\r
4158   POINT pt;\r
4159   static int recursive = 0;\r
4160   HMENU hmenu;\r
4161   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4162 \r
4163   if (recursive) {\r
4164     if (message == WM_MBUTTONUP) {\r
4165       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4166          to the middle button: we simulate pressing the left button too!\r
4167          */\r
4168       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4169       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4170     }\r
4171     return;\r
4172   }\r
4173   recursive++;\r
4174   \r
4175   pt.x = LOWORD(lParam);\r
4176   pt.y = HIWORD(lParam);\r
4177   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4178   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4179   if (!flipView && y >= 0) {\r
4180     y = BOARD_HEIGHT - 1 - y;\r
4181   }\r
4182   if (flipView && x >= 0) {\r
4183     x = BOARD_WIDTH - 1 - x;\r
4184   }\r
4185 \r
4186   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4187 \r
4188   switch (message) {\r
4189   case WM_LBUTTONDOWN:\r
4190       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4191         ClockClick(flipClock); break;\r
4192       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4193         ClockClick(!flipClock); break;\r
4194       }\r
4195       dragInfo.start.x = dragInfo.start.y = -1;\r
4196       dragInfo.from = dragInfo.start;\r
4197     if(fromX == -1 && frozen) { // not sure where this is for\r
4198                 fromX = fromY = -1; \r
4199       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4200       break;\r
4201     }\r
4202       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4203       DrawPosition(TRUE, NULL);\r
4204     break;\r
4205 \r
4206   case WM_LBUTTONUP:\r
4207       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4208       DrawPosition(TRUE, NULL);\r
4209     break;\r
4210 \r
4211   case WM_MOUSEMOVE:\r
4212     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4213     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4214     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4215     if ((appData.animateDragging || appData.highlightDragging)\r
4216         && (wParam & MK_LBUTTON)\r
4217         && dragInfo.from.x >= 0) \r
4218     {\r
4219       BOOL full_repaint = FALSE;\r
4220 \r
4221       if (appData.animateDragging) {\r
4222         dragInfo.pos = pt;\r
4223       }\r
4224       if (appData.highlightDragging) {\r
4225         SetHighlights(fromX, fromY, x, y);\r
4226         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4227             full_repaint = TRUE;\r
4228         }\r
4229       }\r
4230       \r
4231       DrawPosition( full_repaint, NULL);\r
4232       \r
4233       dragInfo.lastpos = dragInfo.pos;\r
4234     }\r
4235     break;\r
4236 \r
4237   case WM_MOUSEWHEEL: // [DM]\r
4238     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4239        /* Mouse Wheel is being rolled forward\r
4240         * Play moves forward\r
4241         */\r
4242        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4243                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4244        /* Mouse Wheel is being rolled backward\r
4245         * Play moves backward\r
4246         */\r
4247        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4248                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4249     }\r
4250     break;\r
4251 \r
4252   case WM_MBUTTONUP:\r
4253   case WM_RBUTTONUP:\r
4254     ReleaseCapture();\r
4255     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4256     break;\r
4257  \r
4258   case WM_MBUTTONDOWN:\r
4259   case WM_RBUTTONDOWN:\r
4260     ErrorPopDown();\r
4261     ReleaseCapture();\r
4262     fromX = fromY = -1;\r
4263     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4264     dragInfo.start.x = dragInfo.start.y = -1;\r
4265     dragInfo.from = dragInfo.start;\r
4266     dragInfo.lastpos = dragInfo.pos;\r
4267     if (appData.highlightDragging) {\r
4268       ClearHighlights();\r
4269     }\r
4270     if(y == -2) {\r
4271       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4272       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4273           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4274       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4275           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4276       }\r
4277       break;\r
4278     }\r
4279     DrawPosition(TRUE, NULL);\r
4280 \r
4281     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4282     switch (menuNr) {\r
4283     case 0:\r
4284       if (message == WM_MBUTTONDOWN) {\r
4285         buttonCount = 3;  /* even if system didn't think so */\r
4286         if (wParam & MK_SHIFT) \r
4287           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4288         else\r
4289           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4290       } else { /* message == WM_RBUTTONDOWN */\r
4291         /* Just have one menu, on the right button.  Windows users don't\r
4292            think to try the middle one, and sometimes other software steals\r
4293            it, or it doesn't really exist. */\r
4294         if(gameInfo.variant != VariantShogi)\r
4295             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4296         else\r
4297             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4298       }\r
4299       break;\r
4300     case 2:\r
4301       SetCapture(hwndMain);\r
4302       break;\r
4303     case 1:\r
4304       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4305       SetupDropMenu(hmenu);\r
4306       MenuPopup(hwnd, pt, hmenu, -1);\r
4307     default:\r
4308       break;\r
4309     }\r
4310     break;\r
4311   }\r
4312 \r
4313   recursive--;\r
4314 }\r
4315 \r
4316 /* Preprocess messages for buttons in main window */\r
4317 LRESULT CALLBACK\r
4318 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4319 {\r
4320   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4321   int i, dir;\r
4322 \r
4323   for (i=0; i<N_BUTTONS; i++) {\r
4324     if (buttonDesc[i].id == id) break;\r
4325   }\r
4326   if (i == N_BUTTONS) return 0;\r
4327   switch (message) {\r
4328   case WM_KEYDOWN:\r
4329     switch (wParam) {\r
4330     case VK_LEFT:\r
4331     case VK_RIGHT:\r
4332       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4333       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4334       return TRUE;\r
4335     }\r
4336     break;\r
4337   case WM_CHAR:\r
4338     switch (wParam) {\r
4339     case '\r':\r
4340       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4341       return TRUE;\r
4342     default:\r
4343       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4344         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4345         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4346         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4347         SetFocus(h);\r
4348         SendMessage(h, WM_CHAR, wParam, lParam);\r
4349         return TRUE;\r
4350       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4351         TypeInEvent((char)wParam);\r
4352       }\r
4353       break;\r
4354     }\r
4355     break;\r
4356   }\r
4357   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4358 }\r
4359 \r
4360 /* Process messages for Promotion dialog box */\r
4361 LRESULT CALLBACK\r
4362 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4363 {\r
4364   char promoChar;\r
4365 \r
4366   switch (message) {\r
4367   case WM_INITDIALOG: /* message: initialize dialog box */\r
4368     /* Center the dialog over the application window */\r
4369     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4370     Translate(hDlg, DLG_PromotionKing);\r
4371     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4372       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4373        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4374        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4375                SW_SHOW : SW_HIDE);\r
4376     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4377     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4378        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4379          PieceToChar(WhiteAngel) != '~') ||\r
4380         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4381          PieceToChar(BlackAngel) != '~')   ) ?\r
4382                SW_SHOW : SW_HIDE);\r
4383     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4384        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4385          PieceToChar(WhiteMarshall) != '~') ||\r
4386         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4387          PieceToChar(BlackMarshall) != '~')   ) ?\r
4388                SW_SHOW : SW_HIDE);\r
4389     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4390     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4391        gameInfo.variant != VariantShogi ?\r
4392                SW_SHOW : SW_HIDE);\r
4393     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4394        gameInfo.variant != VariantShogi ?\r
4395                SW_SHOW : SW_HIDE);\r
4396     if(gameInfo.variant == VariantShogi) {\r
4397         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4398         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4399         SetWindowText(hDlg, "Promote?");\r
4400     }\r
4401     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4402        gameInfo.variant == VariantSuper ?\r
4403                SW_SHOW : SW_HIDE);\r
4404     return TRUE;\r
4405 \r
4406   case WM_COMMAND: /* message: received a command */\r
4407     switch (LOWORD(wParam)) {\r
4408     case IDCANCEL:\r
4409       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4410       ClearHighlights();\r
4411       DrawPosition(FALSE, NULL);\r
4412       return TRUE;\r
4413     case PB_King:\r
4414       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4415       break;\r
4416     case PB_Queen:\r
4417       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4418       break;\r
4419     case PB_Rook:\r
4420       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4421       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4422       break;\r
4423     case PB_Bishop:\r
4424       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4425       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4426       break;\r
4427     case PB_Chancellor:\r
4428       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4429       break;\r
4430     case PB_Archbishop:\r
4431       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4432       break;\r
4433     case PB_Knight:\r
4434       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4435       break;\r
4436     default:\r
4437       return FALSE;\r
4438     }\r
4439     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4440     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4441     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4442     fromX = fromY = -1;\r
4443     if (!appData.highlightLastMove) {\r
4444       ClearHighlights();\r
4445       DrawPosition(FALSE, NULL);\r
4446     }\r
4447     return TRUE;\r
4448   }\r
4449   return FALSE;\r
4450 }\r
4451 \r
4452 /* Pop up promotion dialog */\r
4453 VOID\r
4454 PromotionPopup(HWND hwnd)\r
4455 {\r
4456   FARPROC lpProc;\r
4457 \r
4458   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4459   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4460     hwnd, (DLGPROC)lpProc);\r
4461   FreeProcInstance(lpProc);\r
4462 }\r
4463 \r
4464 void\r
4465 PromotionPopUp()\r
4466 {\r
4467   DrawPosition(TRUE, NULL);\r
4468   PromotionPopup(hwndMain);\r
4469 }\r
4470 \r
4471 /* Toggle ShowThinking */\r
4472 VOID\r
4473 ToggleShowThinking()\r
4474 {\r
4475   appData.showThinking = !appData.showThinking;\r
4476   ShowThinkingEvent();\r
4477 }\r
4478 \r
4479 VOID\r
4480 LoadGameDialog(HWND hwnd, char* title)\r
4481 {\r
4482   UINT number = 0;\r
4483   FILE *f;\r
4484   char fileTitle[MSG_SIZ];\r
4485   f = OpenFileDialog(hwnd, "rb", "",\r
4486                      appData.oldSaveStyle ? "gam" : "pgn",\r
4487                      GAME_FILT,\r
4488                      title, &number, fileTitle, NULL);\r
4489   if (f != NULL) {\r
4490     cmailMsgLoaded = FALSE;\r
4491     if (number == 0) {\r
4492       int error = GameListBuild(f);\r
4493       if (error) {\r
4494         DisplayError(_("Cannot build game list"), error);\r
4495       } else if (!ListEmpty(&gameList) &&\r
4496                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4497         GameListPopUp(f, fileTitle);\r
4498         return;\r
4499       }\r
4500       GameListDestroy();\r
4501       number = 1;\r
4502     }\r
4503     LoadGame(f, number, fileTitle, FALSE);\r
4504   }\r
4505 }\r
4506 \r
4507 int get_term_width()\r
4508 {\r
4509     HDC hdc;\r
4510     TEXTMETRIC tm;\r
4511     RECT rc;\r
4512     HFONT hfont, hold_font;\r
4513     LOGFONT lf;\r
4514     HWND hText;\r
4515 \r
4516     if (hwndConsole)\r
4517         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4518     else\r
4519         return 79;\r
4520 \r
4521     // get the text metrics\r
4522     hdc = GetDC(hText);\r
4523     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4524     if (consoleCF.dwEffects & CFE_BOLD)\r
4525         lf.lfWeight = FW_BOLD;\r
4526     if (consoleCF.dwEffects & CFE_ITALIC)\r
4527         lf.lfItalic = TRUE;\r
4528     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4529         lf.lfStrikeOut = TRUE;\r
4530     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4531         lf.lfUnderline = TRUE;\r
4532     hfont = CreateFontIndirect(&lf);\r
4533     hold_font = SelectObject(hdc, hfont);\r
4534     GetTextMetrics(hdc, &tm);\r
4535     SelectObject(hdc, hold_font);\r
4536     DeleteObject(hfont);\r
4537     ReleaseDC(hText, hdc);\r
4538 \r
4539     // get the rectangle\r
4540     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4541 \r
4542     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4543 }\r
4544 \r
4545 void UpdateICSWidth(HWND hText)\r
4546 {\r
4547     LONG old_width, new_width;\r
4548 \r
4549     new_width = get_term_width(hText, FALSE);\r
4550     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4551     if (new_width != old_width)\r
4552     {\r
4553         ics_update_width(new_width);\r
4554         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4555     }\r
4556 }\r
4557 \r
4558 VOID\r
4559 ChangedConsoleFont()\r
4560 {\r
4561   CHARFORMAT cfmt;\r
4562   CHARRANGE tmpsel, sel;\r
4563   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4564   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4565   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4566   PARAFORMAT paraf;\r
4567 \r
4568   cfmt.cbSize = sizeof(CHARFORMAT);\r
4569   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4570     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4571                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4572   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4573    * size.  This was undocumented in the version of MSVC++ that I had\r
4574    * when I wrote the code, but is apparently documented now.\r
4575    */\r
4576   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4577   cfmt.bCharSet = f->lf.lfCharSet;\r
4578   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4579   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4580   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4581   /* Why are the following seemingly needed too? */\r
4582   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4583   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4584   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4585   tmpsel.cpMin = 0;\r
4586   tmpsel.cpMax = -1; /*999999?*/\r
4587   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4588   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4589   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4590    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4591    */\r
4592   paraf.cbSize = sizeof(paraf);\r
4593   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4594   paraf.dxStartIndent = 0;\r
4595   paraf.dxOffset = WRAP_INDENT;\r
4596   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4597   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4598   UpdateICSWidth(hText);\r
4599 }\r
4600 \r
4601 /*---------------------------------------------------------------------------*\\r
4602  *\r
4603  * Window Proc for main window\r
4604  *\r
4605 \*---------------------------------------------------------------------------*/\r
4606 \r
4607 /* Process messages for main window, etc. */\r
4608 LRESULT CALLBACK\r
4609 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4610 {\r
4611   FARPROC lpProc;\r
4612   int wmId, wmEvent;\r
4613   char *defName;\r
4614   FILE *f;\r
4615   UINT number;\r
4616   char fileTitle[MSG_SIZ];\r
4617   char buf[MSG_SIZ];\r
4618   static SnapData sd;\r
4619   static int peek=0;\r
4620 \r
4621   switch (message) {\r
4622 \r
4623   case WM_PAINT: /* message: repaint portion of window */\r
4624     PaintProc(hwnd);\r
4625     break;\r
4626 \r
4627   case WM_ERASEBKGND:\r
4628     if (IsIconic(hwnd)) {\r
4629       /* Cheat; change the message */\r
4630       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4631     } else {\r
4632       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4633     }\r
4634     break;\r
4635 \r
4636   case WM_LBUTTONDOWN:\r
4637   case WM_MBUTTONDOWN:\r
4638   case WM_RBUTTONDOWN:\r
4639   case WM_LBUTTONUP:\r
4640   case WM_MBUTTONUP:\r
4641   case WM_RBUTTONUP:\r
4642   case WM_MOUSEMOVE:\r
4643   case WM_MOUSEWHEEL:\r
4644     MouseEvent(hwnd, message, wParam, lParam);\r
4645     break;\r
4646 \r
4647   case WM_KEYUP:\r
4648     if((char)wParam == '\b') {\r
4649       ForwardEvent(); peek = 0;\r
4650     }\r
4651 \r
4652     JAWS_KBUP_NAVIGATION\r
4653 \r
4654     break;\r
4655 \r
4656   case WM_KEYDOWN:\r
4657     if((char)wParam == '\b') {\r
4658       if(!peek) BackwardEvent(), peek = 1;\r
4659     }\r
4660 \r
4661     JAWS_KBDOWN_NAVIGATION\r
4662 \r
4663     break;\r
4664 \r
4665   case WM_CHAR:\r
4666     \r
4667     JAWS_ALT_INTERCEPT\r
4668 \r
4669     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4670         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4671         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4672         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4673         SetFocus(h);\r
4674         SendMessage(h, message, wParam, lParam);\r
4675     } else if(lParam != KF_REPEAT) {\r
4676         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4677                 TypeInEvent((char)wParam);\r
4678         } else if((char)wParam == 003) CopyGameToClipboard();\r
4679          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4680     }\r
4681 \r
4682     break;\r
4683 \r
4684   case WM_PALETTECHANGED:\r
4685     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4686       int nnew;\r
4687       HDC hdc = GetDC(hwndMain);\r
4688       SelectPalette(hdc, hPal, TRUE);\r
4689       nnew = RealizePalette(hdc);\r
4690       if (nnew > 0) {\r
4691         paletteChanged = TRUE;\r
4692         InvalidateRect(hwnd, &boardRect, FALSE);\r
4693       }\r
4694       ReleaseDC(hwnd, hdc);\r
4695     }\r
4696     break;\r
4697 \r
4698   case WM_QUERYNEWPALETTE:\r
4699     if (!appData.monoMode /*&& paletteChanged*/) {\r
4700       int nnew;\r
4701       HDC hdc = GetDC(hwndMain);\r
4702       paletteChanged = FALSE;\r
4703       SelectPalette(hdc, hPal, FALSE);\r
4704       nnew = RealizePalette(hdc);\r
4705       if (nnew > 0) {\r
4706         InvalidateRect(hwnd, &boardRect, FALSE);\r
4707       }\r
4708       ReleaseDC(hwnd, hdc);\r
4709       return TRUE;\r
4710     }\r
4711     return FALSE;\r
4712 \r
4713   case WM_COMMAND: /* message: command from application menu */\r
4714     wmId    = LOWORD(wParam);\r
4715     wmEvent = HIWORD(wParam);\r
4716 \r
4717     switch (wmId) {\r
4718     case IDM_NewGame:\r
4719       ResetGameEvent();\r
4720       SAY("new game enter a move to play against the computer with white");\r
4721       break;\r
4722 \r
4723     case IDM_NewGameFRC:\r
4724       if( NewGameFRC() == 0 ) {\r
4725         ResetGameEvent();\r
4726       }\r
4727       break;\r
4728 \r
4729     case IDM_NewVariant:\r
4730       NewVariantPopup(hwnd);\r
4731       break;\r
4732 \r
4733     case IDM_LoadGame:\r
4734       LoadGameDialog(hwnd, _("Load Game from File"));\r
4735       break;\r
4736 \r
4737     case IDM_LoadNextGame:\r
4738       ReloadGame(1);\r
4739       break;\r
4740 \r
4741     case IDM_LoadPrevGame:\r
4742       ReloadGame(-1);\r
4743       break;\r
4744 \r
4745     case IDM_ReloadGame:\r
4746       ReloadGame(0);\r
4747       break;\r
4748 \r
4749     case IDM_LoadPosition:\r
4750       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4751         Reset(FALSE, TRUE);\r
4752       }\r
4753       number = 1;\r
4754       f = OpenFileDialog(hwnd, "rb", "",\r
4755                          appData.oldSaveStyle ? "pos" : "fen",\r
4756                          POSITION_FILT,\r
4757                          _("Load Position from File"), &number, fileTitle, NULL);\r
4758       if (f != NULL) {\r
4759         LoadPosition(f, number, fileTitle);\r
4760       }\r
4761       break;\r
4762 \r
4763     case IDM_LoadNextPosition:\r
4764       ReloadPosition(1);\r
4765       break;\r
4766 \r
4767     case IDM_LoadPrevPosition:\r
4768       ReloadPosition(-1);\r
4769       break;\r
4770 \r
4771     case IDM_ReloadPosition:\r
4772       ReloadPosition(0);\r
4773       break;\r
4774 \r
4775     case IDM_SaveGame:\r
4776       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4777       f = OpenFileDialog(hwnd, "a", defName,\r
4778                          appData.oldSaveStyle ? "gam" : "pgn",\r
4779                          GAME_FILT,\r
4780                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4781       if (f != NULL) {\r
4782         SaveGame(f, 0, "");\r
4783       }\r
4784       break;\r
4785 \r
4786     case IDM_SavePosition:\r
4787       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4788       f = OpenFileDialog(hwnd, "a", defName,\r
4789                          appData.oldSaveStyle ? "pos" : "fen",\r
4790                          POSITION_FILT,\r
4791                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4792       if (f != NULL) {\r
4793         SavePosition(f, 0, "");\r
4794       }\r
4795       break;\r
4796 \r
4797     case IDM_SaveDiagram:\r
4798       defName = "diagram";\r
4799       f = OpenFileDialog(hwnd, "wb", defName,\r
4800                          "bmp",\r
4801                          DIAGRAM_FILT,\r
4802                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4803       if (f != NULL) {\r
4804         SaveDiagram(f);\r
4805       }\r
4806       break;\r
4807 \r
4808     case IDM_CopyGame:\r
4809       CopyGameToClipboard();\r
4810       break;\r
4811 \r
4812     case IDM_PasteGame:\r
4813       PasteGameFromClipboard();\r
4814       break;\r
4815 \r
4816     case IDM_CopyGameListToClipboard:\r
4817       CopyGameListToClipboard();\r
4818       break;\r
4819 \r
4820     /* [AS] Autodetect FEN or PGN data */\r
4821     case IDM_PasteAny:\r
4822       PasteGameOrFENFromClipboard();\r
4823       break;\r
4824 \r
4825     /* [AS] Move history */\r
4826     case IDM_ShowMoveHistory:\r
4827         if( MoveHistoryIsUp() ) {\r
4828             MoveHistoryPopDown();\r
4829         }\r
4830         else {\r
4831             MoveHistoryPopUp();\r
4832         }\r
4833         break;\r
4834 \r
4835     /* [AS] Eval graph */\r
4836     case IDM_ShowEvalGraph:\r
4837         if( EvalGraphIsUp() ) {\r
4838             EvalGraphPopDown();\r
4839         }\r
4840         else {\r
4841             EvalGraphPopUp();\r
4842             SetFocus(hwndMain);\r
4843         }\r
4844         break;\r
4845 \r
4846     /* [AS] Engine output */\r
4847     case IDM_ShowEngineOutput:\r
4848         if( EngineOutputIsUp() ) {\r
4849             EngineOutputPopDown();\r
4850         }\r
4851         else {\r
4852             EngineOutputPopUp();\r
4853         }\r
4854         break;\r
4855 \r
4856     /* [AS] User adjudication */\r
4857     case IDM_UserAdjudication_White:\r
4858         UserAdjudicationEvent( +1 );\r
4859         break;\r
4860 \r
4861     case IDM_UserAdjudication_Black:\r
4862         UserAdjudicationEvent( -1 );\r
4863         break;\r
4864 \r
4865     case IDM_UserAdjudication_Draw:\r
4866         UserAdjudicationEvent( 0 );\r
4867         break;\r
4868 \r
4869     /* [AS] Game list options dialog */\r
4870     case IDM_GameListOptions:\r
4871       GameListOptions();\r
4872       break;\r
4873 \r
4874     case IDM_NewChat:\r
4875       ChatPopUp(NULL);\r
4876       break;\r
4877 \r
4878     case IDM_CopyPosition:\r
4879       CopyFENToClipboard();\r
4880       break;\r
4881 \r
4882     case IDM_PastePosition:\r
4883       PasteFENFromClipboard();\r
4884       break;\r
4885 \r
4886     case IDM_MailMove:\r
4887       MailMoveEvent();\r
4888       break;\r
4889 \r
4890     case IDM_ReloadCMailMsg:\r
4891       Reset(TRUE, TRUE);\r
4892       ReloadCmailMsgEvent(FALSE);\r
4893       break;\r
4894 \r
4895     case IDM_Minimize:\r
4896       ShowWindow(hwnd, SW_MINIMIZE);\r
4897       break;\r
4898 \r
4899     case IDM_Exit:\r
4900       ExitEvent(0);\r
4901       break;\r
4902 \r
4903     case IDM_MachineWhite:\r
4904       MachineWhiteEvent();\r
4905       /*\r
4906        * refresh the tags dialog only if it's visible\r
4907        */\r
4908       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4909           char *tags;\r
4910           tags = PGNTags(&gameInfo);\r
4911           TagsPopUp(tags, CmailMsg());\r
4912           free(tags);\r
4913       }\r
4914       SAY("computer starts playing white");\r
4915       break;\r
4916 \r
4917     case IDM_MachineBlack:\r
4918       MachineBlackEvent();\r
4919       /*\r
4920        * refresh the tags dialog only if it's visible\r
4921        */\r
4922       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4923           char *tags;\r
4924           tags = PGNTags(&gameInfo);\r
4925           TagsPopUp(tags, CmailMsg());\r
4926           free(tags);\r
4927       }\r
4928       SAY("computer starts playing black");\r
4929       break;\r
4930 \r
4931     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
4932       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
4933       break;\r
4934 \r
4935     case IDM_TwoMachines:\r
4936       TwoMachinesEvent();\r
4937       /*\r
4938        * refresh the tags dialog only if it's visible\r
4939        */\r
4940       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4941           char *tags;\r
4942           tags = PGNTags(&gameInfo);\r
4943           TagsPopUp(tags, CmailMsg());\r
4944           free(tags);\r
4945       }\r
4946       SAY("computer starts playing both sides");\r
4947       break;\r
4948 \r
4949     case IDM_AnalysisMode:\r
4950       if (!first.analysisSupport) {\r
4951         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4952         DisplayError(buf, 0);\r
4953       } else {\r
4954         SAY("analyzing current position");\r
4955         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
4956         if (appData.icsActive) {\r
4957                if (gameMode != IcsObserving) {\r
4958                  snprintf(buf, MSG_SIZ, "You are not observing a game");\r
4959                        DisplayError(buf, 0);\r
4960                        /* secure check */\r
4961                        if (appData.icsEngineAnalyze) {\r
4962                                if (appData.debugMode) \r
4963                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
4964                                ExitAnalyzeMode();\r
4965                                ModeHighlight();\r
4966                                break;\r
4967                        }\r
4968                        break;\r
4969                } else {\r
4970                        /* if enable, user want disable icsEngineAnalyze */\r
4971                        if (appData.icsEngineAnalyze) {\r
4972                                ExitAnalyzeMode();\r
4973                                ModeHighlight();\r
4974                                break;\r
4975                        }\r
4976                        appData.icsEngineAnalyze = TRUE;\r
4977                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
4978                }\r
4979         } \r
4980         if (!appData.showThinking) ToggleShowThinking();\r
4981         AnalyzeModeEvent();\r
4982       }\r
4983       break;\r
4984 \r
4985     case IDM_AnalyzeFile:\r
4986       if (!first.analysisSupport) {\r
4987         char buf[MSG_SIZ];\r
4988           snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
4989         DisplayError(buf, 0);\r
4990       } else {\r
4991         if (!appData.showThinking) ToggleShowThinking();\r
4992         AnalyzeFileEvent();\r
4993 //      LoadGameDialog(hwnd, _("Analyze Game from File"));\r
4994         AnalysisPeriodicEvent(1);\r
4995       }\r
4996       break;\r
4997 \r
4998     case IDM_IcsClient:\r
4999       IcsClientEvent();\r
5000       break;\r
5001 \r
5002     case IDM_EditGame:\r
5003     case IDM_EditGame2:\r
5004       EditGameEvent();\r
5005       SAY("edit game");\r
5006       break;\r
5007 \r
5008     case IDM_EditPosition:\r
5009     case IDM_EditPosition2:\r
5010       EditPositionEvent();\r
5011       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5012       break;\r
5013 \r
5014     case IDM_Training:\r
5015       TrainingEvent();\r
5016       break;\r
5017 \r
5018     case IDM_ShowGameList:\r
5019       ShowGameListProc();\r
5020       break;\r
5021 \r
5022     case IDM_EditProgs1:\r
5023       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5024       break;\r
5025 \r
5026     case IDM_LoadProg1:\r
5027      LoadEnginePopUp(hwndMain, 0);\r
5028       break;\r
5029 \r
5030     case IDM_LoadProg2:\r
5031      LoadEnginePopUp(hwndMain, 1);\r
5032       break;\r
5033 \r
5034     case IDM_EditServers:\r
5035       EditTagsPopUp(icsNames, &icsNames);\r
5036       break;\r
5037 \r
5038     case IDM_EditTags:\r
5039     case IDM_Tags:\r
5040       EditTagsProc();\r
5041       break;\r
5042 \r
5043     case IDM_EditBook:\r
5044       EditBookEvent();\r
5045       break;\r
5046 \r
5047     case IDM_EditComment:\r
5048     case IDM_Comment:\r
5049       if (commentUp && editComment) {\r
5050         CommentPopDown();\r
5051       } else {\r
5052         EditCommentEvent();\r
5053       }\r
5054       break;\r
5055 \r
5056     case IDM_Pause:\r
5057       PauseEvent();\r
5058       break;\r
5059 \r
5060     case IDM_Accept:\r
5061       AcceptEvent();\r
5062       break;\r
5063 \r
5064     case IDM_Decline:\r
5065       DeclineEvent();\r
5066       break;\r
5067 \r
5068     case IDM_Rematch:\r
5069       RematchEvent();\r
5070       break;\r
5071 \r
5072     case IDM_CallFlag:\r
5073       CallFlagEvent();\r
5074       break;\r
5075 \r
5076     case IDM_Draw:\r
5077       DrawEvent();\r
5078       break;\r
5079 \r
5080     case IDM_Adjourn:\r
5081       AdjournEvent();\r
5082       break;\r
5083 \r
5084     case IDM_Abort:\r
5085       AbortEvent();\r
5086       break;\r
5087 \r
5088     case IDM_Resign:\r
5089       ResignEvent();\r
5090       break;\r
5091 \r
5092     case IDM_StopObserving:\r
5093       StopObservingEvent();\r
5094       break;\r
5095 \r
5096     case IDM_StopExamining:\r
5097       StopExaminingEvent();\r
5098       break;\r
5099 \r
5100     case IDM_Upload:\r
5101       UploadGameEvent();\r
5102       break;\r
5103 \r
5104     case IDM_TypeInMove:\r
5105       TypeInEvent('\000');\r
5106       break;\r
5107 \r
5108     case IDM_TypeInName:\r
5109       PopUpNameDialog('\000');\r
5110       break;\r
5111 \r
5112     case IDM_Backward:\r
5113       BackwardEvent();\r
5114       SetFocus(hwndMain);\r
5115       break;\r
5116 \r
5117     JAWS_MENU_ITEMS\r
5118 \r
5119     case IDM_Forward:\r
5120       ForwardEvent();\r
5121       SetFocus(hwndMain);\r
5122       break;\r
5123 \r
5124     case IDM_ToStart:\r
5125       ToStartEvent();\r
5126       SetFocus(hwndMain);\r
5127       break;\r
5128 \r
5129     case IDM_ToEnd:\r
5130       ToEndEvent();\r
5131       SetFocus(hwndMain);\r
5132       break;\r
5133 \r
5134     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5135     case OPT_GameListPrev:\r
5136       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5137       break;\r
5138 \r
5139     case IDM_Revert:\r
5140       RevertEvent(FALSE);\r
5141       break;\r
5142 \r
5143     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5144       RevertEvent(TRUE);\r
5145       break;\r
5146 \r
5147     case IDM_TruncateGame:\r
5148       TruncateGameEvent();\r
5149       break;\r
5150 \r
5151     case IDM_MoveNow:\r
5152       MoveNowEvent();\r
5153       break;\r
5154 \r
5155     case IDM_RetractMove:\r
5156       RetractMoveEvent();\r
5157       break;\r
5158 \r
5159     case IDM_FlipView:\r
5160       flipView = !flipView;\r
5161       DrawPosition(FALSE, NULL);\r
5162       break;\r
5163 \r
5164     case IDM_FlipClock:\r
5165       flipClock = !flipClock;\r
5166       DisplayBothClocks();\r
5167       DisplayLogos();\r
5168       break;\r
5169 \r
5170     case IDM_MuteSounds:\r
5171       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5172       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5173                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5174       break;\r
5175 \r
5176     case IDM_GeneralOptions:\r
5177       GeneralOptionsPopup(hwnd);\r
5178       DrawPosition(TRUE, NULL);\r
5179       break;\r
5180 \r
5181     case IDM_BoardOptions:\r
5182       BoardOptionsPopup(hwnd);\r
5183       break;\r
5184 \r
5185     case IDM_EnginePlayOptions:\r
5186       EnginePlayOptionsPopup(hwnd);\r
5187       break;\r
5188 \r
5189     case IDM_Engine1Options:\r
5190       EngineOptionsPopup(hwnd, &first);\r
5191       break;\r
5192 \r
5193     case IDM_Engine2Options:\r
5194       savedHwnd = hwnd;\r
5195       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5196       EngineOptionsPopup(hwnd, &second);\r
5197       break;\r
5198 \r
5199     case IDM_OptionsUCI:\r
5200       UciOptionsPopup(hwnd);\r
5201       break;\r
5202 \r
5203     case IDM_Tourney:\r
5204       TourneyPopup(hwnd);\r
5205       break;\r
5206 \r
5207     case IDM_IcsOptions:\r
5208       IcsOptionsPopup(hwnd);\r
5209       break;\r
5210 \r
5211     case IDM_Fonts:\r
5212       FontsOptionsPopup(hwnd);\r
5213       break;\r
5214 \r
5215     case IDM_Sounds:\r
5216       SoundOptionsPopup(hwnd);\r
5217       break;\r
5218 \r
5219     case IDM_CommPort:\r
5220       CommPortOptionsPopup(hwnd);\r
5221       break;\r
5222 \r
5223     case IDM_LoadOptions:\r
5224       LoadOptionsPopup(hwnd);\r
5225       break;\r
5226 \r
5227     case IDM_SaveOptions:\r
5228       SaveOptionsPopup(hwnd);\r
5229       break;\r
5230 \r
5231     case IDM_TimeControl:\r
5232       TimeControlOptionsPopup(hwnd);\r
5233       break;\r
5234 \r
5235     case IDM_SaveSettings:\r
5236       SaveSettings(settingsFileName);\r
5237       break;\r
5238 \r
5239     case IDM_SaveSettingsOnExit:\r
5240       saveSettingsOnExit = !saveSettingsOnExit;\r
5241       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5242                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5243                                          MF_CHECKED : MF_UNCHECKED));\r
5244       break;\r
5245 \r
5246     case IDM_Hint:\r
5247       HintEvent();\r
5248       break;\r
5249 \r
5250     case IDM_Book:\r
5251       BookEvent();\r
5252       break;\r
5253 \r
5254     case IDM_AboutGame:\r
5255       AboutGameEvent();\r
5256       break;\r
5257 \r
5258     case IDM_Debug:\r
5259       appData.debugMode = !appData.debugMode;\r
5260       if (appData.debugMode) {\r
5261         char dir[MSG_SIZ];\r
5262         GetCurrentDirectory(MSG_SIZ, dir);\r
5263         SetCurrentDirectory(installDir);\r
5264         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5265         SetCurrentDirectory(dir);\r
5266         setbuf(debugFP, NULL);\r
5267       } else {\r
5268         fclose(debugFP);\r
5269         debugFP = NULL;\r
5270       }\r
5271       break;\r
5272 \r
5273     case IDM_HELPCONTENTS:\r
5274       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5275           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5276           MessageBox (GetFocus(),\r
5277                     _("Unable to activate help"),\r
5278                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5279       }\r
5280       break;\r
5281 \r
5282     case IDM_HELPSEARCH:\r
5283         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5284             !HtmlHelp(hwnd, "winboard.chm", 0, 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_HELPHELP:\r
5292       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5293         MessageBox (GetFocus(),\r
5294                     _("Unable to activate help"),\r
5295                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5296       }\r
5297       break;\r
5298 \r
5299     case IDM_ABOUT:\r
5300       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5301       DialogBox(hInst, \r
5302         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5303         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5304       FreeProcInstance(lpProc);\r
5305       break;\r
5306 \r
5307     case IDM_DirectCommand1:\r
5308       AskQuestionEvent(_("Direct Command"),\r
5309                        _("Send to chess program:"), "", "1");\r
5310       break;\r
5311     case IDM_DirectCommand2:\r
5312       AskQuestionEvent(_("Direct Command"),\r
5313                        _("Send to second chess program:"), "", "2");\r
5314       break;\r
5315 \r
5316     case EP_WhitePawn:\r
5317       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5318       fromX = fromY = -1;\r
5319       break;\r
5320 \r
5321     case EP_WhiteKnight:\r
5322       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5323       fromX = fromY = -1;\r
5324       break;\r
5325 \r
5326     case EP_WhiteBishop:\r
5327       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5328       fromX = fromY = -1;\r
5329       break;\r
5330 \r
5331     case EP_WhiteRook:\r
5332       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5333       fromX = fromY = -1;\r
5334       break;\r
5335 \r
5336     case EP_WhiteQueen:\r
5337       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5338       fromX = fromY = -1;\r
5339       break;\r
5340 \r
5341     case EP_WhiteFerz:\r
5342       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5343       fromX = fromY = -1;\r
5344       break;\r
5345 \r
5346     case EP_WhiteWazir:\r
5347       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5348       fromX = fromY = -1;\r
5349       break;\r
5350 \r
5351     case EP_WhiteAlfil:\r
5352       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5353       fromX = fromY = -1;\r
5354       break;\r
5355 \r
5356     case EP_WhiteCannon:\r
5357       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5358       fromX = fromY = -1;\r
5359       break;\r
5360 \r
5361     case EP_WhiteCardinal:\r
5362       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5363       fromX = fromY = -1;\r
5364       break;\r
5365 \r
5366     case EP_WhiteMarshall:\r
5367       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5368       fromX = fromY = -1;\r
5369       break;\r
5370 \r
5371     case EP_WhiteKing:\r
5372       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5373       fromX = fromY = -1;\r
5374       break;\r
5375 \r
5376     case EP_BlackPawn:\r
5377       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5378       fromX = fromY = -1;\r
5379       break;\r
5380 \r
5381     case EP_BlackKnight:\r
5382       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5383       fromX = fromY = -1;\r
5384       break;\r
5385 \r
5386     case EP_BlackBishop:\r
5387       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5388       fromX = fromY = -1;\r
5389       break;\r
5390 \r
5391     case EP_BlackRook:\r
5392       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5393       fromX = fromY = -1;\r
5394       break;\r
5395 \r
5396     case EP_BlackQueen:\r
5397       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5398       fromX = fromY = -1;\r
5399       break;\r
5400 \r
5401     case EP_BlackFerz:\r
5402       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5403       fromX = fromY = -1;\r
5404       break;\r
5405 \r
5406     case EP_BlackWazir:\r
5407       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5408       fromX = fromY = -1;\r
5409       break;\r
5410 \r
5411     case EP_BlackAlfil:\r
5412       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5413       fromX = fromY = -1;\r
5414       break;\r
5415 \r
5416     case EP_BlackCannon:\r
5417       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5418       fromX = fromY = -1;\r
5419       break;\r
5420 \r
5421     case EP_BlackCardinal:\r
5422       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5423       fromX = fromY = -1;\r
5424       break;\r
5425 \r
5426     case EP_BlackMarshall:\r
5427       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5428       fromX = fromY = -1;\r
5429       break;\r
5430 \r
5431     case EP_BlackKing:\r
5432       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5433       fromX = fromY = -1;\r
5434       break;\r
5435 \r
5436     case EP_EmptySquare:\r
5437       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5438       fromX = fromY = -1;\r
5439       break;\r
5440 \r
5441     case EP_ClearBoard:\r
5442       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5443       fromX = fromY = -1;\r
5444       break;\r
5445 \r
5446     case EP_White:\r
5447       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5448       fromX = fromY = -1;\r
5449       break;\r
5450 \r
5451     case EP_Black:\r
5452       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5453       fromX = fromY = -1;\r
5454       break;\r
5455 \r
5456     case EP_Promote:\r
5457       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5458       fromX = fromY = -1;\r
5459       break;\r
5460 \r
5461     case EP_Demote:\r
5462       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5463       fromX = fromY = -1;\r
5464       break;\r
5465 \r
5466     case DP_Pawn:\r
5467       DropMenuEvent(WhitePawn, fromX, fromY);\r
5468       fromX = fromY = -1;\r
5469       break;\r
5470 \r
5471     case DP_Knight:\r
5472       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5473       fromX = fromY = -1;\r
5474       break;\r
5475 \r
5476     case DP_Bishop:\r
5477       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5478       fromX = fromY = -1;\r
5479       break;\r
5480 \r
5481     case DP_Rook:\r
5482       DropMenuEvent(WhiteRook, fromX, fromY);\r
5483       fromX = fromY = -1;\r
5484       break;\r
5485 \r
5486     case DP_Queen:\r
5487       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5488       fromX = fromY = -1;\r
5489       break;\r
5490 \r
5491     case IDM_English:\r
5492       barbaric = 0; appData.language = "";\r
5493       TranslateMenus(0);\r
5494       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5495       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5496       lastChecked = wmId;\r
5497       break;\r
5498 \r
5499     default:\r
5500       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5501           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5502       else\r
5503       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5504           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5505           TranslateMenus(0);\r
5506           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5507           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5508           lastChecked = wmId;\r
5509           break;\r
5510       }\r
5511       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5512     }\r
5513     break;\r
5514 \r
5515   case WM_TIMER:\r
5516     switch (wParam) {\r
5517     case CLOCK_TIMER_ID:\r
5518       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5519       clockTimerEvent = 0;\r
5520       DecrementClocks(); /* call into back end */\r
5521       break;\r
5522     case LOAD_GAME_TIMER_ID:\r
5523       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5524       loadGameTimerEvent = 0;\r
5525       AutoPlayGameLoop(); /* call into back end */\r
5526       break;\r
5527     case ANALYSIS_TIMER_ID:\r
5528       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5529                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5530         AnalysisPeriodicEvent(0);\r
5531       } else {\r
5532         KillTimer(hwnd, analysisTimerEvent);\r
5533         analysisTimerEvent = 0;\r
5534       }\r
5535       break;\r
5536     case DELAYED_TIMER_ID:\r
5537       KillTimer(hwnd, delayedTimerEvent);\r
5538       delayedTimerEvent = 0;\r
5539       delayedTimerCallback();\r
5540       break;\r
5541     }\r
5542     break;\r
5543 \r
5544   case WM_USER_Input:\r
5545     InputEvent(hwnd, message, wParam, lParam);\r
5546     break;\r
5547 \r
5548   /* [AS] Also move "attached" child windows */\r
5549   case WM_WINDOWPOSCHANGING:\r
5550 \r
5551     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5552         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5553 \r
5554         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5555             /* Window is moving */\r
5556             RECT rcMain;\r
5557 \r
5558 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5559             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5560             rcMain.right  = wpMain.x + wpMain.width;\r
5561             rcMain.top    = wpMain.y;\r
5562             rcMain.bottom = wpMain.y + wpMain.height;\r
5563             \r
5564             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5565             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5566             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5567             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5568             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5569             wpMain.x = lpwp->x;\r
5570             wpMain.y = lpwp->y;\r
5571         }\r
5572     }\r
5573     break;\r
5574 \r
5575   /* [AS] Snapping */\r
5576   case WM_ENTERSIZEMOVE:\r
5577     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5578     if (hwnd == hwndMain) {\r
5579       doingSizing = TRUE;\r
5580       lastSizing = 0;\r
5581     }\r
5582     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5583     break;\r
5584 \r
5585   case WM_SIZING:\r
5586     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5587     if (hwnd == hwndMain) {\r
5588       lastSizing = wParam;\r
5589     }\r
5590     break;\r
5591 \r
5592   case WM_MOVING:\r
5593     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5594       return OnMoving( &sd, hwnd, wParam, lParam );\r
5595 \r
5596   case WM_EXITSIZEMOVE:\r
5597     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5598     if (hwnd == hwndMain) {\r
5599       RECT client;\r
5600       doingSizing = FALSE;\r
5601       InvalidateRect(hwnd, &boardRect, FALSE);\r
5602       GetClientRect(hwnd, &client);\r
5603       ResizeBoard(client.right, client.bottom, lastSizing);\r
5604       lastSizing = 0;\r
5605       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5606     }\r
5607     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5608     break;\r
5609 \r
5610   case WM_DESTROY: /* message: window being destroyed */\r
5611     PostQuitMessage(0);\r
5612     break;\r
5613 \r
5614   case WM_CLOSE:\r
5615     if (hwnd == hwndMain) {\r
5616       ExitEvent(0);\r
5617     }\r
5618     break;\r
5619 \r
5620   default:      /* Passes it on if unprocessed */\r
5621     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5622   }\r
5623   return 0;\r
5624 }\r
5625 \r
5626 /*---------------------------------------------------------------------------*\\r
5627  *\r
5628  * Misc utility routines\r
5629  *\r
5630 \*---------------------------------------------------------------------------*/\r
5631 \r
5632 /*\r
5633  * Decent random number generator, at least not as bad as Windows\r
5634  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5635  */\r
5636 unsigned int randstate;\r
5637 \r
5638 int\r
5639 myrandom(void)\r
5640 {\r
5641   randstate = randstate * 1664525 + 1013904223;\r
5642   return (int) randstate & 0x7fffffff;\r
5643 }\r
5644 \r
5645 void\r
5646 mysrandom(unsigned int seed)\r
5647 {\r
5648   randstate = seed;\r
5649 }\r
5650 \r
5651 \r
5652 /* \r
5653  * returns TRUE if user selects a different color, FALSE otherwise \r
5654  */\r
5655 \r
5656 BOOL\r
5657 ChangeColor(HWND hwnd, COLORREF *which)\r
5658 {\r
5659   static BOOL firstTime = TRUE;\r
5660   static DWORD customColors[16];\r
5661   CHOOSECOLOR cc;\r
5662   COLORREF newcolor;\r
5663   int i;\r
5664   ColorClass ccl;\r
5665 \r
5666   if (firstTime) {\r
5667     /* Make initial colors in use available as custom colors */\r
5668     /* Should we put the compiled-in defaults here instead? */\r
5669     i = 0;\r
5670     customColors[i++] = lightSquareColor & 0xffffff;\r
5671     customColors[i++] = darkSquareColor & 0xffffff;\r
5672     customColors[i++] = whitePieceColor & 0xffffff;\r
5673     customColors[i++] = blackPieceColor & 0xffffff;\r
5674     customColors[i++] = highlightSquareColor & 0xffffff;\r
5675     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5676 \r
5677     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5678       customColors[i++] = textAttribs[ccl].color;\r
5679     }\r
5680     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5681     firstTime = FALSE;\r
5682   }\r
5683 \r
5684   cc.lStructSize = sizeof(cc);\r
5685   cc.hwndOwner = hwnd;\r
5686   cc.hInstance = NULL;\r
5687   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5688   cc.lpCustColors = (LPDWORD) customColors;\r
5689   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5690 \r
5691   if (!ChooseColor(&cc)) return FALSE;\r
5692 \r
5693   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5694   if (newcolor == *which) return FALSE;\r
5695   *which = newcolor;\r
5696   return TRUE;\r
5697 \r
5698   /*\r
5699   InitDrawingColors();\r
5700   InvalidateRect(hwnd, &boardRect, FALSE);\r
5701   */\r
5702 }\r
5703 \r
5704 BOOLEAN\r
5705 MyLoadSound(MySound *ms)\r
5706 {\r
5707   BOOL ok = FALSE;\r
5708   struct stat st;\r
5709   FILE *f;\r
5710 \r
5711   if (ms->data && ms->flag) free(ms->data);\r
5712   ms->data = NULL;\r
5713 \r
5714   switch (ms->name[0]) {\r
5715   case NULLCHAR:\r
5716     /* Silence */\r
5717     ok = TRUE;\r
5718     break;\r
5719   case '$':\r
5720     /* System sound from Control Panel.  Don't preload here. */\r
5721     ok = TRUE;\r
5722     break;\r
5723   case '!':\r
5724     if (ms->name[1] == NULLCHAR) {\r
5725       /* "!" alone = silence */\r
5726       ok = TRUE;\r
5727     } else {\r
5728       /* Builtin wave resource.  Error if not found. */\r
5729       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5730       if (h == NULL) break;\r
5731       ms->data = (void *)LoadResource(hInst, h);\r
5732       ms->flag = 0; // not maloced, so cannot be freed!\r
5733       if (h == NULL) break;\r
5734       ok = TRUE;\r
5735     }\r
5736     break;\r
5737   default:\r
5738     /* .wav file.  Error if not found. */\r
5739     f = fopen(ms->name, "rb");\r
5740     if (f == NULL) break;\r
5741     if (fstat(fileno(f), &st) < 0) break;\r
5742     ms->data = malloc(st.st_size);\r
5743     ms->flag = 1;\r
5744     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5745     fclose(f);\r
5746     ok = TRUE;\r
5747     break;\r
5748   }\r
5749   if (!ok) {\r
5750     char buf[MSG_SIZ];\r
5751       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5752     DisplayError(buf, GetLastError());\r
5753   }\r
5754   return ok;\r
5755 }\r
5756 \r
5757 BOOLEAN\r
5758 MyPlaySound(MySound *ms)\r
5759 {\r
5760   BOOLEAN ok = FALSE;\r
5761 \r
5762   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5763   switch (ms->name[0]) {\r
5764   case NULLCHAR:\r
5765         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5766     /* Silence */\r
5767     ok = TRUE;\r
5768     break;\r
5769   case '$':\r
5770     /* System sound from Control Panel (deprecated feature).\r
5771        "$" alone or an unset sound name gets default beep (still in use). */\r
5772     if (ms->name[1]) {\r
5773       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5774     }\r
5775     if (!ok) ok = MessageBeep(MB_OK);\r
5776     break; \r
5777   case '!':\r
5778     /* Builtin wave resource, or "!" alone for silence */\r
5779     if (ms->name[1]) {\r
5780       if (ms->data == NULL) return FALSE;\r
5781       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5782     } else {\r
5783       ok = TRUE;\r
5784     }\r
5785     break;\r
5786   default:\r
5787     /* .wav file.  Error if not found. */\r
5788     if (ms->data == NULL) return FALSE;\r
5789     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5790     break;\r
5791   }\r
5792   /* Don't print an error: this can happen innocently if the sound driver\r
5793      is busy; for instance, if another instance of WinBoard is playing\r
5794      a sound at about the same time. */\r
5795   return ok;\r
5796 }\r
5797 \r
5798 \r
5799 LRESULT CALLBACK\r
5800 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5801 {\r
5802   BOOL ok;\r
5803   OPENFILENAME *ofn;\r
5804   static UINT *number; /* gross that this is static */\r
5805 \r
5806   switch (message) {\r
5807   case WM_INITDIALOG: /* message: initialize dialog box */\r
5808     /* Center the dialog over the application window */\r
5809     ofn = (OPENFILENAME *) lParam;\r
5810     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5811       number = (UINT *) ofn->lCustData;\r
5812       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5813     } else {\r
5814       number = NULL;\r
5815     }\r
5816     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5817     Translate(hDlg, 1536);\r
5818     return FALSE;  /* Allow for further processing */\r
5819 \r
5820   case WM_COMMAND:\r
5821     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5822       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5823     }\r
5824     return FALSE;  /* Allow for further processing */\r
5825   }\r
5826   return FALSE;\r
5827 }\r
5828 \r
5829 UINT APIENTRY\r
5830 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5831 {\r
5832   static UINT *number;\r
5833   OPENFILENAME *ofname;\r
5834   OFNOTIFY *ofnot;\r
5835   switch (uiMsg) {\r
5836   case WM_INITDIALOG:\r
5837     Translate(hdlg, DLG_IndexNumber);\r
5838     ofname = (OPENFILENAME *)lParam;\r
5839     number = (UINT *)(ofname->lCustData);\r
5840     break;\r
5841   case WM_NOTIFY:\r
5842     ofnot = (OFNOTIFY *)lParam;\r
5843     if (ofnot->hdr.code == CDN_FILEOK) {\r
5844       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5845     }\r
5846     break;\r
5847   }\r
5848   return 0;\r
5849 }\r
5850 \r
5851 \r
5852 FILE *\r
5853 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5854                char *nameFilt, char *dlgTitle, UINT *number,\r
5855                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5856 {\r
5857   OPENFILENAME openFileName;\r
5858   char buf1[MSG_SIZ];\r
5859   FILE *f;\r
5860 \r
5861   if (fileName == NULL) fileName = buf1;\r
5862   if (defName == NULL) {\r
5863     safeStrCpy(fileName, "*.", 3 );\r
5864     strcat(fileName, defExt);\r
5865   } else {\r
5866     safeStrCpy(fileName, defName, MSG_SIZ );\r
5867   }\r
5868     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5869   if (number) *number = 0;\r
5870 \r
5871   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5872   openFileName.hwndOwner         = hwnd;\r
5873   openFileName.hInstance         = (HANDLE) hInst;\r
5874   openFileName.lpstrFilter       = nameFilt;\r
5875   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5876   openFileName.nMaxCustFilter    = 0L;\r
5877   openFileName.nFilterIndex      = 1L;\r
5878   openFileName.lpstrFile         = fileName;\r
5879   openFileName.nMaxFile          = MSG_SIZ;\r
5880   openFileName.lpstrFileTitle    = fileTitle;\r
5881   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5882   openFileName.lpstrInitialDir   = NULL;\r
5883   openFileName.lpstrTitle        = dlgTitle;\r
5884   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5885     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5886     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5887     | (oldDialog ? 0 : OFN_EXPLORER);\r
5888   openFileName.nFileOffset       = 0;\r
5889   openFileName.nFileExtension    = 0;\r
5890   openFileName.lpstrDefExt       = defExt;\r
5891   openFileName.lCustData         = (LONG) number;\r
5892   openFileName.lpfnHook          = oldDialog ?\r
5893     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5894   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5895 \r
5896   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5897                         GetOpenFileName(&openFileName)) {\r
5898     /* open the file */\r
5899     f = fopen(openFileName.lpstrFile, write);\r
5900     if (f == NULL) {\r
5901       MessageBox(hwnd, _("File open failed"), NULL,\r
5902                  MB_OK|MB_ICONEXCLAMATION);\r
5903       return NULL;\r
5904     }\r
5905   } else {\r
5906     int err = CommDlgExtendedError();\r
5907     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5908     return FALSE;\r
5909   }\r
5910   return f;\r
5911 }\r
5912 \r
5913 \r
5914 \r
5915 VOID APIENTRY\r
5916 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5917 {\r
5918   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5919 \r
5920   /*\r
5921    * Get the first pop-up menu in the menu template. This is the\r
5922    * menu that TrackPopupMenu displays.\r
5923    */\r
5924   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5925   TranslateOneMenu(10, hmenuTrackPopup);\r
5926 \r
5927   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5928 \r
5929   /*\r
5930    * TrackPopup uses screen coordinates, so convert the\r
5931    * coordinates of the mouse click to screen coordinates.\r
5932    */\r
5933   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5934 \r
5935   /* Draw and track the floating pop-up menu. */\r
5936   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5937                  pt.x, pt.y, 0, hwnd, NULL);\r
5938 \r
5939   /* Destroy the menu.*/\r
5940   DestroyMenu(hmenu);\r
5941 }\r
5942    \r
5943 typedef struct {\r
5944   HWND hDlg, hText;\r
5945   int sizeX, sizeY, newSizeX, newSizeY;\r
5946   HDWP hdwp;\r
5947 } ResizeEditPlusButtonsClosure;\r
5948 \r
5949 BOOL CALLBACK\r
5950 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5951 {\r
5952   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5953   RECT rect;\r
5954   POINT pt;\r
5955 \r
5956   if (hChild == cl->hText) return TRUE;\r
5957   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5958   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5959   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5960   ScreenToClient(cl->hDlg, &pt);\r
5961   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5962     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5963   return TRUE;\r
5964 }\r
5965 \r
5966 /* Resize a dialog that has a (rich) edit field filling most of\r
5967    the top, with a row of buttons below */\r
5968 VOID\r
5969 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5970 {\r
5971   RECT rectText;\r
5972   int newTextHeight, newTextWidth;\r
5973   ResizeEditPlusButtonsClosure cl;\r
5974   \r
5975   /*if (IsIconic(hDlg)) return;*/\r
5976   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5977   \r
5978   cl.hdwp = BeginDeferWindowPos(8);\r
5979 \r
5980   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5981   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5982   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5983   if (newTextHeight < 0) {\r
5984     newSizeY += -newTextHeight;\r
5985     newTextHeight = 0;\r
5986   }\r
5987   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5988     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5989 \r
5990   cl.hDlg = hDlg;\r
5991   cl.hText = hText;\r
5992   cl.sizeX = sizeX;\r
5993   cl.sizeY = sizeY;\r
5994   cl.newSizeX = newSizeX;\r
5995   cl.newSizeY = newSizeY;\r
5996   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5997 \r
5998   EndDeferWindowPos(cl.hdwp);\r
5999 }\r
6000 \r
6001 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6002 {\r
6003     RECT    rChild, rParent;\r
6004     int     wChild, hChild, wParent, hParent;\r
6005     int     wScreen, hScreen, xNew, yNew;\r
6006     HDC     hdc;\r
6007 \r
6008     /* Get the Height and Width of the child window */\r
6009     GetWindowRect (hwndChild, &rChild);\r
6010     wChild = rChild.right - rChild.left;\r
6011     hChild = rChild.bottom - rChild.top;\r
6012 \r
6013     /* Get the Height and Width of the parent window */\r
6014     GetWindowRect (hwndParent, &rParent);\r
6015     wParent = rParent.right - rParent.left;\r
6016     hParent = rParent.bottom - rParent.top;\r
6017 \r
6018     /* Get the display limits */\r
6019     hdc = GetDC (hwndChild);\r
6020     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6021     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6022     ReleaseDC(hwndChild, hdc);\r
6023 \r
6024     /* Calculate new X position, then adjust for screen */\r
6025     xNew = rParent.left + ((wParent - wChild) /2);\r
6026     if (xNew < 0) {\r
6027         xNew = 0;\r
6028     } else if ((xNew+wChild) > wScreen) {\r
6029         xNew = wScreen - wChild;\r
6030     }\r
6031 \r
6032     /* Calculate new Y position, then adjust for screen */\r
6033     if( mode == 0 ) {\r
6034         yNew = rParent.top  + ((hParent - hChild) /2);\r
6035     }\r
6036     else {\r
6037         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6038     }\r
6039 \r
6040     if (yNew < 0) {\r
6041         yNew = 0;\r
6042     } else if ((yNew+hChild) > hScreen) {\r
6043         yNew = hScreen - hChild;\r
6044     }\r
6045 \r
6046     /* Set it, and return */\r
6047     return SetWindowPos (hwndChild, NULL,\r
6048                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6049 }\r
6050 \r
6051 /* Center one window over another */\r
6052 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6053 {\r
6054     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6055 }\r
6056 \r
6057 /*---------------------------------------------------------------------------*\\r
6058  *\r
6059  * Startup Dialog functions\r
6060  *\r
6061 \*---------------------------------------------------------------------------*/\r
6062 void\r
6063 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6064 {\r
6065   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6066 \r
6067   while (*cd != NULL) {\r
6068     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6069     cd++;\r
6070   }\r
6071 }\r
6072 \r
6073 void\r
6074 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6075 {\r
6076   char buf1[MAX_ARG_LEN];\r
6077   int len;\r
6078 \r
6079   if (str[0] == '@') {\r
6080     FILE* f = fopen(str + 1, "r");\r
6081     if (f == NULL) {\r
6082       DisplayFatalError(str + 1, errno, 2);\r
6083       return;\r
6084     }\r
6085     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6086     fclose(f);\r
6087     buf1[len] = NULLCHAR;\r
6088     str = buf1;\r
6089   }\r
6090 \r
6091   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6092 \r
6093   for (;;) {\r
6094     char buf[MSG_SIZ];\r
6095     char *end = strchr(str, '\n');\r
6096     if (end == NULL) return;\r
6097     memcpy(buf, str, end - str);\r
6098     buf[end - str] = NULLCHAR;\r
6099     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6100     str = end + 1;\r
6101   }\r
6102 }\r
6103 \r
6104 void\r
6105 SetStartupDialogEnables(HWND hDlg)\r
6106 {\r
6107   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6108     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6109     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6110   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6111     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6112   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6113     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6114   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6115     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6116   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6117     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6118     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6119     IsDlgButtonChecked(hDlg, OPT_View));\r
6120 }\r
6121 \r
6122 char *\r
6123 QuoteForFilename(char *filename)\r
6124 {\r
6125   int dquote, space;\r
6126   dquote = strchr(filename, '"') != NULL;\r
6127   space = strchr(filename, ' ') != NULL;\r
6128   if (dquote || space) {\r
6129     if (dquote) {\r
6130       return "'";\r
6131     } else {\r
6132       return "\"";\r
6133     }\r
6134   } else {\r
6135     return "";\r
6136   }\r
6137 }\r
6138 \r
6139 VOID\r
6140 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6141 {\r
6142   char buf[MSG_SIZ];\r
6143   char *q;\r
6144 \r
6145   InitComboStringsFromOption(hwndCombo, nthnames);\r
6146   q = QuoteForFilename(nthcp);\r
6147     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6148   if (*nthdir != NULLCHAR) {\r
6149     q = QuoteForFilename(nthdir);\r
6150       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6151   }\r
6152   if (*nthcp == NULLCHAR) {\r
6153     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6154   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6155     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6156     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6157   }\r
6158 }\r
6159 \r
6160 LRESULT CALLBACK\r
6161 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6162 {\r
6163   char buf[MSG_SIZ];\r
6164   HANDLE hwndCombo;\r
6165   char *p;\r
6166 \r
6167   switch (message) {\r
6168   case WM_INITDIALOG:\r
6169     /* Center the dialog */\r
6170     CenterWindow (hDlg, GetDesktopWindow());\r
6171     Translate(hDlg, DLG_Startup);\r
6172     /* Initialize the dialog items */\r
6173     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6174                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6175                   firstChessProgramNames);\r
6176     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6177                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6178                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6179     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6180     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6181       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6182     if (*appData.icsHelper != NULLCHAR) {\r
6183       char *q = QuoteForFilename(appData.icsHelper);\r
6184       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6185     }\r
6186     if (*appData.icsHost == NULLCHAR) {\r
6187       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6188       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6189     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6190       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6191       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6192     }\r
6193 \r
6194     if (appData.icsActive) {\r
6195       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6196     }\r
6197     else if (appData.noChessProgram) {\r
6198       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6199     }\r
6200     else {\r
6201       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6202     }\r
6203 \r
6204     SetStartupDialogEnables(hDlg);\r
6205     return TRUE;\r
6206 \r
6207   case WM_COMMAND:\r
6208     switch (LOWORD(wParam)) {\r
6209     case IDOK:\r
6210       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6211         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6212         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6213         p = buf;\r
6214         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6215         ParseArgs(StringGet, &p);\r
6216         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6217         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6218         p = buf;\r
6219         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6220         ParseArgs(StringGet, &p);\r
6221         SwapEngines(singleList); // ... and then make it 'second'\r
6222         appData.noChessProgram = FALSE;\r
6223         appData.icsActive = FALSE;\r
6224       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6225         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6226         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6227         p = buf;\r
6228         ParseArgs(StringGet, &p);\r
6229         if (appData.zippyPlay) {\r
6230           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6231           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6232           p = buf;\r
6233           ParseArgs(StringGet, &p);\r
6234         }\r
6235       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6236         appData.noChessProgram = TRUE;\r
6237         appData.icsActive = FALSE;\r
6238       } else {\r
6239         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6240                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6241         return TRUE;\r
6242       }\r
6243       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6244         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6245         p = buf;\r
6246         ParseArgs(StringGet, &p);\r
6247       }\r
6248       EndDialog(hDlg, TRUE);\r
6249       return TRUE;\r
6250 \r
6251     case IDCANCEL:\r
6252       ExitEvent(0);\r
6253       return TRUE;\r
6254 \r
6255     case IDM_HELPCONTENTS:\r
6256       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6257         MessageBox (GetFocus(),\r
6258                     _("Unable to activate help"),\r
6259                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6260       }\r
6261       break;\r
6262 \r
6263     default:\r
6264       SetStartupDialogEnables(hDlg);\r
6265       break;\r
6266     }\r
6267     break;\r
6268   }\r
6269   return FALSE;\r
6270 }\r
6271 \r
6272 /*---------------------------------------------------------------------------*\\r
6273  *\r
6274  * About box dialog functions\r
6275  *\r
6276 \*---------------------------------------------------------------------------*/\r
6277 \r
6278 /* Process messages for "About" dialog box */\r
6279 LRESULT CALLBACK\r
6280 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6281 {\r
6282   switch (message) {\r
6283   case WM_INITDIALOG: /* message: initialize dialog box */\r
6284     /* Center the dialog over the application window */\r
6285     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6286     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6287     Translate(hDlg, ABOUTBOX);\r
6288     JAWS_COPYRIGHT\r
6289     return (TRUE);\r
6290 \r
6291   case WM_COMMAND: /* message: received a command */\r
6292     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6293         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6294       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6295       return (TRUE);\r
6296     }\r
6297     break;\r
6298   }\r
6299   return (FALSE);\r
6300 }\r
6301 \r
6302 /*---------------------------------------------------------------------------*\\r
6303  *\r
6304  * Comment Dialog functions\r
6305  *\r
6306 \*---------------------------------------------------------------------------*/\r
6307 \r
6308 LRESULT CALLBACK\r
6309 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6310 {\r
6311   static HANDLE hwndText = NULL;\r
6312   int len, newSizeX, newSizeY, flags;\r
6313   static int sizeX, sizeY;\r
6314   char *str;\r
6315   RECT rect;\r
6316   MINMAXINFO *mmi;\r
6317 \r
6318   switch (message) {\r
6319   case WM_INITDIALOG: /* message: initialize dialog box */\r
6320     /* Initialize the dialog items */\r
6321     Translate(hDlg, DLG_EditComment);\r
6322     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6323     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6324     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6325     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6326     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6327     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6328     SetWindowText(hDlg, commentTitle);\r
6329     if (editComment) {\r
6330       SetFocus(hwndText);\r
6331     } else {\r
6332       SetFocus(GetDlgItem(hDlg, IDOK));\r
6333     }\r
6334     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6335                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6336                 MAKELPARAM(FALSE, 0));\r
6337     /* Size and position the dialog */\r
6338     if (!commentDialog) {\r
6339       commentDialog = hDlg;\r
6340       flags = SWP_NOZORDER;\r
6341       GetClientRect(hDlg, &rect);\r
6342       sizeX = rect.right;\r
6343       sizeY = rect.bottom;\r
6344       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6345           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6346         WINDOWPLACEMENT wp;\r
6347         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6348         wp.length = sizeof(WINDOWPLACEMENT);\r
6349         wp.flags = 0;\r
6350         wp.showCmd = SW_SHOW;\r
6351         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6352         wp.rcNormalPosition.left = wpComment.x;\r
6353         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6354         wp.rcNormalPosition.top = wpComment.y;\r
6355         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6356         SetWindowPlacement(hDlg, &wp);\r
6357 \r
6358         GetClientRect(hDlg, &rect);\r
6359         newSizeX = rect.right;\r
6360         newSizeY = rect.bottom;\r
6361         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6362                               newSizeX, newSizeY);\r
6363         sizeX = newSizeX;\r
6364         sizeY = newSizeY;\r
6365       }\r
6366     }\r
6367     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6368     return FALSE;\r
6369 \r
6370   case WM_COMMAND: /* message: received a command */\r
6371     switch (LOWORD(wParam)) {\r
6372     case IDOK:\r
6373       if (editComment) {\r
6374         char *p, *q;\r
6375         /* Read changed options from the dialog box */\r
6376         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6377         len = GetWindowTextLength(hwndText);\r
6378         str = (char *) malloc(len + 1);\r
6379         GetWindowText(hwndText, str, len + 1);\r
6380         p = q = str;\r
6381         while (*q) {\r
6382           if (*q == '\r')\r
6383             q++;\r
6384           else\r
6385             *p++ = *q++;\r
6386         }\r
6387         *p = NULLCHAR;\r
6388         ReplaceComment(commentIndex, str);\r
6389         free(str);\r
6390       }\r
6391       CommentPopDown();\r
6392       return TRUE;\r
6393 \r
6394     case IDCANCEL:\r
6395     case OPT_CancelComment:\r
6396       CommentPopDown();\r
6397       return TRUE;\r
6398 \r
6399     case OPT_ClearComment:\r
6400       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6401       break;\r
6402 \r
6403     case OPT_EditComment:\r
6404       EditCommentEvent();\r
6405       return TRUE;\r
6406 \r
6407     default:\r
6408       break;\r
6409     }\r
6410     break;\r
6411 \r
6412   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6413         if( wParam == OPT_CommentText ) {\r
6414             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6415 \r
6416             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6417                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6418                 POINTL pt;\r
6419                 LRESULT index;\r
6420 \r
6421                 pt.x = LOWORD( lpMF->lParam );\r
6422                 pt.y = HIWORD( lpMF->lParam );\r
6423 \r
6424                 if(lpMF->msg == WM_CHAR) {\r
6425                         CHARRANGE sel;\r
6426                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6427                         index = sel.cpMin;\r
6428                 } else\r
6429                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6430 \r
6431                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6432                 len = GetWindowTextLength(hwndText);\r
6433                 str = (char *) malloc(len + 1);\r
6434                 GetWindowText(hwndText, str, len + 1);\r
6435                 ReplaceComment(commentIndex, str);\r
6436                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6437                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6438                 free(str);\r
6439 \r
6440                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6441                 lpMF->msg = WM_USER;\r
6442 \r
6443                 return TRUE;\r
6444             }\r
6445         }\r
6446         break;\r
6447 \r
6448   case WM_SIZE:\r
6449     newSizeX = LOWORD(lParam);\r
6450     newSizeY = HIWORD(lParam);\r
6451     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6452     sizeX = newSizeX;\r
6453     sizeY = newSizeY;\r
6454     break;\r
6455 \r
6456   case WM_GETMINMAXINFO:\r
6457     /* Prevent resizing window too small */\r
6458     mmi = (MINMAXINFO *) lParam;\r
6459     mmi->ptMinTrackSize.x = 100;\r
6460     mmi->ptMinTrackSize.y = 100;\r
6461     break;\r
6462   }\r
6463   return FALSE;\r
6464 }\r
6465 \r
6466 VOID\r
6467 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6468 {\r
6469   FARPROC lpProc;\r
6470   char *p, *q;\r
6471 \r
6472   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6473 \r
6474   if (str == NULL) str = "";\r
6475   p = (char *) malloc(2 * strlen(str) + 2);\r
6476   q = p;\r
6477   while (*str) {\r
6478     if (*str == '\n') *q++ = '\r';\r
6479     *q++ = *str++;\r
6480   }\r
6481   *q = NULLCHAR;\r
6482   if (commentText != NULL) free(commentText);\r
6483 \r
6484   commentIndex = index;\r
6485   commentTitle = title;\r
6486   commentText = p;\r
6487   editComment = edit;\r
6488 \r
6489   if (commentDialog) {\r
6490     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6491     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6492   } else {\r
6493     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6494     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6495                  hwndMain, (DLGPROC)lpProc);\r
6496     FreeProcInstance(lpProc);\r
6497   }\r
6498   commentUp = TRUE;\r
6499 }\r
6500 \r
6501 \r
6502 /*---------------------------------------------------------------------------*\\r
6503  *\r
6504  * Type-in move dialog functions\r
6505  * \r
6506 \*---------------------------------------------------------------------------*/\r
6507 \r
6508 LRESULT CALLBACK\r
6509 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6510 {\r
6511   char move[MSG_SIZ];\r
6512   HWND hInput;\r
6513 \r
6514   switch (message) {\r
6515   case WM_INITDIALOG:\r
6516     move[0] = (char) lParam;\r
6517     move[1] = NULLCHAR;\r
6518     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6519     Translate(hDlg, DLG_TypeInMove);\r
6520     hInput = GetDlgItem(hDlg, OPT_Move);\r
6521     SetWindowText(hInput, move);\r
6522     SetFocus(hInput);\r
6523     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6524     return FALSE;\r
6525 \r
6526   case WM_COMMAND:\r
6527     switch (LOWORD(wParam)) {\r
6528     case IDOK:\r
6529 \r
6530       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6531       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6532       TypeInDoneEvent(move);\r
6533       EndDialog(hDlg, TRUE);\r
6534       return TRUE;\r
6535     case IDCANCEL:\r
6536       EndDialog(hDlg, FALSE);\r
6537       return TRUE;\r
6538     default:\r
6539       break;\r
6540     }\r
6541     break;\r
6542   }\r
6543   return FALSE;\r
6544 }\r
6545 \r
6546 VOID\r
6547 PopUpMoveDialog(char firstchar)\r
6548 {\r
6549     FARPROC lpProc;\r
6550 \r
6551       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6552       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6553         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6554       FreeProcInstance(lpProc);\r
6555 }\r
6556 \r
6557 /*---------------------------------------------------------------------------*\\r
6558  *\r
6559  * Type-in name dialog functions\r
6560  * \r
6561 \*---------------------------------------------------------------------------*/\r
6562 \r
6563 LRESULT CALLBACK\r
6564 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6565 {\r
6566   char move[MSG_SIZ];\r
6567   HWND hInput;\r
6568 \r
6569   switch (message) {\r
6570   case WM_INITDIALOG:\r
6571     move[0] = (char) lParam;\r
6572     move[1] = NULLCHAR;\r
6573     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6574     Translate(hDlg, DLG_TypeInName);\r
6575     hInput = GetDlgItem(hDlg, OPT_Name);\r
6576     SetWindowText(hInput, move);\r
6577     SetFocus(hInput);\r
6578     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6579     return FALSE;\r
6580 \r
6581   case WM_COMMAND:\r
6582     switch (LOWORD(wParam)) {\r
6583     case IDOK:\r
6584       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6585       appData.userName = strdup(move);\r
6586       SetUserLogo();\r
6587       SetGameInfo();\r
6588       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6589         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6590         DisplayTitle(move);\r
6591       }\r
6592 \r
6593 \r
6594       EndDialog(hDlg, TRUE);\r
6595       return TRUE;\r
6596     case IDCANCEL:\r
6597       EndDialog(hDlg, FALSE);\r
6598       return TRUE;\r
6599     default:\r
6600       break;\r
6601     }\r
6602     break;\r
6603   }\r
6604   return FALSE;\r
6605 }\r
6606 \r
6607 VOID\r
6608 PopUpNameDialog(char firstchar)\r
6609 {\r
6610     FARPROC lpProc;\r
6611     \r
6612       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6613       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6614         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6615       FreeProcInstance(lpProc);\r
6616 }\r
6617 \r
6618 /*---------------------------------------------------------------------------*\\r
6619  *\r
6620  *  Error dialogs\r
6621  * \r
6622 \*---------------------------------------------------------------------------*/\r
6623 \r
6624 /* Nonmodal error box */\r
6625 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6626                              WPARAM wParam, LPARAM lParam);\r
6627 \r
6628 VOID\r
6629 ErrorPopUp(char *title, char *content)\r
6630 {\r
6631   FARPROC lpProc;\r
6632   char *p, *q;\r
6633   BOOLEAN modal = hwndMain == NULL;\r
6634 \r
6635   p = content;\r
6636   q = errorMessage;\r
6637   while (*p) {\r
6638     if (*p == '\n') {\r
6639       if (modal) {\r
6640         *q++ = ' ';\r
6641         p++;\r
6642       } else {\r
6643         *q++ = '\r';\r
6644         *q++ = *p++;\r
6645       }\r
6646     } else {\r
6647       *q++ = *p++;\r
6648     }\r
6649   }\r
6650   *q = NULLCHAR;\r
6651   strncpy(errorTitle, title, sizeof(errorTitle));\r
6652   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6653   \r
6654   if (modal) {\r
6655     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6656   } else {\r
6657     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6658     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6659                  hwndMain, (DLGPROC)lpProc);\r
6660     FreeProcInstance(lpProc);\r
6661   }\r
6662 }\r
6663 \r
6664 VOID\r
6665 ErrorPopDown()\r
6666 {\r
6667   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6668   if (errorDialog == NULL) return;\r
6669   DestroyWindow(errorDialog);\r
6670   errorDialog = NULL;\r
6671   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6672 }\r
6673 \r
6674 LRESULT CALLBACK\r
6675 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6676 {\r
6677   HANDLE hwndText;\r
6678   RECT rChild;\r
6679 \r
6680   switch (message) {\r
6681   case WM_INITDIALOG:\r
6682     GetWindowRect(hDlg, &rChild);\r
6683 \r
6684     /*\r
6685     SetWindowPos(hDlg, NULL, rChild.left,\r
6686       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6687       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6688     */\r
6689 \r
6690     /* \r
6691         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6692         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6693         and it doesn't work when you resize the dialog.\r
6694         For now, just give it a default position.\r
6695     */\r
6696     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6697     Translate(hDlg, DLG_Error);\r
6698 \r
6699     errorDialog = hDlg;\r
6700     SetWindowText(hDlg, errorTitle);\r
6701     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6702     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6703     return FALSE;\r
6704 \r
6705   case WM_COMMAND:\r
6706     switch (LOWORD(wParam)) {\r
6707     case IDOK:\r
6708     case IDCANCEL:\r
6709       if (errorDialog == hDlg) errorDialog = NULL;\r
6710       DestroyWindow(hDlg);\r
6711       return TRUE;\r
6712 \r
6713     default:\r
6714       break;\r
6715     }\r
6716     break;\r
6717   }\r
6718   return FALSE;\r
6719 }\r
6720 \r
6721 #ifdef GOTHIC\r
6722 HWND gothicDialog = NULL;\r
6723 \r
6724 LRESULT CALLBACK\r
6725 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6726 {\r
6727   HANDLE hwndText;\r
6728   RECT rChild;\r
6729   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6730 \r
6731   switch (message) {\r
6732   case WM_INITDIALOG:\r
6733     GetWindowRect(hDlg, &rChild);\r
6734 \r
6735     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6736                                                              SWP_NOZORDER);\r
6737 \r
6738     /* \r
6739         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6740         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6741         and it doesn't work when you resize the dialog.\r
6742         For now, just give it a default position.\r
6743     */\r
6744     gothicDialog = hDlg;\r
6745     SetWindowText(hDlg, errorTitle);\r
6746     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6747     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6748     return FALSE;\r
6749 \r
6750   case WM_COMMAND:\r
6751     switch (LOWORD(wParam)) {\r
6752     case IDOK:\r
6753     case IDCANCEL:\r
6754       if (errorDialog == hDlg) errorDialog = NULL;\r
6755       DestroyWindow(hDlg);\r
6756       return TRUE;\r
6757 \r
6758     default:\r
6759       break;\r
6760     }\r
6761     break;\r
6762   }\r
6763   return FALSE;\r
6764 }\r
6765 \r
6766 VOID\r
6767 GothicPopUp(char *title, VariantClass variant)\r
6768 {\r
6769   FARPROC lpProc;\r
6770   static char *lastTitle;\r
6771 \r
6772   strncpy(errorTitle, title, sizeof(errorTitle));\r
6773   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6774 \r
6775   if(lastTitle != title && gothicDialog != NULL) {\r
6776     DestroyWindow(gothicDialog);\r
6777     gothicDialog = NULL;\r
6778   }\r
6779   if(variant != VariantNormal && gothicDialog == NULL) {\r
6780     title = lastTitle;\r
6781     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6782     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6783                  hwndMain, (DLGPROC)lpProc);\r
6784     FreeProcInstance(lpProc);\r
6785   }\r
6786 }\r
6787 #endif\r
6788 \r
6789 /*---------------------------------------------------------------------------*\\r
6790  *\r
6791  *  Ics Interaction console functions\r
6792  *\r
6793 \*---------------------------------------------------------------------------*/\r
6794 \r
6795 #define HISTORY_SIZE 64\r
6796 static char *history[HISTORY_SIZE];\r
6797 int histIn = 0, histP = 0;\r
6798 \r
6799 VOID\r
6800 SaveInHistory(char *cmd)\r
6801 {\r
6802   if (history[histIn] != NULL) {\r
6803     free(history[histIn]);\r
6804     history[histIn] = NULL;\r
6805   }\r
6806   if (*cmd == NULLCHAR) return;\r
6807   history[histIn] = StrSave(cmd);\r
6808   histIn = (histIn + 1) % HISTORY_SIZE;\r
6809   if (history[histIn] != NULL) {\r
6810     free(history[histIn]);\r
6811     history[histIn] = NULL;\r
6812   }\r
6813   histP = histIn;\r
6814 }\r
6815 \r
6816 char *\r
6817 PrevInHistory(char *cmd)\r
6818 {\r
6819   int newhp;\r
6820   if (histP == histIn) {\r
6821     if (history[histIn] != NULL) free(history[histIn]);\r
6822     history[histIn] = StrSave(cmd);\r
6823   }\r
6824   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6825   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6826   histP = newhp;\r
6827   return history[histP];\r
6828 }\r
6829 \r
6830 char *\r
6831 NextInHistory()\r
6832 {\r
6833   if (histP == histIn) return NULL;\r
6834   histP = (histP + 1) % HISTORY_SIZE;\r
6835   return history[histP];   \r
6836 }\r
6837 \r
6838 HMENU\r
6839 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6840 {\r
6841   HMENU hmenu, h;\r
6842   int i = 0;\r
6843   hmenu = LoadMenu(hInst, "TextMenu");\r
6844   h = GetSubMenu(hmenu, 0);\r
6845   while (e->item) {\r
6846     if (strcmp(e->item, "-") == 0) {\r
6847       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6848     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6849       int flags = MF_STRING, j = 0;\r
6850       if (e->item[0] == '|') {\r
6851         flags |= MF_MENUBARBREAK;\r
6852         j++;\r
6853       }\r
6854       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6855       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6856     }\r
6857     e++;\r
6858     i++;\r
6859   } \r
6860   return hmenu;\r
6861 }\r
6862 \r
6863 WNDPROC consoleTextWindowProc;\r
6864 \r
6865 void\r
6866 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6867 {\r
6868   char buf[MSG_SIZ], name[MSG_SIZ];\r
6869   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6870   CHARRANGE sel;\r
6871 \r
6872   if (!getname) {\r
6873     SetWindowText(hInput, command);\r
6874     if (immediate) {\r
6875       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6876     } else {\r
6877       sel.cpMin = 999999;\r
6878       sel.cpMax = 999999;\r
6879       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6880       SetFocus(hInput);\r
6881     }\r
6882     return;\r
6883   }    \r
6884   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6885   if (sel.cpMin == sel.cpMax) {\r
6886     /* Expand to surrounding word */\r
6887     TEXTRANGE tr;\r
6888     do {\r
6889       tr.chrg.cpMax = sel.cpMin;\r
6890       tr.chrg.cpMin = --sel.cpMin;\r
6891       if (sel.cpMin < 0) break;\r
6892       tr.lpstrText = name;\r
6893       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6894     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6895     sel.cpMin++;\r
6896 \r
6897     do {\r
6898       tr.chrg.cpMin = sel.cpMax;\r
6899       tr.chrg.cpMax = ++sel.cpMax;\r
6900       tr.lpstrText = name;\r
6901       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6902     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6903     sel.cpMax--;\r
6904 \r
6905     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6906       MessageBeep(MB_ICONEXCLAMATION);\r
6907       return;\r
6908     }\r
6909     tr.chrg = sel;\r
6910     tr.lpstrText = name;\r
6911     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6912   } else {\r
6913     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6914       MessageBeep(MB_ICONEXCLAMATION);\r
6915       return;\r
6916     }\r
6917     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6918   }\r
6919   if (immediate) {\r
6920     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6921     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6922     SetWindowText(hInput, buf);\r
6923     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6924   } else {\r
6925     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6926       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6927     SetWindowText(hInput, buf);\r
6928     sel.cpMin = 999999;\r
6929     sel.cpMax = 999999;\r
6930     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6931     SetFocus(hInput);\r
6932   }\r
6933 }\r
6934 \r
6935 LRESULT CALLBACK \r
6936 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6937 {\r
6938   HWND hInput;\r
6939   CHARRANGE sel;\r
6940 \r
6941   switch (message) {\r
6942   case WM_KEYDOWN:\r
6943     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6944     if(wParam=='R') return 0;\r
6945     switch (wParam) {\r
6946     case VK_PRIOR:\r
6947       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6948       return 0;\r
6949     case VK_NEXT:\r
6950       sel.cpMin = 999999;\r
6951       sel.cpMax = 999999;\r
6952       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6953       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6954       return 0;\r
6955     }\r
6956     break;\r
6957   case WM_CHAR:\r
6958    if(wParam != '\022') {\r
6959     if (wParam == '\t') {\r
6960       if (GetKeyState(VK_SHIFT) < 0) {\r
6961         /* shifted */\r
6962         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6963         if (buttonDesc[0].hwnd) {\r
6964           SetFocus(buttonDesc[0].hwnd);\r
6965         } else {\r
6966           SetFocus(hwndMain);\r
6967         }\r
6968       } else {\r
6969         /* unshifted */\r
6970         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6971       }\r
6972     } else {\r
6973       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6974       JAWS_DELETE( SetFocus(hInput); )\r
6975       SendMessage(hInput, message, wParam, lParam);\r
6976     }\r
6977     return 0;\r
6978    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
6979    lParam = -1;\r
6980   case WM_RBUTTONDOWN:\r
6981     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6982       /* Move selection here if it was empty */\r
6983       POINT pt;\r
6984       pt.x = LOWORD(lParam);\r
6985       pt.y = HIWORD(lParam);\r
6986       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6987       if (sel.cpMin == sel.cpMax) {\r
6988         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6989         sel.cpMax = sel.cpMin;\r
6990         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6991       }\r
6992       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6993 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
6994       POINT pt;\r
6995       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6996       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6997       if (sel.cpMin == sel.cpMax) {\r
6998         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6999         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7000       }\r
7001       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7002         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7003       }\r
7004       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7005       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7006       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7007       MenuPopup(hwnd, pt, hmenu, -1);\r
7008 }\r
7009     }\r
7010     return 0;\r
7011   case WM_RBUTTONUP:\r
7012     if (GetKeyState(VK_SHIFT) & ~1) {\r
7013       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7014         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7015     }\r
7016     return 0;\r
7017   case WM_PASTE:\r
7018     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7019     SetFocus(hInput);\r
7020     return SendMessage(hInput, message, wParam, lParam);\r
7021   case WM_MBUTTONDOWN:\r
7022     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7023   case WM_COMMAND:\r
7024     switch (LOWORD(wParam)) {\r
7025     case IDM_QuickPaste:\r
7026       {\r
7027         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7028         if (sel.cpMin == sel.cpMax) {\r
7029           MessageBeep(MB_ICONEXCLAMATION);\r
7030           return 0;\r
7031         }\r
7032         SendMessage(hwnd, WM_COPY, 0, 0);\r
7033         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7034         SendMessage(hInput, WM_PASTE, 0, 0);\r
7035         SetFocus(hInput);\r
7036         return 0;\r
7037       }\r
7038     case IDM_Cut:\r
7039       SendMessage(hwnd, WM_CUT, 0, 0);\r
7040       return 0;\r
7041     case IDM_Paste:\r
7042       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7043       return 0;\r
7044     case IDM_Copy:\r
7045       SendMessage(hwnd, WM_COPY, 0, 0);\r
7046       return 0;\r
7047     default:\r
7048       {\r
7049         int i = LOWORD(wParam) - IDM_CommandX;\r
7050         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7051             icsTextMenuEntry[i].command != NULL) {\r
7052           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7053                    icsTextMenuEntry[i].getname,\r
7054                    icsTextMenuEntry[i].immediate);\r
7055           return 0;\r
7056         }\r
7057       }\r
7058       break;\r
7059     }\r
7060     break;\r
7061   }\r
7062   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7063 }\r
7064 \r
7065 WNDPROC consoleInputWindowProc;\r
7066 \r
7067 LRESULT CALLBACK\r
7068 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7069 {\r
7070   char buf[MSG_SIZ];\r
7071   char *p;\r
7072   static BOOL sendNextChar = FALSE;\r
7073   static BOOL quoteNextChar = FALSE;\r
7074   InputSource *is = consoleInputSource;\r
7075   CHARFORMAT cf;\r
7076   CHARRANGE sel;\r
7077 \r
7078   switch (message) {\r
7079   case WM_CHAR:\r
7080     if (!appData.localLineEditing || sendNextChar) {\r
7081       is->buf[0] = (CHAR) wParam;\r
7082       is->count = 1;\r
7083       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7084       sendNextChar = FALSE;\r
7085       return 0;\r
7086     }\r
7087     if (quoteNextChar) {\r
7088       buf[0] = (char) wParam;\r
7089       buf[1] = NULLCHAR;\r
7090       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7091       quoteNextChar = FALSE;\r
7092       return 0;\r
7093     }\r
7094     switch (wParam) {\r
7095     case '\r':   /* Enter key */\r
7096       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7097       if (consoleEcho) SaveInHistory(is->buf);\r
7098       is->buf[is->count++] = '\n';\r
7099       is->buf[is->count] = NULLCHAR;\r
7100       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7101       if (consoleEcho) {\r
7102         ConsoleOutput(is->buf, is->count, TRUE);\r
7103       } else if (appData.localLineEditing) {\r
7104         ConsoleOutput("\n", 1, TRUE);\r
7105       }\r
7106       /* fall thru */\r
7107     case '\033': /* Escape key */\r
7108       SetWindowText(hwnd, "");\r
7109       cf.cbSize = sizeof(CHARFORMAT);\r
7110       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7111       if (consoleEcho) {\r
7112         cf.crTextColor = textAttribs[ColorNormal].color;\r
7113       } else {\r
7114         cf.crTextColor = COLOR_ECHOOFF;\r
7115       }\r
7116       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7117       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7118       return 0;\r
7119     case '\t':   /* Tab key */\r
7120       if (GetKeyState(VK_SHIFT) < 0) {\r
7121         /* shifted */\r
7122         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7123       } else {\r
7124         /* unshifted */\r
7125         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7126         if (buttonDesc[0].hwnd) {\r
7127           SetFocus(buttonDesc[0].hwnd);\r
7128         } else {\r
7129           SetFocus(hwndMain);\r
7130         }\r
7131       }\r
7132       return 0;\r
7133     case '\023': /* Ctrl+S */\r
7134       sendNextChar = TRUE;\r
7135       return 0;\r
7136     case '\021': /* Ctrl+Q */\r
7137       quoteNextChar = TRUE;\r
7138       return 0;\r
7139     JAWS_REPLAY\r
7140     default:\r
7141       break;\r
7142     }\r
7143     break;\r
7144   case WM_KEYDOWN:\r
7145     switch (wParam) {\r
7146     case VK_UP:\r
7147       GetWindowText(hwnd, buf, MSG_SIZ);\r
7148       p = PrevInHistory(buf);\r
7149       if (p != NULL) {\r
7150         SetWindowText(hwnd, p);\r
7151         sel.cpMin = 999999;\r
7152         sel.cpMax = 999999;\r
7153         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7154         return 0;\r
7155       }\r
7156       break;\r
7157     case VK_DOWN:\r
7158       p = NextInHistory();\r
7159       if (p != NULL) {\r
7160         SetWindowText(hwnd, p);\r
7161         sel.cpMin = 999999;\r
7162         sel.cpMax = 999999;\r
7163         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7164         return 0;\r
7165       }\r
7166       break;\r
7167     case VK_HOME:\r
7168     case VK_END:\r
7169       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7170       /* fall thru */\r
7171     case VK_PRIOR:\r
7172     case VK_NEXT:\r
7173       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7174       return 0;\r
7175     }\r
7176     break;\r
7177   case WM_MBUTTONDOWN:\r
7178     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7179       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7180     break;\r
7181   case WM_RBUTTONUP:\r
7182     if (GetKeyState(VK_SHIFT) & ~1) {\r
7183       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7184         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7185     } else {\r
7186       POINT pt;\r
7187       HMENU hmenu;\r
7188       hmenu = LoadMenu(hInst, "InputMenu");\r
7189       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7190       if (sel.cpMin == sel.cpMax) {\r
7191         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7192         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7193       }\r
7194       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7195         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7196       }\r
7197       pt.x = LOWORD(lParam);\r
7198       pt.y = HIWORD(lParam);\r
7199       MenuPopup(hwnd, pt, hmenu, -1);\r
7200     }\r
7201     return 0;\r
7202   case WM_COMMAND:\r
7203     switch (LOWORD(wParam)) { \r
7204     case IDM_Undo:\r
7205       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7206       return 0;\r
7207     case IDM_SelectAll:\r
7208       sel.cpMin = 0;\r
7209       sel.cpMax = -1; /*999999?*/\r
7210       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7211       return 0;\r
7212     case IDM_Cut:\r
7213       SendMessage(hwnd, WM_CUT, 0, 0);\r
7214       return 0;\r
7215     case IDM_Paste:\r
7216       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7217       return 0;\r
7218     case IDM_Copy:\r
7219       SendMessage(hwnd, WM_COPY, 0, 0);\r
7220       return 0;\r
7221     }\r
7222     break;\r
7223   }\r
7224   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7225 }\r
7226 \r
7227 #define CO_MAX  100000\r
7228 #define CO_TRIM   1000\r
7229 \r
7230 LRESULT CALLBACK\r
7231 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7232 {\r
7233   static SnapData sd;\r
7234   HWND hText, hInput;\r
7235   RECT rect;\r
7236   static int sizeX, sizeY;\r
7237   int newSizeX, newSizeY;\r
7238   MINMAXINFO *mmi;\r
7239   WORD wMask;\r
7240 \r
7241   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7242   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7243 \r
7244   switch (message) {\r
7245   case WM_NOTIFY:\r
7246     if (((NMHDR*)lParam)->code == EN_LINK)\r
7247     {\r
7248       ENLINK *pLink = (ENLINK*)lParam;\r
7249       if (pLink->msg == WM_LBUTTONUP)\r
7250       {\r
7251         TEXTRANGE tr;\r
7252 \r
7253         tr.chrg = pLink->chrg;\r
7254         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7255         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7256         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7257         free(tr.lpstrText);\r
7258       }\r
7259     }\r
7260     break;\r
7261   case WM_INITDIALOG: /* message: initialize dialog box */\r
7262     hwndConsole = hDlg;\r
7263     SetFocus(hInput);\r
7264     consoleTextWindowProc = (WNDPROC)\r
7265       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7266     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7267     consoleInputWindowProc = (WNDPROC)\r
7268       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7269     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7270     Colorize(ColorNormal, TRUE);\r
7271     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7272     ChangedConsoleFont();\r
7273     GetClientRect(hDlg, &rect);\r
7274     sizeX = rect.right;\r
7275     sizeY = rect.bottom;\r
7276     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7277         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7278       WINDOWPLACEMENT wp;\r
7279       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7280       wp.length = sizeof(WINDOWPLACEMENT);\r
7281       wp.flags = 0;\r
7282       wp.showCmd = SW_SHOW;\r
7283       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7284       wp.rcNormalPosition.left = wpConsole.x;\r
7285       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7286       wp.rcNormalPosition.top = wpConsole.y;\r
7287       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7288       SetWindowPlacement(hDlg, &wp);\r
7289     }\r
7290 \r
7291    // [HGM] Chessknight's change 2004-07-13\r
7292    else { /* Determine Defaults */\r
7293        WINDOWPLACEMENT wp;\r
7294        wpConsole.x = wpMain.width + 1;\r
7295        wpConsole.y = wpMain.y;\r
7296        wpConsole.width = screenWidth -  wpMain.width;\r
7297        wpConsole.height = wpMain.height;\r
7298        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7299        wp.length = sizeof(WINDOWPLACEMENT);\r
7300        wp.flags = 0;\r
7301        wp.showCmd = SW_SHOW;\r
7302        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7303        wp.rcNormalPosition.left = wpConsole.x;\r
7304        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7305        wp.rcNormalPosition.top = wpConsole.y;\r
7306        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7307        SetWindowPlacement(hDlg, &wp);\r
7308     }\r
7309 \r
7310    // Allow hText to highlight URLs and send notifications on them\r
7311    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7312    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7313    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7314    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7315 \r
7316     return FALSE;\r
7317 \r
7318   case WM_SETFOCUS:\r
7319     SetFocus(hInput);\r
7320     return 0;\r
7321 \r
7322   case WM_CLOSE:\r
7323     ExitEvent(0);\r
7324     /* not reached */\r
7325     break;\r
7326 \r
7327   case WM_SIZE:\r
7328     if (IsIconic(hDlg)) break;\r
7329     newSizeX = LOWORD(lParam);\r
7330     newSizeY = HIWORD(lParam);\r
7331     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7332       RECT rectText, rectInput;\r
7333       POINT pt;\r
7334       int newTextHeight, newTextWidth;\r
7335       GetWindowRect(hText, &rectText);\r
7336       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7337       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7338       if (newTextHeight < 0) {\r
7339         newSizeY += -newTextHeight;\r
7340         newTextHeight = 0;\r
7341       }\r
7342       SetWindowPos(hText, NULL, 0, 0,\r
7343         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7344       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7345       pt.x = rectInput.left;\r
7346       pt.y = rectInput.top + newSizeY - sizeY;\r
7347       ScreenToClient(hDlg, &pt);\r
7348       SetWindowPos(hInput, NULL, \r
7349         pt.x, pt.y, /* needs client coords */   \r
7350         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7351         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7352     }\r
7353     sizeX = newSizeX;\r
7354     sizeY = newSizeY;\r
7355     break;\r
7356 \r
7357   case WM_GETMINMAXINFO:\r
7358     /* Prevent resizing window too small */\r
7359     mmi = (MINMAXINFO *) lParam;\r
7360     mmi->ptMinTrackSize.x = 100;\r
7361     mmi->ptMinTrackSize.y = 100;\r
7362     break;\r
7363 \r
7364   /* [AS] Snapping */\r
7365   case WM_ENTERSIZEMOVE:\r
7366     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7367 \r
7368   case WM_SIZING:\r
7369     return OnSizing( &sd, hDlg, wParam, lParam );\r
7370 \r
7371   case WM_MOVING:\r
7372     return OnMoving( &sd, hDlg, wParam, lParam );\r
7373 \r
7374   case WM_EXITSIZEMOVE:\r
7375         UpdateICSWidth(hText);\r
7376     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7377   }\r
7378 \r
7379   return DefWindowProc(hDlg, message, wParam, lParam);\r
7380 }\r
7381 \r
7382 \r
7383 VOID\r
7384 ConsoleCreate()\r
7385 {\r
7386   HWND hCons;\r
7387   if (hwndConsole) return;\r
7388   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7389   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7390 }\r
7391 \r
7392 \r
7393 VOID\r
7394 ConsoleOutput(char* data, int length, int forceVisible)\r
7395 {\r
7396   HWND hText;\r
7397   int trim, exlen;\r
7398   char *p, *q;\r
7399   char buf[CO_MAX+1];\r
7400   POINT pEnd;\r
7401   RECT rect;\r
7402   static int delayLF = 0;\r
7403   CHARRANGE savesel, sel;\r
7404 \r
7405   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7406   p = data;\r
7407   q = buf;\r
7408   if (delayLF) {\r
7409     *q++ = '\r';\r
7410     *q++ = '\n';\r
7411     delayLF = 0;\r
7412   }\r
7413   while (length--) {\r
7414     if (*p == '\n') {\r
7415       if (*++p) {\r
7416         *q++ = '\r';\r
7417         *q++ = '\n';\r
7418       } else {\r
7419         delayLF = 1;\r
7420       }\r
7421     } else if (*p == '\007') {\r
7422        MyPlaySound(&sounds[(int)SoundBell]);\r
7423        p++;\r
7424     } else {\r
7425       *q++ = *p++;\r
7426     }\r
7427   }\r
7428   *q = NULLCHAR;\r
7429   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7430   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7431   /* Save current selection */\r
7432   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7433   exlen = GetWindowTextLength(hText);\r
7434   /* Find out whether current end of text is visible */\r
7435   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7436   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7437   /* Trim existing text if it's too long */\r
7438   if (exlen + (q - buf) > CO_MAX) {\r
7439     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7440     sel.cpMin = 0;\r
7441     sel.cpMax = trim;\r
7442     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7443     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7444     exlen -= trim;\r
7445     savesel.cpMin -= trim;\r
7446     savesel.cpMax -= trim;\r
7447     if (exlen < 0) exlen = 0;\r
7448     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7449     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7450   }\r
7451   /* Append the new text */\r
7452   sel.cpMin = exlen;\r
7453   sel.cpMax = exlen;\r
7454   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7455   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7456   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7457   if (forceVisible || exlen == 0 ||\r
7458       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7459        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7460     /* Scroll to make new end of text visible if old end of text\r
7461        was visible or new text is an echo of user typein */\r
7462     sel.cpMin = 9999999;\r
7463     sel.cpMax = 9999999;\r
7464     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7465     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7466     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7467     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7468   }\r
7469   if (savesel.cpMax == exlen || forceVisible) {\r
7470     /* Move insert point to new end of text if it was at the old\r
7471        end of text or if the new text is an echo of user typein */\r
7472     sel.cpMin = 9999999;\r
7473     sel.cpMax = 9999999;\r
7474     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7475   } else {\r
7476     /* Restore previous selection */\r
7477     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7478   }\r
7479   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7480 }\r
7481 \r
7482 /*---------*/\r
7483 \r
7484 \r
7485 void\r
7486 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7487 {\r
7488   char buf[100];\r
7489   char *str;\r
7490   COLORREF oldFg, oldBg;\r
7491   HFONT oldFont;\r
7492   RECT rect;\r
7493 \r
7494   if(copyNumber > 1)\r
7495     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7496 \r
7497   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7498   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7499   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7500 \r
7501   rect.left = x;\r
7502   rect.right = x + squareSize;\r
7503   rect.top  = y;\r
7504   rect.bottom = y + squareSize;\r
7505   str = buf;\r
7506 \r
7507   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7508                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7509              y, ETO_CLIPPED|ETO_OPAQUE,\r
7510              &rect, str, strlen(str), NULL);\r
7511 \r
7512   (void) SetTextColor(hdc, oldFg);\r
7513   (void) SetBkColor(hdc, oldBg);\r
7514   (void) SelectObject(hdc, oldFont);\r
7515 }\r
7516 \r
7517 void\r
7518 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7519               RECT *rect, char *color, char *flagFell)\r
7520 {\r
7521   char buf[100];\r
7522   char *str;\r
7523   COLORREF oldFg, oldBg;\r
7524   HFONT oldFont;\r
7525 \r
7526   if (twoBoards && partnerUp) return;\r
7527   if (appData.clockMode) {\r
7528     if (tinyLayout)\r
7529       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7530     else\r
7531       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7532     str = buf;\r
7533   } else {\r
7534     str = color;\r
7535   }\r
7536 \r
7537   if (highlight) {\r
7538     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7539     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7540   } else {\r
7541     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7542     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7543   }\r
7544   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7545 \r
7546   JAWS_SILENCE\r
7547 \r
7548   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7549              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7550              rect, str, strlen(str), NULL);\r
7551   if(logoHeight > 0 && appData.clockMode) {\r
7552       RECT r;\r
7553       str += strlen(color)+2;\r
7554       r.top = rect->top + logoHeight/2;\r
7555       r.left = rect->left;\r
7556       r.right = rect->right;\r
7557       r.bottom = rect->bottom;\r
7558       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7559                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7560                  &r, str, strlen(str), NULL);\r
7561   }\r
7562   (void) SetTextColor(hdc, oldFg);\r
7563   (void) SetBkColor(hdc, oldBg);\r
7564   (void) SelectObject(hdc, oldFont);\r
7565 }\r
7566 \r
7567 \r
7568 int\r
7569 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7570            OVERLAPPED *ovl)\r
7571 {\r
7572   int ok, err;\r
7573 \r
7574   /* [AS]  */\r
7575   if( count <= 0 ) {\r
7576     if (appData.debugMode) {\r
7577       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7578     }\r
7579 \r
7580     return ERROR_INVALID_USER_BUFFER;\r
7581   }\r
7582 \r
7583   ResetEvent(ovl->hEvent);\r
7584   ovl->Offset = ovl->OffsetHigh = 0;\r
7585   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7586   if (ok) {\r
7587     err = NO_ERROR;\r
7588   } else {\r
7589     err = GetLastError();\r
7590     if (err == ERROR_IO_PENDING) {\r
7591       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7592       if (ok)\r
7593         err = NO_ERROR;\r
7594       else\r
7595         err = GetLastError();\r
7596     }\r
7597   }\r
7598   return err;\r
7599 }\r
7600 \r
7601 int\r
7602 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7603             OVERLAPPED *ovl)\r
7604 {\r
7605   int ok, err;\r
7606 \r
7607   ResetEvent(ovl->hEvent);\r
7608   ovl->Offset = ovl->OffsetHigh = 0;\r
7609   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7610   if (ok) {\r
7611     err = NO_ERROR;\r
7612   } else {\r
7613     err = GetLastError();\r
7614     if (err == ERROR_IO_PENDING) {\r
7615       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7616       if (ok)\r
7617         err = NO_ERROR;\r
7618       else\r
7619         err = GetLastError();\r
7620     }\r
7621   }\r
7622   return err;\r
7623 }\r
7624 \r
7625 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7626 void CheckForInputBufferFull( InputSource * is )\r
7627 {\r
7628     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7629         /* Look for end of line */\r
7630         char * p = is->buf;\r
7631         \r
7632         while( p < is->next && *p != '\n' ) {\r
7633             p++;\r
7634         }\r
7635 \r
7636         if( p >= is->next ) {\r
7637             if (appData.debugMode) {\r
7638                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7639             }\r
7640 \r
7641             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7642             is->count = (DWORD) -1;\r
7643             is->next = is->buf;\r
7644         }\r
7645     }\r
7646 }\r
7647 \r
7648 DWORD\r
7649 InputThread(LPVOID arg)\r
7650 {\r
7651   InputSource *is;\r
7652   OVERLAPPED ovl;\r
7653 \r
7654   is = (InputSource *) arg;\r
7655   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7656   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7657   while (is->hThread != NULL) {\r
7658     is->error = DoReadFile(is->hFile, is->next,\r
7659                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7660                            &is->count, &ovl);\r
7661     if (is->error == NO_ERROR) {\r
7662       is->next += is->count;\r
7663     } else {\r
7664       if (is->error == ERROR_BROKEN_PIPE) {\r
7665         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7666         is->count = 0;\r
7667       } else {\r
7668         is->count = (DWORD) -1;\r
7669         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7670         break; \r
7671       }\r
7672     }\r
7673 \r
7674     CheckForInputBufferFull( is );\r
7675 \r
7676     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7677 \r
7678     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7679 \r
7680     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7681   }\r
7682 \r
7683   CloseHandle(ovl.hEvent);\r
7684   CloseHandle(is->hFile);\r
7685 \r
7686   if (appData.debugMode) {\r
7687     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7688   }\r
7689 \r
7690   return 0;\r
7691 }\r
7692 \r
7693 \r
7694 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7695 DWORD\r
7696 NonOvlInputThread(LPVOID arg)\r
7697 {\r
7698   InputSource *is;\r
7699   char *p, *q;\r
7700   int i;\r
7701   char prev;\r
7702 \r
7703   is = (InputSource *) arg;\r
7704   while (is->hThread != NULL) {\r
7705     is->error = ReadFile(is->hFile, is->next,\r
7706                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7707                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7708     if (is->error == NO_ERROR) {\r
7709       /* Change CRLF to LF */\r
7710       if (is->next > is->buf) {\r
7711         p = is->next - 1;\r
7712         i = is->count + 1;\r
7713       } else {\r
7714         p = is->next;\r
7715         i = is->count;\r
7716       }\r
7717       q = p;\r
7718       prev = NULLCHAR;\r
7719       while (i > 0) {\r
7720         if (prev == '\r' && *p == '\n') {\r
7721           *(q-1) = '\n';\r
7722           is->count--;\r
7723         } else { \r
7724           *q++ = *p;\r
7725         }\r
7726         prev = *p++;\r
7727         i--;\r
7728       }\r
7729       *q = NULLCHAR;\r
7730       is->next = q;\r
7731     } else {\r
7732       if (is->error == ERROR_BROKEN_PIPE) {\r
7733         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7734         is->count = 0; \r
7735       } else {\r
7736         is->count = (DWORD) -1;\r
7737       }\r
7738     }\r
7739 \r
7740     CheckForInputBufferFull( is );\r
7741 \r
7742     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7743 \r
7744     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7745 \r
7746     if (is->count < 0) break;  /* Quit on error */\r
7747   }\r
7748   CloseHandle(is->hFile);\r
7749   return 0;\r
7750 }\r
7751 \r
7752 DWORD\r
7753 SocketInputThread(LPVOID arg)\r
7754 {\r
7755   InputSource *is;\r
7756 \r
7757   is = (InputSource *) arg;\r
7758   while (is->hThread != NULL) {\r
7759     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7760     if ((int)is->count == SOCKET_ERROR) {\r
7761       is->count = (DWORD) -1;\r
7762       is->error = WSAGetLastError();\r
7763     } else {\r
7764       is->error = NO_ERROR;\r
7765       is->next += is->count;\r
7766       if (is->count == 0 && is->second == is) {\r
7767         /* End of file on stderr; quit with no message */\r
7768         break;\r
7769       }\r
7770     }\r
7771     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7772 \r
7773     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7774 \r
7775     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7776   }\r
7777   return 0;\r
7778 }\r
7779 \r
7780 VOID\r
7781 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7782 {\r
7783   InputSource *is;\r
7784 \r
7785   is = (InputSource *) lParam;\r
7786   if (is->lineByLine) {\r
7787     /* Feed in lines one by one */\r
7788     char *p = is->buf;\r
7789     char *q = p;\r
7790     while (q < is->next) {\r
7791       if (*q++ == '\n') {\r
7792         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7793         p = q;\r
7794       }\r
7795     }\r
7796     \r
7797     /* Move any partial line to the start of the buffer */\r
7798     q = is->buf;\r
7799     while (p < is->next) {\r
7800       *q++ = *p++;\r
7801     }\r
7802     is->next = q;\r
7803 \r
7804     if (is->error != NO_ERROR || is->count == 0) {\r
7805       /* Notify backend of the error.  Note: If there was a partial\r
7806          line at the end, it is not flushed through. */\r
7807       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7808     }\r
7809   } else {\r
7810     /* Feed in the whole chunk of input at once */\r
7811     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7812     is->next = is->buf;\r
7813   }\r
7814 }\r
7815 \r
7816 /*---------------------------------------------------------------------------*\\r
7817  *\r
7818  *  Menu enables. Used when setting various modes.\r
7819  *\r
7820 \*---------------------------------------------------------------------------*/\r
7821 \r
7822 typedef struct {\r
7823   int item;\r
7824   int flags;\r
7825 } Enables;\r
7826 \r
7827 VOID\r
7828 GreyRevert(Boolean grey)\r
7829 { // [HGM] vari: for retracting variations in local mode\r
7830   HMENU hmenu = GetMenu(hwndMain);\r
7831   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7832   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7833 }\r
7834 \r
7835 VOID\r
7836 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7837 {\r
7838   while (enab->item > 0) {\r
7839     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7840     enab++;\r
7841   }\r
7842 }\r
7843 \r
7844 Enables gnuEnables[] = {\r
7845   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7846   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7847   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7848   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7849   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7850   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7851   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7852   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7858 \r
7859   // Needed to switch from ncp to GNU mode on Engine Load\r
7860   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7861   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7865   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7866   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7867   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7868   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7869   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7870   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7871   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7872   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7873   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7874   { -1, -1 }\r
7875 };\r
7876 \r
7877 Enables icsEnables[] = {\r
7878   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7879   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7880   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7881   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7882   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7883   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7884   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7885   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7886   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7887   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7888   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7889   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7890   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7891   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7894   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7898   { -1, -1 }\r
7899 };\r
7900 \r
7901 #if ZIPPY\r
7902 Enables zippyEnables[] = {\r
7903   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7904   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7905   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7906   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7907   { -1, -1 }\r
7908 };\r
7909 #endif\r
7910 \r
7911 Enables ncpEnables[] = {\r
7912   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7913   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7921   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7922   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7924   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7925   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7927   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7929   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
7934   { -1, -1 }\r
7935 };\r
7936 \r
7937 Enables trainingOnEnables[] = {\r
7938   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7941   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7942   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7943   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7944   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7947   { -1, -1 }\r
7948 };\r
7949 \r
7950 Enables trainingOffEnables[] = {\r
7951   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7952   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
7953   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7954   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7955   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7956   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7957   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7958   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7959   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7960   { -1, -1 }\r
7961 };\r
7962 \r
7963 /* These modify either ncpEnables or gnuEnables */\r
7964 Enables cmailEnables[] = {\r
7965   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7966   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7967   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7968   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7970   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7972   { -1, -1 }\r
7973 };\r
7974 \r
7975 Enables machineThinkingEnables[] = {\r
7976   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7981   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7986   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7987   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7988   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7989 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7990   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7991   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7992   { -1, -1 }\r
7993 };\r
7994 \r
7995 Enables userThinkingEnables[] = {\r
7996   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7997   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7998   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7999   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8000   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8001   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8002   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8003   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8004   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8005   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8006   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8007   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8008   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8009 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8010   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8011   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8012   { -1, -1 }\r
8013 };\r
8014 \r
8015 /*---------------------------------------------------------------------------*\\r
8016  *\r
8017  *  Front-end interface functions exported by XBoard.\r
8018  *  Functions appear in same order as prototypes in frontend.h.\r
8019  * \r
8020 \*---------------------------------------------------------------------------*/\r
8021 VOID\r
8022 CheckMark(UINT item, int state)\r
8023 {\r
8024     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8025 }\r
8026 \r
8027 VOID\r
8028 ModeHighlight()\r
8029 {\r
8030   static UINT prevChecked = 0;\r
8031   static int prevPausing = 0;\r
8032   UINT nowChecked;\r
8033 \r
8034   if (pausing != prevPausing) {\r
8035     prevPausing = pausing;\r
8036     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8037                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8038     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8039   }\r
8040 \r
8041   switch (gameMode) {\r
8042   case BeginningOfGame:\r
8043     if (appData.icsActive)\r
8044       nowChecked = IDM_IcsClient;\r
8045     else if (appData.noChessProgram)\r
8046       nowChecked = IDM_EditGame;\r
8047     else\r
8048       nowChecked = IDM_MachineBlack;\r
8049     break;\r
8050   case MachinePlaysBlack:\r
8051     nowChecked = IDM_MachineBlack;\r
8052     break;\r
8053   case MachinePlaysWhite:\r
8054     nowChecked = IDM_MachineWhite;\r
8055     break;\r
8056   case TwoMachinesPlay:\r
8057     nowChecked = IDM_TwoMachines;\r
8058     break;\r
8059   case AnalyzeMode:\r
8060     nowChecked = IDM_AnalysisMode;\r
8061     break;\r
8062   case AnalyzeFile:\r
8063     nowChecked = IDM_AnalyzeFile;\r
8064     break;\r
8065   case EditGame:\r
8066     nowChecked = IDM_EditGame;\r
8067     break;\r
8068   case PlayFromGameFile:\r
8069     nowChecked = IDM_LoadGame;\r
8070     break;\r
8071   case EditPosition:\r
8072     nowChecked = IDM_EditPosition;\r
8073     break;\r
8074   case Training:\r
8075     nowChecked = IDM_Training;\r
8076     break;\r
8077   case IcsPlayingWhite:\r
8078   case IcsPlayingBlack:\r
8079   case IcsObserving:\r
8080   case IcsIdle:\r
8081     nowChecked = IDM_IcsClient;\r
8082     break;\r
8083   default:\r
8084   case EndOfGame:\r
8085     nowChecked = 0;\r
8086     break;\r
8087   }\r
8088   CheckMark(prevChecked, MF_UNCHECKED);\r
8089   CheckMark(nowChecked, MF_CHECKED);\r
8090   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8091 \r
8092   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8093     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8094                           MF_BYCOMMAND|MF_ENABLED);\r
8095   } else {\r
8096     (void) EnableMenuItem(GetMenu(hwndMain), \r
8097                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8098   }\r
8099 \r
8100   prevChecked = nowChecked;\r
8101 \r
8102   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8103   if (appData.icsActive) {\r
8104        if (appData.icsEngineAnalyze) {\r
8105                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8106        } else {\r
8107                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8108        }\r
8109   }\r
8110   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8111 }\r
8112 \r
8113 VOID\r
8114 SetICSMode()\r
8115 {\r
8116   HMENU hmenu = GetMenu(hwndMain);\r
8117   SetMenuEnables(hmenu, icsEnables);\r
8118   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8119     MF_BYCOMMAND|MF_ENABLED);\r
8120 #if ZIPPY\r
8121   if (appData.zippyPlay) {\r
8122     SetMenuEnables(hmenu, zippyEnables);\r
8123     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8124          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8125           MF_BYCOMMAND|MF_ENABLED);\r
8126   }\r
8127 #endif\r
8128 }\r
8129 \r
8130 VOID\r
8131 SetGNUMode()\r
8132 {\r
8133   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8134 }\r
8135 \r
8136 VOID\r
8137 SetNCPMode()\r
8138 {\r
8139   HMENU hmenu = GetMenu(hwndMain);\r
8140   SetMenuEnables(hmenu, ncpEnables);\r
8141     DrawMenuBar(hwndMain);\r
8142 }\r
8143 \r
8144 VOID\r
8145 SetCmailMode()\r
8146 {\r
8147   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8148 }\r
8149 \r
8150 VOID \r
8151 SetTrainingModeOn()\r
8152 {\r
8153   int i;\r
8154   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8155   for (i = 0; i < N_BUTTONS; i++) {\r
8156     if (buttonDesc[i].hwnd != NULL)\r
8157       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8158   }\r
8159   CommentPopDown();\r
8160 }\r
8161 \r
8162 VOID SetTrainingModeOff()\r
8163 {\r
8164   int i;\r
8165   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8166   for (i = 0; i < N_BUTTONS; i++) {\r
8167     if (buttonDesc[i].hwnd != NULL)\r
8168       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8169   }\r
8170 }\r
8171 \r
8172 \r
8173 VOID\r
8174 SetUserThinkingEnables()\r
8175 {\r
8176   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8177 }\r
8178 \r
8179 VOID\r
8180 SetMachineThinkingEnables()\r
8181 {\r
8182   HMENU hMenu = GetMenu(hwndMain);\r
8183   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8184 \r
8185   SetMenuEnables(hMenu, machineThinkingEnables);\r
8186 \r
8187   if (gameMode == MachinePlaysBlack) {\r
8188     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8189   } else if (gameMode == MachinePlaysWhite) {\r
8190     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8191   } else if (gameMode == TwoMachinesPlay) {\r
8192     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8193   }\r
8194 }\r
8195 \r
8196 \r
8197 VOID\r
8198 DisplayTitle(char *str)\r
8199 {\r
8200   char title[MSG_SIZ], *host;\r
8201   if (str[0] != NULLCHAR) {\r
8202     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8203   } else if (appData.icsActive) {\r
8204     if (appData.icsCommPort[0] != NULLCHAR)\r
8205       host = "ICS";\r
8206     else \r
8207       host = appData.icsHost;\r
8208       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8209   } else if (appData.noChessProgram) {\r
8210     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8211   } else {\r
8212     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8213     strcat(title, ": ");\r
8214     strcat(title, first.tidy);\r
8215   }\r
8216   SetWindowText(hwndMain, title);\r
8217 }\r
8218 \r
8219 \r
8220 VOID\r
8221 DisplayMessage(char *str1, char *str2)\r
8222 {\r
8223   HDC hdc;\r
8224   HFONT oldFont;\r
8225   int remain = MESSAGE_TEXT_MAX - 1;\r
8226   int len;\r
8227 \r
8228   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8229   messageText[0] = NULLCHAR;\r
8230   if (*str1) {\r
8231     len = strlen(str1);\r
8232     if (len > remain) len = remain;\r
8233     strncpy(messageText, str1, len);\r
8234     messageText[len] = NULLCHAR;\r
8235     remain -= len;\r
8236   }\r
8237   if (*str2 && remain >= 2) {\r
8238     if (*str1) {\r
8239       strcat(messageText, "  ");\r
8240       remain -= 2;\r
8241     }\r
8242     len = strlen(str2);\r
8243     if (len > remain) len = remain;\r
8244     strncat(messageText, str2, len);\r
8245   }\r
8246   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8247   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8248 \r
8249   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8250 \r
8251   SAYMACHINEMOVE();\r
8252 \r
8253   hdc = GetDC(hwndMain);\r
8254   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8255   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8256              &messageRect, messageText, strlen(messageText), NULL);\r
8257   (void) SelectObject(hdc, oldFont);\r
8258   (void) ReleaseDC(hwndMain, hdc);\r
8259 }\r
8260 \r
8261 VOID\r
8262 DisplayError(char *str, int error)\r
8263 {\r
8264   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8265   int len;\r
8266 \r
8267   if (error == 0) {\r
8268     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8269   } else {\r
8270     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8271                         NULL, error, LANG_NEUTRAL,\r
8272                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8273     if (len > 0) {\r
8274       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8275     } else {\r
8276       ErrorMap *em = errmap;\r
8277       while (em->err != 0 && em->err != error) em++;\r
8278       if (em->err != 0) {\r
8279         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8280       } else {\r
8281         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8282       }\r
8283     }\r
8284   }\r
8285   \r
8286   ErrorPopUp(_("Error"), buf);\r
8287 }\r
8288 \r
8289 \r
8290 VOID\r
8291 DisplayMoveError(char *str)\r
8292 {\r
8293   fromX = fromY = -1;\r
8294   ClearHighlights();\r
8295   DrawPosition(FALSE, NULL);\r
8296   if (appData.popupMoveErrors) {\r
8297     ErrorPopUp(_("Error"), str);\r
8298   } else {\r
8299     DisplayMessage(str, "");\r
8300     moveErrorMessageUp = TRUE;\r
8301   }\r
8302 }\r
8303 \r
8304 VOID\r
8305 DisplayFatalError(char *str, int error, int exitStatus)\r
8306 {\r
8307   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8308   int len;\r
8309   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8310 \r
8311   if (error != 0) {\r
8312     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8313                         NULL, error, LANG_NEUTRAL,\r
8314                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8315     if (len > 0) {\r
8316       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8317     } else {\r
8318       ErrorMap *em = errmap;\r
8319       while (em->err != 0 && em->err != error) em++;\r
8320       if (em->err != 0) {\r
8321         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8322       } else {\r
8323         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8324       }\r
8325     }\r
8326     str = buf;\r
8327   }\r
8328   if (appData.debugMode) {\r
8329     fprintf(debugFP, "%s: %s\n", label, str);\r
8330   }\r
8331   if (appData.popupExitMessage) {\r
8332     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8333                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8334   }\r
8335   ExitEvent(exitStatus);\r
8336 }\r
8337 \r
8338 \r
8339 VOID\r
8340 DisplayInformation(char *str)\r
8341 {\r
8342   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8343 }\r
8344 \r
8345 \r
8346 VOID\r
8347 DisplayNote(char *str)\r
8348 {\r
8349   ErrorPopUp(_("Note"), str);\r
8350 }\r
8351 \r
8352 \r
8353 typedef struct {\r
8354   char *title, *question, *replyPrefix;\r
8355   ProcRef pr;\r
8356 } QuestionParams;\r
8357 \r
8358 LRESULT CALLBACK\r
8359 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8360 {\r
8361   static QuestionParams *qp;\r
8362   char reply[MSG_SIZ];\r
8363   int len, err;\r
8364 \r
8365   switch (message) {\r
8366   case WM_INITDIALOG:\r
8367     qp = (QuestionParams *) lParam;\r
8368     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8369     Translate(hDlg, DLG_Question);\r
8370     SetWindowText(hDlg, qp->title);\r
8371     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8372     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8373     return FALSE;\r
8374 \r
8375   case WM_COMMAND:\r
8376     switch (LOWORD(wParam)) {\r
8377     case IDOK:\r
8378       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8379       if (*reply) strcat(reply, " ");\r
8380       len = strlen(reply);\r
8381       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8382       strcat(reply, "\n");\r
8383       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8384       EndDialog(hDlg, TRUE);\r
8385       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8386       return TRUE;\r
8387     case IDCANCEL:\r
8388       EndDialog(hDlg, FALSE);\r
8389       return TRUE;\r
8390     default:\r
8391       break;\r
8392     }\r
8393     break;\r
8394   }\r
8395   return FALSE;\r
8396 }\r
8397 \r
8398 VOID\r
8399 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8400 {\r
8401     QuestionParams qp;\r
8402     FARPROC lpProc;\r
8403     \r
8404     qp.title = title;\r
8405     qp.question = question;\r
8406     qp.replyPrefix = replyPrefix;\r
8407     qp.pr = pr;\r
8408     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8409     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8410       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8411     FreeProcInstance(lpProc);\r
8412 }\r
8413 \r
8414 /* [AS] Pick FRC position */\r
8415 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8416 {\r
8417     static int * lpIndexFRC;\r
8418     BOOL index_is_ok;\r
8419     char buf[16];\r
8420 \r
8421     switch( message )\r
8422     {\r
8423     case WM_INITDIALOG:\r
8424         lpIndexFRC = (int *) lParam;\r
8425 \r
8426         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8427         Translate(hDlg, DLG_NewGameFRC);\r
8428 \r
8429         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8430         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8431         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8432         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8433 \r
8434         break;\r
8435 \r
8436     case WM_COMMAND:\r
8437         switch( LOWORD(wParam) ) {\r
8438         case IDOK:\r
8439             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8440             EndDialog( hDlg, 0 );\r
8441             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8442             return TRUE;\r
8443         case IDCANCEL:\r
8444             EndDialog( hDlg, 1 );   \r
8445             return TRUE;\r
8446         case IDC_NFG_Edit:\r
8447             if( HIWORD(wParam) == EN_CHANGE ) {\r
8448                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8449 \r
8450                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8451             }\r
8452             return TRUE;\r
8453         case IDC_NFG_Random:\r
8454           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8455             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8456             return TRUE;\r
8457         }\r
8458 \r
8459         break;\r
8460     }\r
8461 \r
8462     return FALSE;\r
8463 }\r
8464 \r
8465 int NewGameFRC()\r
8466 {\r
8467     int result;\r
8468     int index = appData.defaultFrcPosition;\r
8469     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8470 \r
8471     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8472 \r
8473     if( result == 0 ) {\r
8474         appData.defaultFrcPosition = index;\r
8475     }\r
8476 \r
8477     return result;\r
8478 }\r
8479 \r
8480 /* [AS] Game list options. Refactored by HGM */\r
8481 \r
8482 HWND gameListOptionsDialog;\r
8483 \r
8484 // low-level front-end: clear text edit / list widget\r
8485 void\r
8486 GLT_ClearList()\r
8487 {\r
8488     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8489 }\r
8490 \r
8491 // low-level front-end: clear text edit / list widget\r
8492 void\r
8493 GLT_DeSelectList()\r
8494 {\r
8495     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8496 }\r
8497 \r
8498 // low-level front-end: append line to text edit / list widget\r
8499 void\r
8500 GLT_AddToList( char *name )\r
8501 {\r
8502     if( name != 0 ) {\r
8503             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8504     }\r
8505 }\r
8506 \r
8507 // low-level front-end: get line from text edit / list widget\r
8508 Boolean\r
8509 GLT_GetFromList( int index, char *name )\r
8510 {\r
8511     if( name != 0 ) {\r
8512             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8513                 return TRUE;\r
8514     }\r
8515     return FALSE;\r
8516 }\r
8517 \r
8518 void GLT_MoveSelection( HWND hDlg, int delta )\r
8519 {\r
8520     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8521     int idx2 = idx1 + delta;\r
8522     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8523 \r
8524     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8525         char buf[128];\r
8526 \r
8527         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8528         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8529         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8530         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8531     }\r
8532 }\r
8533 \r
8534 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8535 {\r
8536     switch( message )\r
8537     {\r
8538     case WM_INITDIALOG:\r
8539         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8540         \r
8541         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8542         Translate(hDlg, DLG_GameListOptions);\r
8543 \r
8544         /* Initialize list */\r
8545         GLT_TagsToList( lpUserGLT );\r
8546 \r
8547         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8548 \r
8549         break;\r
8550 \r
8551     case WM_COMMAND:\r
8552         switch( LOWORD(wParam) ) {\r
8553         case IDOK:\r
8554             GLT_ParseList();\r
8555             EndDialog( hDlg, 0 );\r
8556             return TRUE;\r
8557         case IDCANCEL:\r
8558             EndDialog( hDlg, 1 );\r
8559             return TRUE;\r
8560 \r
8561         case IDC_GLT_Default:\r
8562             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8563             return TRUE;\r
8564 \r
8565         case IDC_GLT_Restore:\r
8566             GLT_TagsToList( appData.gameListTags );\r
8567             return TRUE;\r
8568 \r
8569         case IDC_GLT_Up:\r
8570             GLT_MoveSelection( hDlg, -1 );\r
8571             return TRUE;\r
8572 \r
8573         case IDC_GLT_Down:\r
8574             GLT_MoveSelection( hDlg, +1 );\r
8575             return TRUE;\r
8576         }\r
8577 \r
8578         break;\r
8579     }\r
8580 \r
8581     return FALSE;\r
8582 }\r
8583 \r
8584 int GameListOptions()\r
8585 {\r
8586     int result;\r
8587     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8588 \r
8589       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8590 \r
8591     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8592 \r
8593     if( result == 0 ) {\r
8594         /* [AS] Memory leak here! */\r
8595         appData.gameListTags = strdup( lpUserGLT ); \r
8596     }\r
8597 \r
8598     return result;\r
8599 }\r
8600 \r
8601 VOID\r
8602 DisplayIcsInteractionTitle(char *str)\r
8603 {\r
8604   char consoleTitle[MSG_SIZ];\r
8605 \r
8606     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8607     SetWindowText(hwndConsole, consoleTitle);\r
8608 \r
8609     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8610       char buf[MSG_SIZ], *p = buf, *q;\r
8611         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8612       do {\r
8613         q = strchr(p, ';');\r
8614         if(q) *q++ = 0;\r
8615         if(*p) ChatPopUp(p);\r
8616       } while(p=q);\r
8617     }\r
8618 \r
8619     SetActiveWindow(hwndMain);\r
8620 }\r
8621 \r
8622 void\r
8623 DrawPosition(int fullRedraw, Board board)\r
8624 {\r
8625   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8626 }\r
8627 \r
8628 void NotifyFrontendLogin()\r
8629 {\r
8630         if (hwndConsole)\r
8631                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8632 }\r
8633 \r
8634 VOID\r
8635 ResetFrontEnd()\r
8636 {\r
8637   fromX = fromY = -1;\r
8638   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8639     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8640     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8641     dragInfo.lastpos = dragInfo.pos;\r
8642     dragInfo.start.x = dragInfo.start.y = -1;\r
8643     dragInfo.from = dragInfo.start;\r
8644     ReleaseCapture();\r
8645     DrawPosition(TRUE, NULL);\r
8646   }\r
8647   TagsPopDown();\r
8648 }\r
8649 \r
8650 \r
8651 VOID\r
8652 CommentPopUp(char *title, char *str)\r
8653 {\r
8654   HWND hwnd = GetActiveWindow();\r
8655   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8656   SAY(str);\r
8657   SetActiveWindow(hwnd);\r
8658 }\r
8659 \r
8660 VOID\r
8661 CommentPopDown(void)\r
8662 {\r
8663   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8664   if (commentDialog) {\r
8665     ShowWindow(commentDialog, SW_HIDE);\r
8666   }\r
8667   commentUp = FALSE;\r
8668 }\r
8669 \r
8670 VOID\r
8671 EditCommentPopUp(int index, char *title, char *str)\r
8672 {\r
8673   EitherCommentPopUp(index, title, str, TRUE);\r
8674 }\r
8675 \r
8676 \r
8677 VOID\r
8678 RingBell()\r
8679 {\r
8680   MyPlaySound(&sounds[(int)SoundMove]);\r
8681 }\r
8682 \r
8683 VOID PlayIcsWinSound()\r
8684 {\r
8685   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8686 }\r
8687 \r
8688 VOID PlayIcsLossSound()\r
8689 {\r
8690   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8691 }\r
8692 \r
8693 VOID PlayIcsDrawSound()\r
8694 {\r
8695   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8696 }\r
8697 \r
8698 VOID PlayIcsUnfinishedSound()\r
8699 {\r
8700   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8701 }\r
8702 \r
8703 VOID\r
8704 PlayAlarmSound()\r
8705 {\r
8706   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8707 }\r
8708 \r
8709 VOID\r
8710 PlayTellSound()\r
8711 {\r
8712   MyPlaySound(&textAttribs[ColorTell].sound);\r
8713 }\r
8714 \r
8715 \r
8716 VOID\r
8717 EchoOn()\r
8718 {\r
8719   HWND hInput;\r
8720   consoleEcho = TRUE;\r
8721   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8722   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8723   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8724 }\r
8725 \r
8726 \r
8727 VOID\r
8728 EchoOff()\r
8729 {\r
8730   CHARFORMAT cf;\r
8731   HWND hInput;\r
8732   consoleEcho = FALSE;\r
8733   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8734   /* This works OK: set text and background both to the same color */\r
8735   cf = consoleCF;\r
8736   cf.crTextColor = COLOR_ECHOOFF;\r
8737   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8738   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8739 }\r
8740 \r
8741 /* No Raw()...? */\r
8742 \r
8743 void Colorize(ColorClass cc, int continuation)\r
8744 {\r
8745   currentColorClass = cc;\r
8746   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8747   consoleCF.crTextColor = textAttribs[cc].color;\r
8748   consoleCF.dwEffects = textAttribs[cc].effects;\r
8749   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8750 }\r
8751 \r
8752 char *\r
8753 UserName()\r
8754 {\r
8755   static char buf[MSG_SIZ];\r
8756   DWORD bufsiz = MSG_SIZ;\r
8757 \r
8758   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8759         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8760   }\r
8761   if (!GetUserName(buf, &bufsiz)) {\r
8762     /*DisplayError("Error getting user name", GetLastError());*/\r
8763     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8764   }\r
8765   return buf;\r
8766 }\r
8767 \r
8768 char *\r
8769 HostName()\r
8770 {\r
8771   static char buf[MSG_SIZ];\r
8772   DWORD bufsiz = MSG_SIZ;\r
8773 \r
8774   if (!GetComputerName(buf, &bufsiz)) {\r
8775     /*DisplayError("Error getting host name", GetLastError());*/\r
8776     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8777   }\r
8778   return buf;\r
8779 }\r
8780 \r
8781 \r
8782 int\r
8783 ClockTimerRunning()\r
8784 {\r
8785   return clockTimerEvent != 0;\r
8786 }\r
8787 \r
8788 int\r
8789 StopClockTimer()\r
8790 {\r
8791   if (clockTimerEvent == 0) return FALSE;\r
8792   KillTimer(hwndMain, clockTimerEvent);\r
8793   clockTimerEvent = 0;\r
8794   return TRUE;\r
8795 }\r
8796 \r
8797 void\r
8798 StartClockTimer(long millisec)\r
8799 {\r
8800   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8801                              (UINT) millisec, NULL);\r
8802 }\r
8803 \r
8804 void\r
8805 DisplayWhiteClock(long timeRemaining, int highlight)\r
8806 {\r
8807   HDC hdc;\r
8808   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8809 \r
8810   if(appData.noGUI) return;\r
8811   hdc = GetDC(hwndMain);\r
8812   if (!IsIconic(hwndMain)) {\r
8813     DisplayAClock(hdc, timeRemaining, highlight, \r
8814                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8815   }\r
8816   if (highlight && iconCurrent == iconBlack) {\r
8817     iconCurrent = iconWhite;\r
8818     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8819     if (IsIconic(hwndMain)) {\r
8820       DrawIcon(hdc, 2, 2, iconCurrent);\r
8821     }\r
8822   }\r
8823   (void) ReleaseDC(hwndMain, hdc);\r
8824   if (hwndConsole)\r
8825     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8826 }\r
8827 \r
8828 void\r
8829 DisplayBlackClock(long timeRemaining, int highlight)\r
8830 {\r
8831   HDC hdc;\r
8832   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8833 \r
8834   if(appData.noGUI) return;\r
8835   hdc = GetDC(hwndMain);\r
8836   if (!IsIconic(hwndMain)) {\r
8837     DisplayAClock(hdc, timeRemaining, highlight, \r
8838                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8839   }\r
8840   if (highlight && iconCurrent == iconWhite) {\r
8841     iconCurrent = iconBlack;\r
8842     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8843     if (IsIconic(hwndMain)) {\r
8844       DrawIcon(hdc, 2, 2, iconCurrent);\r
8845     }\r
8846   }\r
8847   (void) ReleaseDC(hwndMain, hdc);\r
8848   if (hwndConsole)\r
8849     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8850 }\r
8851 \r
8852 \r
8853 int\r
8854 LoadGameTimerRunning()\r
8855 {\r
8856   return loadGameTimerEvent != 0;\r
8857 }\r
8858 \r
8859 int\r
8860 StopLoadGameTimer()\r
8861 {\r
8862   if (loadGameTimerEvent == 0) return FALSE;\r
8863   KillTimer(hwndMain, loadGameTimerEvent);\r
8864   loadGameTimerEvent = 0;\r
8865   return TRUE;\r
8866 }\r
8867 \r
8868 void\r
8869 StartLoadGameTimer(long millisec)\r
8870 {\r
8871   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8872                                 (UINT) millisec, NULL);\r
8873 }\r
8874 \r
8875 void\r
8876 AutoSaveGame()\r
8877 {\r
8878   char *defName;\r
8879   FILE *f;\r
8880   char fileTitle[MSG_SIZ];\r
8881 \r
8882   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8883   f = OpenFileDialog(hwndMain, "a", defName,\r
8884                      appData.oldSaveStyle ? "gam" : "pgn",\r
8885                      GAME_FILT, \r
8886                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8887   if (f != NULL) {\r
8888     SaveGame(f, 0, "");\r
8889     fclose(f);\r
8890   }\r
8891 }\r
8892 \r
8893 \r
8894 void\r
8895 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8896 {\r
8897   if (delayedTimerEvent != 0) {\r
8898     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8899       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8900     }\r
8901     KillTimer(hwndMain, delayedTimerEvent);\r
8902     delayedTimerEvent = 0;\r
8903     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8904     delayedTimerCallback();\r
8905   }\r
8906   delayedTimerCallback = cb;\r
8907   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8908                                 (UINT) millisec, NULL);\r
8909 }\r
8910 \r
8911 DelayedEventCallback\r
8912 GetDelayedEvent()\r
8913 {\r
8914   if (delayedTimerEvent) {\r
8915     return delayedTimerCallback;\r
8916   } else {\r
8917     return NULL;\r
8918   }\r
8919 }\r
8920 \r
8921 void\r
8922 CancelDelayedEvent()\r
8923 {\r
8924   if (delayedTimerEvent) {\r
8925     KillTimer(hwndMain, delayedTimerEvent);\r
8926     delayedTimerEvent = 0;\r
8927   }\r
8928 }\r
8929 \r
8930 DWORD GetWin32Priority(int nice)\r
8931 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8932 /*\r
8933 REALTIME_PRIORITY_CLASS     0x00000100\r
8934 HIGH_PRIORITY_CLASS         0x00000080\r
8935 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
8936 NORMAL_PRIORITY_CLASS       0x00000020\r
8937 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
8938 IDLE_PRIORITY_CLASS         0x00000040\r
8939 */\r
8940         if (nice < -15) return 0x00000080;\r
8941         if (nice < 0)   return 0x00008000;\r
8942         if (nice == 0)  return 0x00000020;\r
8943         if (nice < 15)  return 0x00004000;\r
8944         return 0x00000040;\r
8945 }\r
8946 \r
8947 void RunCommand(char *cmdLine)\r
8948 {\r
8949   /* Now create the child process. */\r
8950   STARTUPINFO siStartInfo;\r
8951   PROCESS_INFORMATION piProcInfo;\r
8952 \r
8953   siStartInfo.cb = sizeof(STARTUPINFO);\r
8954   siStartInfo.lpReserved = NULL;\r
8955   siStartInfo.lpDesktop = NULL;\r
8956   siStartInfo.lpTitle = NULL;\r
8957   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8958   siStartInfo.cbReserved2 = 0;\r
8959   siStartInfo.lpReserved2 = NULL;\r
8960   siStartInfo.hStdInput = NULL;\r
8961   siStartInfo.hStdOutput = NULL;\r
8962   siStartInfo.hStdError = NULL;\r
8963 \r
8964   CreateProcess(NULL,\r
8965                 cmdLine,           /* command line */\r
8966                 NULL,      /* process security attributes */\r
8967                 NULL,      /* primary thread security attrs */\r
8968                 TRUE,      /* handles are inherited */\r
8969                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8970                 NULL,      /* use parent's environment */\r
8971                 NULL,\r
8972                 &siStartInfo, /* STARTUPINFO pointer */\r
8973                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
8974 \r
8975   CloseHandle(piProcInfo.hThread);\r
8976 }\r
8977 \r
8978 /* Start a child process running the given program.\r
8979    The process's standard output can be read from "from", and its\r
8980    standard input can be written to "to".\r
8981    Exit with fatal error if anything goes wrong.\r
8982    Returns an opaque pointer that can be used to destroy the process\r
8983    later.\r
8984 */\r
8985 int\r
8986 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8987 {\r
8988 #define BUFSIZE 4096\r
8989 \r
8990   HANDLE hChildStdinRd, hChildStdinWr,\r
8991     hChildStdoutRd, hChildStdoutWr;\r
8992   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8993   SECURITY_ATTRIBUTES saAttr;\r
8994   BOOL fSuccess;\r
8995   PROCESS_INFORMATION piProcInfo;\r
8996   STARTUPINFO siStartInfo;\r
8997   ChildProc *cp;\r
8998   char buf[MSG_SIZ];\r
8999   DWORD err;\r
9000 \r
9001   if (appData.debugMode) {\r
9002     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9003   }\r
9004 \r
9005   *pr = NoProc;\r
9006 \r
9007   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9008   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9009   saAttr.bInheritHandle = TRUE;\r
9010   saAttr.lpSecurityDescriptor = NULL;\r
9011 \r
9012   /*\r
9013    * The steps for redirecting child's STDOUT:\r
9014    *     1. Create anonymous pipe to be STDOUT for child.\r
9015    *     2. Create a noninheritable duplicate of read handle,\r
9016    *         and close the inheritable read handle.\r
9017    */\r
9018 \r
9019   /* Create a pipe for the child's STDOUT. */\r
9020   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9021     return GetLastError();\r
9022   }\r
9023 \r
9024   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9025   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9026                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9027                              FALSE,     /* not inherited */\r
9028                              DUPLICATE_SAME_ACCESS);\r
9029   if (! fSuccess) {\r
9030     return GetLastError();\r
9031   }\r
9032   CloseHandle(hChildStdoutRd);\r
9033 \r
9034   /*\r
9035    * The steps for redirecting child's STDIN:\r
9036    *     1. Create anonymous pipe to be STDIN for child.\r
9037    *     2. Create a noninheritable duplicate of write handle,\r
9038    *         and close the inheritable write handle.\r
9039    */\r
9040 \r
9041   /* Create a pipe for the child's STDIN. */\r
9042   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9043     return GetLastError();\r
9044   }\r
9045 \r
9046   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9047   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9048                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9049                              FALSE,     /* not inherited */\r
9050                              DUPLICATE_SAME_ACCESS);\r
9051   if (! fSuccess) {\r
9052     return GetLastError();\r
9053   }\r
9054   CloseHandle(hChildStdinWr);\r
9055 \r
9056   /* Arrange to (1) look in dir for the child .exe file, and\r
9057    * (2) have dir be the child's working directory.  Interpret\r
9058    * dir relative to the directory WinBoard loaded from. */\r
9059   GetCurrentDirectory(MSG_SIZ, buf);\r
9060   SetCurrentDirectory(installDir);\r
9061   SetCurrentDirectory(dir);\r
9062 \r
9063   /* Now create the child process. */\r
9064 \r
9065   siStartInfo.cb = sizeof(STARTUPINFO);\r
9066   siStartInfo.lpReserved = NULL;\r
9067   siStartInfo.lpDesktop = NULL;\r
9068   siStartInfo.lpTitle = NULL;\r
9069   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9070   siStartInfo.cbReserved2 = 0;\r
9071   siStartInfo.lpReserved2 = NULL;\r
9072   siStartInfo.hStdInput = hChildStdinRd;\r
9073   siStartInfo.hStdOutput = hChildStdoutWr;\r
9074   siStartInfo.hStdError = hChildStdoutWr;\r
9075 \r
9076   fSuccess = CreateProcess(NULL,\r
9077                            cmdLine,        /* command line */\r
9078                            NULL,           /* process security attributes */\r
9079                            NULL,           /* primary thread security attrs */\r
9080                            TRUE,           /* handles are inherited */\r
9081                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9082                            NULL,           /* use parent's environment */\r
9083                            NULL,\r
9084                            &siStartInfo, /* STARTUPINFO pointer */\r
9085                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9086 \r
9087   err = GetLastError();\r
9088   SetCurrentDirectory(buf); /* return to prev directory */\r
9089   if (! fSuccess) {\r
9090     return err;\r
9091   }\r
9092 \r
9093   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9094     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9095     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9096   }\r
9097 \r
9098   /* Close the handles we don't need in the parent */\r
9099   CloseHandle(piProcInfo.hThread);\r
9100   CloseHandle(hChildStdinRd);\r
9101   CloseHandle(hChildStdoutWr);\r
9102 \r
9103   /* Prepare return value */\r
9104   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9105   cp->kind = CPReal;\r
9106   cp->hProcess = piProcInfo.hProcess;\r
9107   cp->pid = piProcInfo.dwProcessId;\r
9108   cp->hFrom = hChildStdoutRdDup;\r
9109   cp->hTo = hChildStdinWrDup;\r
9110 \r
9111   *pr = (void *) cp;\r
9112 \r
9113   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9114      2000 where engines sometimes don't see the initial command(s)\r
9115      from WinBoard and hang.  I don't understand how that can happen,\r
9116      but the Sleep is harmless, so I've put it in.  Others have also\r
9117      reported what may be the same problem, so hopefully this will fix\r
9118      it for them too.  */\r
9119   Sleep(500);\r
9120 \r
9121   return NO_ERROR;\r
9122 }\r
9123 \r
9124 \r
9125 void\r
9126 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9127 {\r
9128   ChildProc *cp; int result;\r
9129 \r
9130   cp = (ChildProc *) pr;\r
9131   if (cp == NULL) return;\r
9132 \r
9133   switch (cp->kind) {\r
9134   case CPReal:\r
9135     /* TerminateProcess is considered harmful, so... */\r
9136     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9137     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9138     /* The following doesn't work because the chess program\r
9139        doesn't "have the same console" as WinBoard.  Maybe\r
9140        we could arrange for this even though neither WinBoard\r
9141        nor the chess program uses a console for stdio? */\r
9142     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9143 \r
9144     /* [AS] Special termination modes for misbehaving programs... */\r
9145     if( signal == 9 ) { \r
9146         result = TerminateProcess( cp->hProcess, 0 );\r
9147 \r
9148         if ( appData.debugMode) {\r
9149             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9150         }\r
9151     }\r
9152     else if( signal == 10 ) {\r
9153         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9154 \r
9155         if( dw != WAIT_OBJECT_0 ) {\r
9156             result = TerminateProcess( cp->hProcess, 0 );\r
9157 \r
9158             if ( appData.debugMode) {\r
9159                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9160             }\r
9161 \r
9162         }\r
9163     }\r
9164 \r
9165     CloseHandle(cp->hProcess);\r
9166     break;\r
9167 \r
9168   case CPComm:\r
9169     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9170     break;\r
9171 \r
9172   case CPSock:\r
9173     closesocket(cp->sock);\r
9174     WSACleanup();\r
9175     break;\r
9176 \r
9177   case CPRcmd:\r
9178     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9179     closesocket(cp->sock);\r
9180     closesocket(cp->sock2);\r
9181     WSACleanup();\r
9182     break;\r
9183   }\r
9184   free(cp);\r
9185 }\r
9186 \r
9187 void\r
9188 InterruptChildProcess(ProcRef pr)\r
9189 {\r
9190   ChildProc *cp;\r
9191 \r
9192   cp = (ChildProc *) pr;\r
9193   if (cp == NULL) return;\r
9194   switch (cp->kind) {\r
9195   case CPReal:\r
9196     /* The following doesn't work because the chess program\r
9197        doesn't "have the same console" as WinBoard.  Maybe\r
9198        we could arrange for this even though neither WinBoard\r
9199        nor the chess program uses a console for stdio */\r
9200     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9201     break;\r
9202 \r
9203   case CPComm:\r
9204   case CPSock:\r
9205     /* Can't interrupt */\r
9206     break;\r
9207 \r
9208   case CPRcmd:\r
9209     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9210     break;\r
9211   }\r
9212 }\r
9213 \r
9214 \r
9215 int\r
9216 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9217 {\r
9218   char cmdLine[MSG_SIZ];\r
9219 \r
9220   if (port[0] == NULLCHAR) {\r
9221     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9222   } else {\r
9223     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9224   }\r
9225   return StartChildProcess(cmdLine, "", pr);\r
9226 }\r
9227 \r
9228 \r
9229 /* Code to open TCP sockets */\r
9230 \r
9231 int\r
9232 OpenTCP(char *host, char *port, ProcRef *pr)\r
9233 {\r
9234   ChildProc *cp;\r
9235   int err;\r
9236   SOCKET s;\r
9237 \r
9238   struct sockaddr_in sa, mysa;\r
9239   struct hostent FAR *hp;\r
9240   unsigned short uport;\r
9241   WORD wVersionRequested;\r
9242   WSADATA wsaData;\r
9243 \r
9244   /* Initialize socket DLL */\r
9245   wVersionRequested = MAKEWORD(1, 1);\r
9246   err = WSAStartup(wVersionRequested, &wsaData);\r
9247   if (err != 0) return err;\r
9248 \r
9249   /* Make socket */\r
9250   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9251     err = WSAGetLastError();\r
9252     WSACleanup();\r
9253     return err;\r
9254   }\r
9255 \r
9256   /* Bind local address using (mostly) don't-care values.\r
9257    */\r
9258   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9259   mysa.sin_family = AF_INET;\r
9260   mysa.sin_addr.s_addr = INADDR_ANY;\r
9261   uport = (unsigned short) 0;\r
9262   mysa.sin_port = htons(uport);\r
9263   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9264       == SOCKET_ERROR) {\r
9265     err = WSAGetLastError();\r
9266     WSACleanup();\r
9267     return err;\r
9268   }\r
9269 \r
9270   /* Resolve remote host name */\r
9271   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9272   if (!(hp = gethostbyname(host))) {\r
9273     unsigned int b0, b1, b2, b3;\r
9274 \r
9275     err = WSAGetLastError();\r
9276 \r
9277     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9278       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9279       hp->h_addrtype = AF_INET;\r
9280       hp->h_length = 4;\r
9281       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9282       hp->h_addr_list[0] = (char *) malloc(4);\r
9283       hp->h_addr_list[0][0] = (char) b0;\r
9284       hp->h_addr_list[0][1] = (char) b1;\r
9285       hp->h_addr_list[0][2] = (char) b2;\r
9286       hp->h_addr_list[0][3] = (char) b3;\r
9287     } else {\r
9288       WSACleanup();\r
9289       return err;\r
9290     }\r
9291   }\r
9292   sa.sin_family = hp->h_addrtype;\r
9293   uport = (unsigned short) atoi(port);\r
9294   sa.sin_port = htons(uport);\r
9295   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9296 \r
9297   /* Make connection */\r
9298   if (connect(s, (struct sockaddr *) &sa,\r
9299               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9300     err = WSAGetLastError();\r
9301     WSACleanup();\r
9302     return err;\r
9303   }\r
9304 \r
9305   /* Prepare return value */\r
9306   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9307   cp->kind = CPSock;\r
9308   cp->sock = s;\r
9309   *pr = (ProcRef *) cp;\r
9310 \r
9311   return NO_ERROR;\r
9312 }\r
9313 \r
9314 int\r
9315 OpenCommPort(char *name, ProcRef *pr)\r
9316 {\r
9317   HANDLE h;\r
9318   COMMTIMEOUTS ct;\r
9319   ChildProc *cp;\r
9320   char fullname[MSG_SIZ];\r
9321 \r
9322   if (*name != '\\')\r
9323     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9324   else\r
9325     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9326 \r
9327   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9328                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9329   if (h == (HANDLE) -1) {\r
9330     return GetLastError();\r
9331   }\r
9332   hCommPort = h;\r
9333 \r
9334   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9335 \r
9336   /* Accumulate characters until a 100ms pause, then parse */\r
9337   ct.ReadIntervalTimeout = 100;\r
9338   ct.ReadTotalTimeoutMultiplier = 0;\r
9339   ct.ReadTotalTimeoutConstant = 0;\r
9340   ct.WriteTotalTimeoutMultiplier = 0;\r
9341   ct.WriteTotalTimeoutConstant = 0;\r
9342   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9343 \r
9344   /* Prepare return value */\r
9345   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9346   cp->kind = CPComm;\r
9347   cp->hFrom = h;\r
9348   cp->hTo = h;\r
9349   *pr = (ProcRef *) cp;\r
9350 \r
9351   return NO_ERROR;\r
9352 }\r
9353 \r
9354 int\r
9355 OpenLoopback(ProcRef *pr)\r
9356 {\r
9357   DisplayFatalError(_("Not implemented"), 0, 1);\r
9358   return NO_ERROR;\r
9359 }\r
9360 \r
9361 \r
9362 int\r
9363 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9364 {\r
9365   ChildProc *cp;\r
9366   int err;\r
9367   SOCKET s, s2, s3;\r
9368   struct sockaddr_in sa, mysa;\r
9369   struct hostent FAR *hp;\r
9370   unsigned short uport;\r
9371   WORD wVersionRequested;\r
9372   WSADATA wsaData;\r
9373   int fromPort;\r
9374   char stderrPortStr[MSG_SIZ];\r
9375 \r
9376   /* Initialize socket DLL */\r
9377   wVersionRequested = MAKEWORD(1, 1);\r
9378   err = WSAStartup(wVersionRequested, &wsaData);\r
9379   if (err != 0) return err;\r
9380 \r
9381   /* Resolve remote host name */\r
9382   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9383   if (!(hp = gethostbyname(host))) {\r
9384     unsigned int b0, b1, b2, b3;\r
9385 \r
9386     err = WSAGetLastError();\r
9387 \r
9388     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9389       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9390       hp->h_addrtype = AF_INET;\r
9391       hp->h_length = 4;\r
9392       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9393       hp->h_addr_list[0] = (char *) malloc(4);\r
9394       hp->h_addr_list[0][0] = (char) b0;\r
9395       hp->h_addr_list[0][1] = (char) b1;\r
9396       hp->h_addr_list[0][2] = (char) b2;\r
9397       hp->h_addr_list[0][3] = (char) b3;\r
9398     } else {\r
9399       WSACleanup();\r
9400       return err;\r
9401     }\r
9402   }\r
9403   sa.sin_family = hp->h_addrtype;\r
9404   uport = (unsigned short) 514;\r
9405   sa.sin_port = htons(uport);\r
9406   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9407 \r
9408   /* Bind local socket to unused "privileged" port address\r
9409    */\r
9410   s = INVALID_SOCKET;\r
9411   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9412   mysa.sin_family = AF_INET;\r
9413   mysa.sin_addr.s_addr = INADDR_ANY;\r
9414   for (fromPort = 1023;; fromPort--) {\r
9415     if (fromPort < 0) {\r
9416       WSACleanup();\r
9417       return WSAEADDRINUSE;\r
9418     }\r
9419     if (s == INVALID_SOCKET) {\r
9420       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9421         err = WSAGetLastError();\r
9422         WSACleanup();\r
9423         return err;\r
9424       }\r
9425     }\r
9426     uport = (unsigned short) fromPort;\r
9427     mysa.sin_port = htons(uport);\r
9428     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9429         == SOCKET_ERROR) {\r
9430       err = WSAGetLastError();\r
9431       if (err == WSAEADDRINUSE) continue;\r
9432       WSACleanup();\r
9433       return err;\r
9434     }\r
9435     if (connect(s, (struct sockaddr *) &sa,\r
9436       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9437       err = WSAGetLastError();\r
9438       if (err == WSAEADDRINUSE) {\r
9439         closesocket(s);\r
9440         s = -1;\r
9441         continue;\r
9442       }\r
9443       WSACleanup();\r
9444       return err;\r
9445     }\r
9446     break;\r
9447   }\r
9448 \r
9449   /* Bind stderr local socket to unused "privileged" port address\r
9450    */\r
9451   s2 = INVALID_SOCKET;\r
9452   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9453   mysa.sin_family = AF_INET;\r
9454   mysa.sin_addr.s_addr = INADDR_ANY;\r
9455   for (fromPort = 1023;; fromPort--) {\r
9456     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9457     if (fromPort < 0) {\r
9458       (void) closesocket(s);\r
9459       WSACleanup();\r
9460       return WSAEADDRINUSE;\r
9461     }\r
9462     if (s2 == INVALID_SOCKET) {\r
9463       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9464         err = WSAGetLastError();\r
9465         closesocket(s);\r
9466         WSACleanup();\r
9467         return err;\r
9468       }\r
9469     }\r
9470     uport = (unsigned short) fromPort;\r
9471     mysa.sin_port = htons(uport);\r
9472     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9473         == SOCKET_ERROR) {\r
9474       err = WSAGetLastError();\r
9475       if (err == WSAEADDRINUSE) continue;\r
9476       (void) closesocket(s);\r
9477       WSACleanup();\r
9478       return err;\r
9479     }\r
9480     if (listen(s2, 1) == SOCKET_ERROR) {\r
9481       err = WSAGetLastError();\r
9482       if (err == WSAEADDRINUSE) {\r
9483         closesocket(s2);\r
9484         s2 = INVALID_SOCKET;\r
9485         continue;\r
9486       }\r
9487       (void) closesocket(s);\r
9488       (void) closesocket(s2);\r
9489       WSACleanup();\r
9490       return err;\r
9491     }\r
9492     break;\r
9493   }\r
9494   prevStderrPort = fromPort; // remember port used\r
9495   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9496 \r
9497   if (send(s, stderrPortStr, strlen(stderrPortStr) + 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 \r
9505   if (send(s, UserName(), strlen(UserName()) + 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 (*user == NULLCHAR) user = UserName();\r
9513   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9514     err = WSAGetLastError();\r
9515     (void) closesocket(s);\r
9516     (void) closesocket(s2);\r
9517     WSACleanup();\r
9518     return err;\r
9519   }\r
9520   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\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 \r
9528   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9529     err = WSAGetLastError();\r
9530     (void) closesocket(s);\r
9531     (void) closesocket(s2);\r
9532     WSACleanup();\r
9533     return err;\r
9534   }\r
9535   (void) closesocket(s2);  /* Stop listening */\r
9536 \r
9537   /* Prepare return value */\r
9538   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9539   cp->kind = CPRcmd;\r
9540   cp->sock = s;\r
9541   cp->sock2 = s3;\r
9542   *pr = (ProcRef *) cp;\r
9543 \r
9544   return NO_ERROR;\r
9545 }\r
9546 \r
9547 \r
9548 InputSourceRef\r
9549 AddInputSource(ProcRef pr, int lineByLine,\r
9550                InputCallback func, VOIDSTAR closure)\r
9551 {\r
9552   InputSource *is, *is2 = NULL;\r
9553   ChildProc *cp = (ChildProc *) pr;\r
9554 \r
9555   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9556   is->lineByLine = lineByLine;\r
9557   is->func = func;\r
9558   is->closure = closure;\r
9559   is->second = NULL;\r
9560   is->next = is->buf;\r
9561   if (pr == NoProc) {\r
9562     is->kind = CPReal;\r
9563     consoleInputSource = is;\r
9564   } else {\r
9565     is->kind = cp->kind;\r
9566     /* \r
9567         [AS] Try to avoid a race condition if the thread is given control too early:\r
9568         we create all threads suspended so that the is->hThread variable can be\r
9569         safely assigned, then let the threads start with ResumeThread.\r
9570     */\r
9571     switch (cp->kind) {\r
9572     case CPReal:\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) NonOvlInputThread,\r
9577                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9578       break;\r
9579 \r
9580     case CPComm:\r
9581       is->hFile = cp->hFrom;\r
9582       cp->hFrom = NULL; /* now owned by InputThread */\r
9583       is->hThread =\r
9584         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9585                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9586       break;\r
9587 \r
9588     case CPSock:\r
9589       is->sock = cp->sock;\r
9590       is->hThread =\r
9591         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9592                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9593       break;\r
9594 \r
9595     case CPRcmd:\r
9596       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9597       *is2 = *is;\r
9598       is->sock = cp->sock;\r
9599       is->second = is2;\r
9600       is2->sock = cp->sock2;\r
9601       is2->second = is2;\r
9602       is->hThread =\r
9603         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9604                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9605       is2->hThread =\r
9606         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9607                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9608       break;\r
9609     }\r
9610 \r
9611     if( is->hThread != NULL ) {\r
9612         ResumeThread( is->hThread );\r
9613     }\r
9614 \r
9615     if( is2 != NULL && is2->hThread != NULL ) {\r
9616         ResumeThread( is2->hThread );\r
9617     }\r
9618   }\r
9619 \r
9620   return (InputSourceRef) is;\r
9621 }\r
9622 \r
9623 void\r
9624 RemoveInputSource(InputSourceRef isr)\r
9625 {\r
9626   InputSource *is;\r
9627 \r
9628   is = (InputSource *) isr;\r
9629   is->hThread = NULL;  /* tell thread to stop */\r
9630   CloseHandle(is->hThread);\r
9631   if (is->second != NULL) {\r
9632     is->second->hThread = NULL;\r
9633     CloseHandle(is->second->hThread);\r
9634   }\r
9635 }\r
9636 \r
9637 int no_wrap(char *message, int count)\r
9638 {\r
9639     ConsoleOutput(message, count, FALSE);\r
9640     return count;\r
9641 }\r
9642 \r
9643 int\r
9644 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9645 {\r
9646   DWORD dOutCount;\r
9647   int outCount = SOCKET_ERROR;\r
9648   ChildProc *cp = (ChildProc *) pr;\r
9649   static OVERLAPPED ovl;\r
9650   static int line = 0;\r
9651 \r
9652   if (pr == NoProc)\r
9653   {\r
9654     if (appData.noJoin || !appData.useInternalWrap)\r
9655       return no_wrap(message, count);\r
9656     else\r
9657     {\r
9658       int width = get_term_width();\r
9659       int len = wrap(NULL, message, count, width, &line);\r
9660       char *msg = malloc(len);\r
9661       int dbgchk;\r
9662 \r
9663       if (!msg)\r
9664         return no_wrap(message, count);\r
9665       else\r
9666       {\r
9667         dbgchk = wrap(msg, message, count, width, &line);\r
9668         if (dbgchk != len && appData.debugMode)\r
9669             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9670         ConsoleOutput(msg, len, FALSE);\r
9671         free(msg);\r
9672         return len;\r
9673       }\r
9674     }\r
9675   }\r
9676 \r
9677   if (ovl.hEvent == NULL) {\r
9678     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9679   }\r
9680   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9681 \r
9682   switch (cp->kind) {\r
9683   case CPSock:\r
9684   case CPRcmd:\r
9685     outCount = send(cp->sock, message, count, 0);\r
9686     if (outCount == SOCKET_ERROR) {\r
9687       *outError = WSAGetLastError();\r
9688     } else {\r
9689       *outError = NO_ERROR;\r
9690     }\r
9691     break;\r
9692 \r
9693   case CPReal:\r
9694     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9695                   &dOutCount, NULL)) {\r
9696       *outError = NO_ERROR;\r
9697       outCount = (int) dOutCount;\r
9698     } else {\r
9699       *outError = GetLastError();\r
9700     }\r
9701     break;\r
9702 \r
9703   case CPComm:\r
9704     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9705                             &dOutCount, &ovl);\r
9706     if (*outError == NO_ERROR) {\r
9707       outCount = (int) dOutCount;\r
9708     }\r
9709     break;\r
9710   }\r
9711   return outCount;\r
9712 }\r
9713 \r
9714 void\r
9715 DoSleep(int n)\r
9716 {\r
9717     if(n != 0) Sleep(n);\r
9718 }\r
9719 \r
9720 int\r
9721 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9722                        long msdelay)\r
9723 {\r
9724   /* Ignore delay, not implemented for WinBoard */\r
9725   return OutputToProcess(pr, message, count, outError);\r
9726 }\r
9727 \r
9728 \r
9729 void\r
9730 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9731                         char *buf, int count, int error)\r
9732 {\r
9733   DisplayFatalError(_("Not implemented"), 0, 1);\r
9734 }\r
9735 \r
9736 /* see wgamelist.c for Game List functions */\r
9737 /* see wedittags.c for Edit Tags functions */\r
9738 \r
9739 \r
9740 VOID\r
9741 ICSInitScript()\r
9742 {\r
9743   FILE *f;\r
9744   char buf[MSG_SIZ];\r
9745   char *dummy;\r
9746 \r
9747   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9748     f = fopen(buf, "r");\r
9749     if (f != NULL) {\r
9750       ProcessICSInitScript(f);\r
9751       fclose(f);\r
9752     }\r
9753   }\r
9754 }\r
9755 \r
9756 \r
9757 VOID\r
9758 StartAnalysisClock()\r
9759 {\r
9760   if (analysisTimerEvent) return;\r
9761   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9762                                         (UINT) 2000, NULL);\r
9763 }\r
9764 \r
9765 VOID\r
9766 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9767 {\r
9768   highlightInfo.sq[0].x = fromX;\r
9769   highlightInfo.sq[0].y = fromY;\r
9770   highlightInfo.sq[1].x = toX;\r
9771   highlightInfo.sq[1].y = toY;\r
9772 }\r
9773 \r
9774 VOID\r
9775 ClearHighlights()\r
9776 {\r
9777   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9778     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9779 }\r
9780 \r
9781 VOID\r
9782 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9783 {\r
9784   premoveHighlightInfo.sq[0].x = fromX;\r
9785   premoveHighlightInfo.sq[0].y = fromY;\r
9786   premoveHighlightInfo.sq[1].x = toX;\r
9787   premoveHighlightInfo.sq[1].y = toY;\r
9788 }\r
9789 \r
9790 VOID\r
9791 ClearPremoveHighlights()\r
9792 {\r
9793   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9794     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9795 }\r
9796 \r
9797 VOID\r
9798 ShutDownFrontEnd()\r
9799 {\r
9800   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9801   DeleteClipboardTempFiles();\r
9802 }\r
9803 \r
9804 void\r
9805 BoardToTop()\r
9806 {\r
9807     if (IsIconic(hwndMain))\r
9808       ShowWindow(hwndMain, SW_RESTORE);\r
9809 \r
9810     SetActiveWindow(hwndMain);\r
9811 }\r
9812 \r
9813 /*\r
9814  * Prototypes for animation support routines\r
9815  */\r
9816 static void ScreenSquare(int column, int row, POINT * pt);\r
9817 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9818      POINT frames[], int * nFrames);\r
9819 \r
9820 \r
9821 #define kFactor 4\r
9822 \r
9823 void\r
9824 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9825 {       // [HGM] atomic: animate blast wave\r
9826         int i;\r
9827 \r
9828         explodeInfo.fromX = fromX;\r
9829         explodeInfo.fromY = fromY;\r
9830         explodeInfo.toX = toX;\r
9831         explodeInfo.toY = toY;\r
9832         for(i=1; i<4*kFactor; i++) {\r
9833             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9834             DrawPosition(FALSE, board);\r
9835             Sleep(appData.animSpeed);\r
9836         }\r
9837         explodeInfo.radius = 0;\r
9838         DrawPosition(TRUE, board);\r
9839 }\r
9840 \r
9841 void\r
9842 AnimateMove(board, fromX, fromY, toX, toY)\r
9843      Board board;\r
9844      int fromX;\r
9845      int fromY;\r
9846      int toX;\r
9847      int toY;\r
9848 {\r
9849   ChessSquare piece;\r
9850   POINT start, finish, mid;\r
9851   POINT frames[kFactor * 2 + 1];\r
9852   int nFrames, n;\r
9853 \r
9854   if (!appData.animate) return;\r
9855   if (doingSizing) return;\r
9856   if (fromY < 0 || fromX < 0) return;\r
9857   piece = board[fromY][fromX];\r
9858   if (piece >= EmptySquare) return;\r
9859 \r
9860   ScreenSquare(fromX, fromY, &start);\r
9861   ScreenSquare(toX, toY, &finish);\r
9862 \r
9863   /* All moves except knight jumps move in straight line */\r
9864   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9865     mid.x = start.x + (finish.x - start.x) / 2;\r
9866     mid.y = start.y + (finish.y - start.y) / 2;\r
9867   } else {\r
9868     /* Knight: make straight movement then diagonal */\r
9869     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9870        mid.x = start.x + (finish.x - start.x) / 2;\r
9871        mid.y = start.y;\r
9872      } else {\r
9873        mid.x = start.x;\r
9874        mid.y = start.y + (finish.y - start.y) / 2;\r
9875      }\r
9876   }\r
9877   \r
9878   /* Don't use as many frames for very short moves */\r
9879   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9880     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9881   else\r
9882     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9883 \r
9884   animInfo.from.x = fromX;\r
9885   animInfo.from.y = fromY;\r
9886   animInfo.to.x = toX;\r
9887   animInfo.to.y = toY;\r
9888   animInfo.lastpos = start;\r
9889   animInfo.piece = piece;\r
9890   for (n = 0; n < nFrames; n++) {\r
9891     animInfo.pos = frames[n];\r
9892     DrawPosition(FALSE, NULL);\r
9893     animInfo.lastpos = animInfo.pos;\r
9894     Sleep(appData.animSpeed);\r
9895   }\r
9896   animInfo.pos = finish;\r
9897   DrawPosition(FALSE, NULL);\r
9898   animInfo.piece = EmptySquare;\r
9899   Explode(board, fromX, fromY, toX, toY);\r
9900 }\r
9901 \r
9902 /*      Convert board position to corner of screen rect and color       */\r
9903 \r
9904 static void\r
9905 ScreenSquare(column, row, pt)\r
9906      int column; int row; POINT * pt;\r
9907 {\r
9908   if (flipView) {\r
9909     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9910     pt->y = lineGap + row * (squareSize + lineGap);\r
9911   } else {\r
9912     pt->x = lineGap + column * (squareSize + lineGap);\r
9913     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9914   }\r
9915 }\r
9916 \r
9917 /*      Generate a series of frame coords from start->mid->finish.\r
9918         The movement rate doubles until the half way point is\r
9919         reached, then halves back down to the final destination,\r
9920         which gives a nice slow in/out effect. The algorithmn\r
9921         may seem to generate too many intermediates for short\r
9922         moves, but remember that the purpose is to attract the\r
9923         viewers attention to the piece about to be moved and\r
9924         then to where it ends up. Too few frames would be less\r
9925         noticeable.                                             */\r
9926 \r
9927 static void\r
9928 Tween(start, mid, finish, factor, frames, nFrames)\r
9929      POINT * start; POINT * mid;\r
9930      POINT * finish; int factor;\r
9931      POINT frames[]; int * nFrames;\r
9932 {\r
9933   int n, fraction = 1, count = 0;\r
9934 \r
9935   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9936   for (n = 0; n < factor; n++)\r
9937     fraction *= 2;\r
9938   for (n = 0; n < factor; n++) {\r
9939     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9940     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9941     count ++;\r
9942     fraction = fraction / 2;\r
9943   }\r
9944   \r
9945   /* Midpoint */\r
9946   frames[count] = *mid;\r
9947   count ++;\r
9948   \r
9949   /* Slow out, stepping 1/2, then 1/4, ... */\r
9950   fraction = 2;\r
9951   for (n = 0; n < factor; n++) {\r
9952     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9953     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9954     count ++;\r
9955     fraction = fraction * 2;\r
9956   }\r
9957   *nFrames = count;\r
9958 }\r
9959 \r
9960 void\r
9961 SettingsPopUp(ChessProgramState *cps)\r
9962 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
9963       EngineOptionsPopup(savedHwnd, cps);\r
9964 }\r
9965 \r
9966 int flock(int fid, int code)\r
9967 {\r
9968     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
9969     OVERLAPPED ov;\r
9970     ov.hEvent = NULL;\r
9971     ov.Offset = 0;\r
9972     ov.OffsetHigh = 0;\r
9973     switch(code) {\r
9974       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
9975       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
9976       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
9977       default: return -1;\r
9978     }\r
9979     return 0;\r
9980 }\r