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