WinBoard multi-monitor support
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013 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 \r
226 \r
227 \r
228 #if defined(_winmajor)\r
229 #define oldDialog (_winmajor < 4)\r
230 #else\r
231 #define oldDialog 0\r
232 #endif\r
233 #endif\r
234 \r
235 #define INTERNATIONAL\r
236 \r
237 #ifdef INTERNATIONAL\r
238 #  define _(s) T_(s)\r
239 #  define N_(s) s\r
240 #else\r
241 #  define _(s) s\r
242 #  define N_(s) s\r
243 #  define T_(s) s\r
244 #  define Translate(x, y)\r
245 #  define LoadLanguageFile(s)\r
246 #endif\r
247 \r
248 #ifdef INTERNATIONAL\r
249 \r
250 Boolean barbaric; // flag indicating if translation is needed\r
251 \r
252 // list of item numbers used in each dialog (used to alter language at run time)\r
253 \r
254 #define ABOUTBOX -1  /* not sure why these are needed */\r
255 #define ABOUTBOX2 -1\r
256 \r
257 int dialogItems[][42] = {\r
258 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
259 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
260   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
261 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
262   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
263 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
264   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
265 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
266 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
267   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
268 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
269 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
270   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
271 { ABOUTBOX2, IDC_ChessBoard }, \r
272 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
273   OPT_GameListClose, IDC_GameListDoFilter }, \r
274 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
275 { DLG_Error, IDOK }, \r
276 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
277   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
278 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
279 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
280   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
281   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
282 { DLG_IndexNumber, IDC_Index }, \r
283 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
284 { DLG_TypeInName, IDOK, IDCANCEL }, \r
285 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
286   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
287 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
288   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
289   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
290   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
291   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
292   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
293   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
294 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
295   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
296   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
297   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
298   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
299   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
300   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
301   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
302   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
303 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
304   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
305   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
306   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
307   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
308   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
309   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
310   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
311 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
312   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
313   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
314   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
315   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
316   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
317   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
318   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
319   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
320 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
321   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
322   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
323   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
324   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
325 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
326 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
327   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
328 { DLG_MoveHistory }, \r
329 { DLG_EvalGraph }, \r
330 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
331 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
332 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
333   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
334   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
335   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
336 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
337   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
338   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
339 { 0 }\r
340 };\r
341 \r
342 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
343 static int lastChecked;\r
344 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
345 extern int tinyLayout;\r
346 extern char * menuBarText[][10];\r
347 \r
348 void\r
349 LoadLanguageFile(char *name)\r
350 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
351     FILE *f;\r
352     int i=0, j=0, n=0, k;\r
353     char buf[MSG_SIZ];\r
354 \r
355     if(!name || name[0] == NULLCHAR) return;\r
356       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
357     appData.language = oldLanguage;\r
358     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
359     if((f = fopen(buf, "r")) == NULL) return;\r
360     while((k = fgetc(f)) != EOF) {\r
361         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
362         languageBuf[i] = k;\r
363         if(k == '\n') {\r
364             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
365                 char *p;\r
366                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
367                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
368                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
369                         english[j] = languageBuf + n + 1; *p = 0;\r
370                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
371 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
372                     }\r
373                 }\r
374             }\r
375             n = i + 1;\r
376         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
377             switch(k) {\r
378               case 'n': k = '\n'; break;\r
379               case 'r': k = '\r'; break;\r
380               case 't': k = '\t'; break;\r
381             }\r
382             languageBuf[--i] = k;\r
383         }\r
384         i++;\r
385     }\r
386     fclose(f);\r
387     barbaric = (j != 0);\r
388     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
389 }\r
390 \r
391 char *\r
392 T_(char *s)\r
393 {   // return the translation of the given string\r
394     // efficiency can be improved a lot...\r
395     int i=0;\r
396     static char buf[MSG_SIZ];\r
397 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
398     if(!barbaric) return s;\r
399     if(!s) return ""; // sanity\r
400     while(english[i]) {\r
401         if(!strcmp(s, english[i])) return foreign[i];\r
402         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
403             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
404             return buf;\r
405         }\r
406         i++;\r
407     }\r
408     return s;\r
409 }\r
410 \r
411 void\r
412 Translate(HWND hDlg, int dialogID)\r
413 {   // translate all text items in the given dialog\r
414     int i=0, j, k;\r
415     char buf[MSG_SIZ], *s;\r
416     if(!barbaric) return;\r
417     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
418     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
419     GetWindowText( hDlg, buf, MSG_SIZ );\r
420     s = T_(buf);\r
421     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
422     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
423         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
424         if(strlen(buf) == 0) continue;\r
425         s = T_(buf);\r
426         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
427     }\r
428 }\r
429 \r
430 HMENU\r
431 TranslateOneMenu(int i, HMENU subMenu)\r
432 {\r
433     int j;\r
434     static MENUITEMINFO info;\r
435 \r
436     info.cbSize = sizeof(MENUITEMINFO);\r
437     info.fMask = MIIM_STATE | MIIM_TYPE;\r
438           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
439             char buf[MSG_SIZ];\r
440             info.dwTypeData = buf;\r
441             info.cch = sizeof(buf);\r
442             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
443             if(i < 10) {\r
444                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
445                 else menuText[i][j] = strdup(buf); // remember original on first change\r
446             }\r
447             if(buf[0] == NULLCHAR) continue;\r
448             info.dwTypeData = T_(buf);\r
449             info.cch = strlen(buf)+1;\r
450             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
451           }\r
452     return subMenu;\r
453 }\r
454 \r
455 void\r
456 TranslateMenus(int addLanguage)\r
457 {\r
458     int i;\r
459     WIN32_FIND_DATA fileData;\r
460     HANDLE hFind;\r
461 #define IDM_English 1970\r
462     if(1) {\r
463         HMENU mainMenu = GetMenu(hwndMain);\r
464         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
465           HMENU subMenu = GetSubMenu(mainMenu, i);\r
466           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
467                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
468           TranslateOneMenu(i, subMenu);\r
469         }\r
470         DrawMenuBar(hwndMain);\r
471     }\r
472 \r
473     if(!addLanguage) return;\r
474     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
475         HMENU mainMenu = GetMenu(hwndMain);\r
476         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
477         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
478         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
479         i = 0; lastChecked = IDM_English;\r
480         do {\r
481             char *p, *q = fileData.cFileName;\r
482             int checkFlag = MF_UNCHECKED;\r
483             languageFile[i] = strdup(q);\r
484             if(barbaric && !strcmp(oldLanguage, q)) {\r
485                 checkFlag = MF_CHECKED;\r
486                 lastChecked = IDM_English + i + 1;\r
487                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
488             }\r
489             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
490             p = strstr(fileData.cFileName, ".lng");\r
491             if(p) *p = 0;\r
492             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
493         } while(FindNextFile(hFind, &fileData));\r
494         FindClose(hFind);\r
495     }\r
496 }\r
497 \r
498 #endif\r
499 \r
500 #define IDM_RecentEngines 3000\r
501 \r
502 void\r
503 RecentEngineMenu (char *s)\r
504 {\r
505     if(appData.icsActive) return;\r
506     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
507         HMENU mainMenu = GetMenu(hwndMain);\r
508         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
509         int i=IDM_RecentEngines;\r
510         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
511         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
512         while(*s) {\r
513           char *p = strchr(s, '\n');\r
514           if(p == NULL) return; // malformed!\r
515           *p = NULLCHAR;\r
516           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
517           *p = '\n';\r
518           s = p+1;\r
519         }\r
520     }\r
521 }\r
522 \r
523 \r
524 typedef struct {\r
525   char *name;\r
526   int squareSize;\r
527   int lineGap;\r
528   int smallLayout;\r
529   int tinyLayout;\r
530   int cliWidth, cliHeight;\r
531 } SizeInfo;\r
532 \r
533 SizeInfo sizeInfo[] = \r
534 {\r
535   { "tiny",     21, 0, 1, 1, 0, 0 },\r
536   { "teeny",    25, 1, 1, 1, 0, 0 },\r
537   { "dinky",    29, 1, 1, 1, 0, 0 },\r
538   { "petite",   33, 1, 1, 1, 0, 0 },\r
539   { "slim",     37, 2, 1, 0, 0, 0 },\r
540   { "small",    40, 2, 1, 0, 0, 0 },\r
541   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
542   { "middling", 49, 2, 0, 0, 0, 0 },\r
543   { "average",  54, 2, 0, 0, 0, 0 },\r
544   { "moderate", 58, 3, 0, 0, 0, 0 },\r
545   { "medium",   64, 3, 0, 0, 0, 0 },\r
546   { "bulky",    72, 3, 0, 0, 0, 0 },\r
547   { "large",    80, 3, 0, 0, 0, 0 },\r
548   { "big",      87, 3, 0, 0, 0, 0 },\r
549   { "huge",     95, 3, 0, 0, 0, 0 },\r
550   { "giant",    108, 3, 0, 0, 0, 0 },\r
551   { "colossal", 116, 4, 0, 0, 0, 0 },\r
552   { "titanic",  129, 4, 0, 0, 0, 0 },\r
553   { NULL, 0, 0, 0, 0, 0, 0 }\r
554 };\r
555 \r
556 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
557 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
558 {\r
559   { 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
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576   { 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
577 };\r
578 \r
579 MyFont *font[NUM_SIZES][NUM_FONTS];\r
580 \r
581 typedef struct {\r
582   char *label;\r
583   int id;\r
584   HWND hwnd;\r
585   WNDPROC wndproc;\r
586 } MyButtonDesc;\r
587 \r
588 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
589 #define N_BUTTONS 5\r
590 \r
591 MyButtonDesc buttonDesc[N_BUTTONS] =\r
592 {\r
593   {"<<", IDM_ToStart, NULL, NULL},\r
594   {"<", IDM_Backward, NULL, NULL},\r
595   {"P", IDM_Pause, NULL, NULL},\r
596   {">", IDM_Forward, NULL, NULL},\r
597   {">>", IDM_ToEnd, NULL, NULL},\r
598 };\r
599 \r
600 int tinyLayout = 0, smallLayout = 0;\r
601 #define MENU_BAR_ITEMS 9\r
602 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
603   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
604   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
605 };\r
606 \r
607 \r
608 MySound sounds[(int)NSoundClasses];\r
609 MyTextAttribs textAttribs[(int)NColorClasses];\r
610 \r
611 MyColorizeAttribs colorizeAttribs[] = {\r
612   { (COLORREF)0, 0, N_("Shout Text") },\r
613   { (COLORREF)0, 0, N_("SShout/CShout") },\r
614   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
615   { (COLORREF)0, 0, N_("Channel Text") },\r
616   { (COLORREF)0, 0, N_("Kibitz Text") },\r
617   { (COLORREF)0, 0, N_("Tell Text") },\r
618   { (COLORREF)0, 0, N_("Challenge Text") },\r
619   { (COLORREF)0, 0, N_("Request Text") },\r
620   { (COLORREF)0, 0, N_("Seek Text") },\r
621   { (COLORREF)0, 0, N_("Normal Text") },\r
622   { (COLORREF)0, 0, N_("None") }\r
623 };\r
624 \r
625 \r
626 \r
627 static char *commentTitle;\r
628 static char *commentText;\r
629 static int commentIndex;\r
630 static Boolean editComment = FALSE;\r
631 \r
632 \r
633 char errorTitle[MSG_SIZ];\r
634 char errorMessage[2*MSG_SIZ];\r
635 HWND errorDialog = NULL;\r
636 BOOLEAN moveErrorMessageUp = FALSE;\r
637 BOOLEAN consoleEcho = TRUE;\r
638 CHARFORMAT consoleCF;\r
639 COLORREF consoleBackgroundColor;\r
640 \r
641 char *programVersion;\r
642 \r
643 #define CPReal 1\r
644 #define CPComm 2\r
645 #define CPSock 3\r
646 #define CPRcmd 4\r
647 typedef int CPKind;\r
648 \r
649 typedef struct {\r
650   CPKind kind;\r
651   HANDLE hProcess;\r
652   DWORD pid;\r
653   HANDLE hTo;\r
654   HANDLE hFrom;\r
655   SOCKET sock;\r
656   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
657 } ChildProc;\r
658 \r
659 #define INPUT_SOURCE_BUF_SIZE 4096\r
660 \r
661 typedef struct _InputSource {\r
662   CPKind kind;\r
663   HANDLE hFile;\r
664   SOCKET sock;\r
665   int lineByLine;\r
666   HANDLE hThread;\r
667   DWORD id;\r
668   char buf[INPUT_SOURCE_BUF_SIZE];\r
669   char *next;\r
670   DWORD count;\r
671   int error;\r
672   InputCallback func;\r
673   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
674   VOIDSTAR closure;\r
675 } InputSource;\r
676 \r
677 InputSource *consoleInputSource;\r
678 \r
679 DCB dcb;\r
680 \r
681 /* forward */\r
682 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
683 VOID ConsoleCreate();\r
684 LRESULT CALLBACK\r
685   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
686 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
687 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
688 VOID ParseCommSettings(char *arg, DCB *dcb);\r
689 LRESULT CALLBACK\r
690   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
691 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
692 void ParseIcsTextMenu(char *icsTextMenuString);\r
693 VOID PopUpNameDialog(char firstchar);\r
694 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
695 \r
696 /* [AS] */\r
697 int NewGameFRC();\r
698 int GameListOptions();\r
699 \r
700 int dummy; // [HGM] for obsolete args\r
701 \r
702 HWND hwndMain = NULL;        /* root window*/\r
703 HWND hwndConsole = NULL;\r
704 HWND commentDialog = NULL;\r
705 HWND moveHistoryDialog = NULL;\r
706 HWND evalGraphDialog = NULL;\r
707 HWND engineOutputDialog = NULL;\r
708 HWND gameListDialog = NULL;\r
709 HWND editTagsDialog = NULL;\r
710 \r
711 int commentUp = FALSE;\r
712 \r
713 WindowPlacement wpMain;\r
714 WindowPlacement wpConsole;\r
715 WindowPlacement wpComment;\r
716 WindowPlacement wpMoveHistory;\r
717 WindowPlacement wpEvalGraph;\r
718 WindowPlacement wpEngineOutput;\r
719 WindowPlacement wpGameList;\r
720 WindowPlacement wpTags;\r
721 \r
722 VOID EngineOptionsPopup(); // [HGM] settings\r
723 \r
724 VOID GothicPopUp(char *title, VariantClass variant);\r
725 /*\r
726  * Setting "frozen" should disable all user input other than deleting\r
727  * the window.  We do this while engines are initializing themselves.\r
728  */\r
729 static int frozen = 0;\r
730 static int oldMenuItemState[MENU_BAR_ITEMS];\r
731 void FreezeUI()\r
732 {\r
733   HMENU hmenu;\r
734   int i;\r
735 \r
736   if (frozen) return;\r
737   frozen = 1;\r
738   hmenu = GetMenu(hwndMain);\r
739   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
740     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
741   }\r
742   DrawMenuBar(hwndMain);\r
743 }\r
744 \r
745 /* Undo a FreezeUI */\r
746 void ThawUI()\r
747 {\r
748   HMENU hmenu;\r
749   int i;\r
750 \r
751   if (!frozen) return;\r
752   frozen = 0;\r
753   hmenu = GetMenu(hwndMain);\r
754   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
755     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
756   }\r
757   DrawMenuBar(hwndMain);\r
758 }\r
759 \r
760 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
761 \r
762 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
763 #ifdef JAWS\r
764 #include "jaws.c"\r
765 #else\r
766 #define JAWS_INIT\r
767 #define JAWS_ARGS\r
768 #define JAWS_ALT_INTERCEPT\r
769 #define JAWS_KBUP_NAVIGATION\r
770 #define JAWS_KBDOWN_NAVIGATION\r
771 #define JAWS_MENU_ITEMS\r
772 #define JAWS_SILENCE\r
773 #define JAWS_REPLAY\r
774 #define JAWS_ACCEL\r
775 #define JAWS_COPYRIGHT\r
776 #define JAWS_DELETE(X) X\r
777 #define SAYMACHINEMOVE()\r
778 #define SAY(X)\r
779 #endif\r
780 \r
781 /*---------------------------------------------------------------------------*\\r
782  *\r
783  * WinMain\r
784  *\r
785 \*---------------------------------------------------------------------------*/\r
786 \r
787 int APIENTRY\r
788 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
789         LPSTR lpCmdLine, int nCmdShow)\r
790 {\r
791   MSG msg;\r
792   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
793 //  INITCOMMONCONTROLSEX ex;\r
794 \r
795   debugFP = stderr;\r
796 \r
797   LoadLibrary("RICHED32.DLL");\r
798   consoleCF.cbSize = sizeof(CHARFORMAT);\r
799 \r
800   if (!InitApplication(hInstance)) {\r
801     return (FALSE);\r
802   }\r
803   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
804     return (FALSE);\r
805   }\r
806 \r
807   JAWS_INIT\r
808   TranslateMenus(1);\r
809 \r
810 //  InitCommonControlsEx(&ex);\r
811   InitCommonControls();\r
812 \r
813   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
814   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
815   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
816 \r
817   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
818 \r
819   while (GetMessage(&msg, /* message structure */\r
820                     NULL, /* handle of window receiving the message */\r
821                     0,    /* lowest message to examine */\r
822                     0))   /* highest message to examine */\r
823     {\r
824 \r
825       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
826         // [HGM] navigate: switch between all windows with tab\r
827         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
828         int i, currentElement = 0;\r
829 \r
830         // first determine what element of the chain we come from (if any)\r
831         if(appData.icsActive) {\r
832             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
833             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
834         }\r
835         if(engineOutputDialog && EngineOutputIsUp()) {\r
836             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
837             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
838         }\r
839         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
840             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
841         }\r
842         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
843         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
844         if(msg.hwnd == e1)                 currentElement = 2; else\r
845         if(msg.hwnd == e2)                 currentElement = 3; else\r
846         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
847         if(msg.hwnd == mh)                currentElement = 4; else\r
848         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
849         if(msg.hwnd == hText)  currentElement = 5; else\r
850         if(msg.hwnd == hInput) currentElement = 6; else\r
851         for (i = 0; i < N_BUTTONS; i++) {\r
852             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
853         }\r
854 \r
855         // determine where to go to\r
856         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
857           do {\r
858             currentElement = (currentElement + direction) % 7;\r
859             switch(currentElement) {\r
860                 case 0:\r
861                   h = hwndMain; break; // passing this case always makes the loop exit\r
862                 case 1:\r
863                   h = buttonDesc[0].hwnd; break; // could be NULL\r
864                 case 2:\r
865                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
866                   h = e1; break;\r
867                 case 3:\r
868                   if(!EngineOutputIsUp()) continue;\r
869                   h = e2; break;\r
870                 case 4:\r
871                   if(!MoveHistoryIsUp()) continue;\r
872                   h = mh; break;\r
873 //              case 6: // input to eval graph does not seem to get here!\r
874 //                if(!EvalGraphIsUp()) continue;\r
875 //                h = evalGraphDialog; break;\r
876                 case 5:\r
877                   if(!appData.icsActive) continue;\r
878                   SAY("display");\r
879                   h = hText; break;\r
880                 case 6:\r
881                   if(!appData.icsActive) continue;\r
882                   SAY("input");\r
883                   h = hInput; break;\r
884             }\r
885           } while(h == 0);\r
886 \r
887           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
888           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
889           SetFocus(h);\r
890 \r
891           continue; // this message now has been processed\r
892         }\r
893       }\r
894 \r
895       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
896           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
897           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
898           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
899           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
900           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
901           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
902           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
903           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
904           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
905         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
906         for(i=0; i<MAX_CHAT; i++) \r
907             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
908                 done = 1; break;\r
909         }\r
910         if(done) continue; // [HGM] chat: end patch\r
911         TranslateMessage(&msg); /* Translates virtual key codes */\r
912         DispatchMessage(&msg);  /* Dispatches message to window */\r
913       }\r
914     }\r
915 \r
916 \r
917   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
918 }\r
919 \r
920 /*---------------------------------------------------------------------------*\\r
921  *\r
922  * Initialization functions\r
923  *\r
924 \*---------------------------------------------------------------------------*/\r
925 \r
926 void\r
927 SetUserLogo()\r
928 {   // update user logo if necessary\r
929     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
930 \r
931     if(appData.autoLogo) {\r
932           curName = UserName();\r
933           if(strcmp(curName, oldUserName)) {\r
934                 GetCurrentDirectory(MSG_SIZ, dir);\r
935                 SetCurrentDirectory(installDir);\r
936                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
937                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
938                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
939                 if(userLogo == NULL)\r
940                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
941                 SetCurrentDirectory(dir); /* return to prev directory */\r
942           }\r
943     }\r
944 }\r
945 \r
946 BOOL\r
947 InitApplication(HINSTANCE hInstance)\r
948 {\r
949   WNDCLASS wc;\r
950 \r
951   /* Fill in window class structure with parameters that describe the */\r
952   /* main window. */\r
953 \r
954   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
955   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
956   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
957   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
958   wc.hInstance     = hInstance;         /* Owner of this class */\r
959   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
960   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
961   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
962   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
963   wc.lpszClassName = szAppName;                 /* Name to register as */\r
964 \r
965   /* Register the window class and return success/failure code. */\r
966   if (!RegisterClass(&wc)) return FALSE;\r
967 \r
968   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
969   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
970   wc.cbClsExtra    = 0;\r
971   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
972   wc.hInstance     = hInstance;\r
973   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
974   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
975   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
976   wc.lpszMenuName  = NULL;\r
977   wc.lpszClassName = szConsoleName;\r
978 \r
979   if (!RegisterClass(&wc)) return FALSE;\r
980   return TRUE;\r
981 }\r
982 \r
983 \r
984 /* Set by InitInstance, used by EnsureOnScreen */\r
985 int screenHeight, screenWidth;\r
986 RECT screenGeometry;\r
987 \r
988 void\r
989 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
990 {\r
991 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
992   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
993   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
994   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
995   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
996   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
997 }\r
998 \r
999 VOID\r
1000 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1001 {\r
1002   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1003   GetCurrentDirectory(MSG_SIZ, dir);\r
1004   SetCurrentDirectory(installDir);\r
1005   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1006       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1007 \r
1008       if (cps->programLogo == NULL && appData.debugMode) {\r
1009           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1010       }\r
1011   } else if(appData.autoLogo) {\r
1012       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1013         char *opponent = "";\r
1014         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1015         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1016         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1017         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1018             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1019             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1020         }\r
1021       } else\r
1022       if(appData.directory[n] && appData.directory[n][0]) {\r
1023         SetCurrentDirectory(appData.directory[n]);\r
1024         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1025       }\r
1026   }\r
1027   SetCurrentDirectory(dir); /* return to prev directory */\r
1028 }\r
1029 \r
1030 VOID\r
1031 InitTextures()\r
1032 {\r
1033   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1034   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1035   \r
1036   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1037       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1038       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1039       liteBackTextureMode = appData.liteBackTextureMode;\r
1040 \r
1041       if (liteBackTexture == NULL && appData.debugMode) {\r
1042           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1043       }\r
1044   }\r
1045   \r
1046   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1047       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1048       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1049       darkBackTextureMode = appData.darkBackTextureMode;\r
1050 \r
1051       if (darkBackTexture == NULL && appData.debugMode) {\r
1052           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1053       }\r
1054   }\r
1055 }\r
1056 \r
1057 #ifndef SM_CXVIRTUALSCREEN\r
1058 #define SM_CXVIRTUALSCREEN 78\r
1059 #endif\r
1060 #ifndef SM_CYVIRTUALSCREEN\r
1061 #define SM_CYVIRTUALSCREEN 79\r
1062 #endif\r
1063 #ifndef SM_XVIRTUALSCREEN \r
1064 #define SM_XVIRTUALSCREEN 76\r
1065 #endif\r
1066 #ifndef SM_YVIRTUALSCREEN \r
1067 #define SM_YVIRTUALSCREEN 77\r
1068 #endif\r
1069 \r
1070 VOID\r
1071 InitGeometry()\r
1072 {\r
1073   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1074   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1075   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1076   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1077   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1078   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1079   screenGeometry.right = screenGeometry.left + screenWidth;\r
1080   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1081 }\r
1082 \r
1083 BOOL\r
1084 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1085 {\r
1086   HWND hwnd; /* Main window handle. */\r
1087   int ibs;\r
1088   WINDOWPLACEMENT wp;\r
1089   char *filepart;\r
1090 \r
1091   hInst = hInstance;    /* Store instance handle in our global variable */\r
1092   programName = szAppName;\r
1093 \r
1094   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1095     *filepart = NULLCHAR;\r
1096     SetCurrentDirectory(installDir);\r
1097   } else {\r
1098     GetCurrentDirectory(MSG_SIZ, installDir);\r
1099   }\r
1100   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1101   InitGeometry();\r
1102   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1103   /* xboard, and older WinBoards, controlled the move sound with the\r
1104      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1105      always turn the option on (so that the backend will call us),\r
1106      then let the user turn the sound off by setting it to silence if\r
1107      desired.  To accommodate old winboard.ini files saved by old\r
1108      versions of WinBoard, we also turn off the sound if the option\r
1109      was initially set to false. [HGM] taken out of InitAppData */\r
1110   if (!appData.ringBellAfterMoves) {\r
1111     sounds[(int)SoundMove].name = strdup("");\r
1112     appData.ringBellAfterMoves = TRUE;\r
1113   }\r
1114   if (appData.debugMode) {\r
1115     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1116     setbuf(debugFP, NULL);\r
1117   }\r
1118 \r
1119   LoadLanguageFile(appData.language);\r
1120 \r
1121   InitBackEnd1();\r
1122 \r
1123 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1124 //  InitEngineUCI( installDir, &second );\r
1125 \r
1126   /* Create a main window for this application instance. */\r
1127   hwnd = CreateWindow(szAppName, szTitle,\r
1128                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1129                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1130                       NULL, NULL, hInstance, NULL);\r
1131   hwndMain = hwnd;\r
1132 \r
1133   /* If window could not be created, return "failure" */\r
1134   if (!hwnd) {\r
1135     return (FALSE);\r
1136   }\r
1137 \r
1138   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1139   LoadLogo(&first, 0, FALSE);\r
1140   LoadLogo(&second, 1, appData.icsActive);\r
1141 \r
1142   SetUserLogo();\r
1143 \r
1144   iconWhite = LoadIcon(hInstance, "icon_white");\r
1145   iconBlack = LoadIcon(hInstance, "icon_black");\r
1146   iconCurrent = iconWhite;\r
1147   InitDrawingColors();\r
1148 \r
1149   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1150   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1151     /* Compute window size for each board size, and use the largest\r
1152        size that fits on this screen as the default. */\r
1153     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1154     if (boardSize == (BoardSize)-1 &&\r
1155         winH <= screenHeight\r
1156            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1157         && winW <= screenWidth) {\r
1158       boardSize = (BoardSize)ibs;\r
1159     }\r
1160   }\r
1161 \r
1162   InitDrawingSizes(boardSize, 0);\r
1163   RecentEngineMenu(appData.recentEngineList);\r
1164   InitMenuChecks();\r
1165   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1166 \r
1167   /* [AS] Load textures if specified */\r
1168   InitTextures();\r
1169 \r
1170   mysrandom( (unsigned) time(NULL) );\r
1171 \r
1172   /* [AS] Restore layout */\r
1173   if( wpMoveHistory.visible ) {\r
1174       MoveHistoryPopUp();\r
1175   }\r
1176 \r
1177   if( wpEvalGraph.visible ) {\r
1178       EvalGraphPopUp();\r
1179   }\r
1180 \r
1181   if( wpEngineOutput.visible ) {\r
1182       EngineOutputPopUp();\r
1183   }\r
1184 \r
1185   /* Make the window visible; update its client area; and return "success" */\r
1186   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1187   wp.length = sizeof(WINDOWPLACEMENT);\r
1188   wp.flags = 0;\r
1189   wp.showCmd = nCmdShow;\r
1190   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1191   wp.rcNormalPosition.left = wpMain.x;\r
1192   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1193   wp.rcNormalPosition.top = wpMain.y;\r
1194   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1195   SetWindowPlacement(hwndMain, &wp);\r
1196 \r
1197   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1198 \r
1199   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1200                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1201 \r
1202   if (hwndConsole) {\r
1203 #if AOT_CONSOLE\r
1204     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1205                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1206 #endif\r
1207     ShowWindow(hwndConsole, nCmdShow);\r
1208     SetActiveWindow(hwndConsole);\r
1209   }\r
1210   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1211   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1212 \r
1213   return TRUE;\r
1214 \r
1215 }\r
1216 \r
1217 VOID\r
1218 InitMenuChecks()\r
1219 {\r
1220   HMENU hmenu = GetMenu(hwndMain);\r
1221 \r
1222   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1223                         MF_BYCOMMAND|((appData.icsActive &&\r
1224                                        *appData.icsCommPort != NULLCHAR) ?\r
1225                                       MF_ENABLED : MF_GRAYED));\r
1226   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1227                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1228                                      MF_CHECKED : MF_UNCHECKED));\r
1229 }\r
1230 \r
1231 //---------------------------------------------------------------------------------------------------------\r
1232 \r
1233 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1234 #define XBOARD FALSE\r
1235 \r
1236 #define OPTCHAR "/"\r
1237 #define SEPCHAR "="\r
1238 #define TOPLEVEL 0\r
1239 \r
1240 #include "args.h"\r
1241 \r
1242 // front-end part of option handling\r
1243 \r
1244 VOID\r
1245 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1246 {\r
1247   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1248   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1249   DeleteDC(hdc);\r
1250   lf->lfWidth = 0;\r
1251   lf->lfEscapement = 0;\r
1252   lf->lfOrientation = 0;\r
1253   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1254   lf->lfItalic = mfp->italic;\r
1255   lf->lfUnderline = mfp->underline;\r
1256   lf->lfStrikeOut = mfp->strikeout;\r
1257   lf->lfCharSet = mfp->charset;\r
1258   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1259   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1260   lf->lfQuality = DEFAULT_QUALITY;\r
1261   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1262     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1263 }\r
1264 \r
1265 void\r
1266 CreateFontInMF(MyFont *mf)\r
1267\r
1268   LFfromMFP(&mf->lf, &mf->mfp);\r
1269   if (mf->hf) DeleteObject(mf->hf);\r
1270   mf->hf = CreateFontIndirect(&mf->lf);\r
1271 }\r
1272 \r
1273 // [HGM] This platform-dependent table provides the location for storing the color info\r
1274 void *\r
1275 colorVariable[] = {\r
1276   &whitePieceColor, \r
1277   &blackPieceColor, \r
1278   &lightSquareColor,\r
1279   &darkSquareColor, \r
1280   &highlightSquareColor,\r
1281   &premoveHighlightColor,\r
1282   NULL,\r
1283   &consoleBackgroundColor,\r
1284   &appData.fontForeColorWhite,\r
1285   &appData.fontBackColorWhite,\r
1286   &appData.fontForeColorBlack,\r
1287   &appData.fontBackColorBlack,\r
1288   &appData.evalHistColorWhite,\r
1289   &appData.evalHistColorBlack,\r
1290   &appData.highlightArrowColor,\r
1291 };\r
1292 \r
1293 /* Command line font name parser.  NULL name means do nothing.\r
1294    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1295    For backward compatibility, syntax without the colon is also\r
1296    accepted, but font names with digits in them won't work in that case.\r
1297 */\r
1298 VOID\r
1299 ParseFontName(char *name, MyFontParams *mfp)\r
1300 {\r
1301   char *p, *q;\r
1302   if (name == NULL) return;\r
1303   p = name;\r
1304   q = strchr(p, ':');\r
1305   if (q) {\r
1306     if (q - p >= sizeof(mfp->faceName))\r
1307       ExitArgError(_("Font name too long:"), name, TRUE);\r
1308     memcpy(mfp->faceName, p, q - p);\r
1309     mfp->faceName[q - p] = NULLCHAR;\r
1310     p = q + 1;\r
1311   } else {\r
1312     q = mfp->faceName;\r
1313 \r
1314     while (*p && !isdigit(*p)) {\r
1315       *q++ = *p++;\r
1316       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1317         ExitArgError(_("Font name too long:"), name, TRUE);\r
1318     }\r
1319     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1320     *q = NULLCHAR;\r
1321   }\r
1322   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1323   mfp->pointSize = (float) atof(p);\r
1324   mfp->bold = (strchr(p, 'b') != NULL);\r
1325   mfp->italic = (strchr(p, 'i') != NULL);\r
1326   mfp->underline = (strchr(p, 'u') != NULL);\r
1327   mfp->strikeout = (strchr(p, 's') != NULL);\r
1328   mfp->charset = DEFAULT_CHARSET;\r
1329   q = strchr(p, 'c');\r
1330   if (q)\r
1331     mfp->charset = (BYTE) atoi(q+1);\r
1332 }\r
1333 \r
1334 void\r
1335 ParseFont(char *name, int number)\r
1336 { // wrapper to shield back-end from 'font'\r
1337   ParseFontName(name, &font[boardSize][number]->mfp);\r
1338 }\r
1339 \r
1340 void\r
1341 SetFontDefaults()\r
1342 { // in WB  we have a 2D array of fonts; this initializes their description\r
1343   int i, j;\r
1344   /* Point font array elements to structures and\r
1345      parse default font names */\r
1346   for (i=0; i<NUM_FONTS; i++) {\r
1347     for (j=0; j<NUM_SIZES; j++) {\r
1348       font[j][i] = &fontRec[j][i];\r
1349       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1350     }\r
1351   }\r
1352 }\r
1353 \r
1354 void\r
1355 CreateFonts()\r
1356 { // here we create the actual fonts from the selected descriptions\r
1357   int i, j;\r
1358   for (i=0; i<NUM_FONTS; i++) {\r
1359     for (j=0; j<NUM_SIZES; j++) {\r
1360       CreateFontInMF(font[j][i]);\r
1361     }\r
1362   }\r
1363 }\r
1364 /* Color name parser.\r
1365    X version accepts X color names, but this one\r
1366    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1367 COLORREF\r
1368 ParseColorName(char *name)\r
1369 {\r
1370   int red, green, blue, count;\r
1371   char buf[MSG_SIZ];\r
1372 \r
1373   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1374   if (count != 3) {\r
1375     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1376       &red, &green, &blue);\r
1377   }\r
1378   if (count != 3) {\r
1379     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1380     DisplayError(buf, 0);\r
1381     return RGB(0, 0, 0);\r
1382   }\r
1383   return PALETTERGB(red, green, blue);\r
1384 }\r
1385 \r
1386 void\r
1387 ParseColor(int n, char *name)\r
1388 { // for WinBoard the color is an int, which needs to be derived from the string\r
1389   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1390 }\r
1391 \r
1392 void\r
1393 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1394 {\r
1395   char *e = argValue;\r
1396   int eff = 0;\r
1397 \r
1398   while (*e) {\r
1399     if (*e == 'b')      eff |= CFE_BOLD;\r
1400     else if (*e == 'i') eff |= CFE_ITALIC;\r
1401     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1402     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1403     else if (*e == '#' || isdigit(*e)) break;\r
1404     e++;\r
1405   }\r
1406   *effects = eff;\r
1407   *color   = ParseColorName(e);\r
1408 }\r
1409 \r
1410 void\r
1411 ParseTextAttribs(ColorClass cc, char *s)\r
1412 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1413     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1414     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1415 }\r
1416 \r
1417 void\r
1418 ParseBoardSize(void *addr, char *name)\r
1419 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1420   BoardSize bs = SizeTiny;\r
1421   while (sizeInfo[bs].name != NULL) {\r
1422     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1423         *(BoardSize *)addr = bs;\r
1424         return;\r
1425     }\r
1426     bs++;\r
1427   }\r
1428   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1429 }\r
1430 \r
1431 void\r
1432 LoadAllSounds()\r
1433 { // [HGM] import name from appData first\r
1434   ColorClass cc;\r
1435   SoundClass sc;\r
1436   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1437     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1438     textAttribs[cc].sound.data = NULL;\r
1439     MyLoadSound(&textAttribs[cc].sound);\r
1440   }\r
1441   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1442     textAttribs[cc].sound.name = strdup("");\r
1443     textAttribs[cc].sound.data = NULL;\r
1444   }\r
1445   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1446     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1447     sounds[sc].data = NULL;\r
1448     MyLoadSound(&sounds[sc]);\r
1449   }\r
1450 }\r
1451 \r
1452 void\r
1453 SetCommPortDefaults()\r
1454 {\r
1455    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1456   dcb.DCBlength = sizeof(DCB);\r
1457   dcb.BaudRate = 9600;\r
1458   dcb.fBinary = TRUE;\r
1459   dcb.fParity = FALSE;\r
1460   dcb.fOutxCtsFlow = FALSE;\r
1461   dcb.fOutxDsrFlow = FALSE;\r
1462   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1463   dcb.fDsrSensitivity = FALSE;\r
1464   dcb.fTXContinueOnXoff = TRUE;\r
1465   dcb.fOutX = FALSE;\r
1466   dcb.fInX = FALSE;\r
1467   dcb.fNull = FALSE;\r
1468   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1469   dcb.fAbortOnError = FALSE;\r
1470   dcb.ByteSize = 7;\r
1471   dcb.Parity = SPACEPARITY;\r
1472   dcb.StopBits = ONESTOPBIT;\r
1473 }\r
1474 \r
1475 // [HGM] args: these three cases taken out to stay in front-end\r
1476 void\r
1477 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1478 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1479         // while the curent board size determines the element. This system should be ported to XBoard.\r
1480         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1481         int bs;\r
1482         for (bs=0; bs<NUM_SIZES; bs++) {\r
1483           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1484           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1485           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1486             ad->argName, mfp->faceName, mfp->pointSize,\r
1487             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1488             mfp->bold ? "b" : "",\r
1489             mfp->italic ? "i" : "",\r
1490             mfp->underline ? "u" : "",\r
1491             mfp->strikeout ? "s" : "",\r
1492             (int)mfp->charset);\r
1493         }\r
1494       }\r
1495 \r
1496 void\r
1497 ExportSounds()\r
1498 { // [HGM] copy the names from the internal WB variables to appData\r
1499   ColorClass cc;\r
1500   SoundClass sc;\r
1501   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1502     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1503   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1504     (&appData.soundMove)[sc] = sounds[sc].name;\r
1505 }\r
1506 \r
1507 void\r
1508 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1509 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1510         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1511         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1512           (ta->effects & CFE_BOLD) ? "b" : "",\r
1513           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1514           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1515           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1516           (ta->effects) ? " " : "",\r
1517           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1518       }\r
1519 \r
1520 void\r
1521 SaveColor(FILE *f, ArgDescriptor *ad)\r
1522 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1523         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1524         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1525           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1526 }\r
1527 \r
1528 void\r
1529 SaveBoardSize(FILE *f, char *name, void *addr)\r
1530 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1531   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1532 }\r
1533 \r
1534 void\r
1535 ParseCommPortSettings(char *s)\r
1536 { // wrapper to keep dcb from back-end\r
1537   ParseCommSettings(s, &dcb);\r
1538 }\r
1539 \r
1540 void\r
1541 GetWindowCoords()\r
1542 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1543   GetActualPlacement(hwndMain, &wpMain);\r
1544   GetActualPlacement(hwndConsole, &wpConsole);\r
1545   GetActualPlacement(commentDialog, &wpComment);\r
1546   GetActualPlacement(editTagsDialog, &wpTags);\r
1547   GetActualPlacement(gameListDialog, &wpGameList);\r
1548   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1549   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1550   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1551 }\r
1552 \r
1553 void\r
1554 PrintCommPortSettings(FILE *f, char *name)\r
1555 { // wrapper to shield back-end from DCB\r
1556       PrintCommSettings(f, name, &dcb);\r
1557 }\r
1558 \r
1559 int\r
1560 MySearchPath(char *installDir, char *name, char *fullname)\r
1561 {\r
1562   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1563   if(name[0]== '%') {\r
1564     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1565     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1566       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1567       *strchr(buf, '%') = 0;\r
1568       strcat(fullname, getenv(buf));\r
1569       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1570     }\r
1571     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1572     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1573     return (int) strlen(fullname);\r
1574   }\r
1575   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1576 }\r
1577 \r
1578 int\r
1579 MyGetFullPathName(char *name, char *fullname)\r
1580 {\r
1581   char *dummy;\r
1582   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1583 }\r
1584 \r
1585 int\r
1586 MainWindowUp()\r
1587 { // [HGM] args: allows testing if main window is realized from back-end\r
1588   return hwndMain != NULL;\r
1589 }\r
1590 \r
1591 void\r
1592 PopUpStartupDialog()\r
1593 {\r
1594     FARPROC lpProc;\r
1595     \r
1596     LoadLanguageFile(appData.language);\r
1597     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1598     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1599     FreeProcInstance(lpProc);\r
1600 }\r
1601 \r
1602 /*---------------------------------------------------------------------------*\\r
1603  *\r
1604  * GDI board drawing routines\r
1605  *\r
1606 \*---------------------------------------------------------------------------*/\r
1607 \r
1608 /* [AS] Draw square using background texture */\r
1609 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1610 {\r
1611     XFORM   x;\r
1612 \r
1613     if( mode == 0 ) {\r
1614         return; /* Should never happen! */\r
1615     }\r
1616 \r
1617     SetGraphicsMode( dst, GM_ADVANCED );\r
1618 \r
1619     switch( mode ) {\r
1620     case 1:\r
1621         /* Identity */\r
1622         break;\r
1623     case 2:\r
1624         /* X reflection */\r
1625         x.eM11 = -1.0;\r
1626         x.eM12 = 0;\r
1627         x.eM21 = 0;\r
1628         x.eM22 = 1.0;\r
1629         x.eDx = (FLOAT) dw + dx - 1;\r
1630         x.eDy = 0;\r
1631         dx = 0;\r
1632         SetWorldTransform( dst, &x );\r
1633         break;\r
1634     case 3:\r
1635         /* Y reflection */\r
1636         x.eM11 = 1.0;\r
1637         x.eM12 = 0;\r
1638         x.eM21 = 0;\r
1639         x.eM22 = -1.0;\r
1640         x.eDx = 0;\r
1641         x.eDy = (FLOAT) dh + dy - 1;\r
1642         dy = 0;\r
1643         SetWorldTransform( dst, &x );\r
1644         break;\r
1645     case 4:\r
1646         /* X/Y flip */\r
1647         x.eM11 = 0;\r
1648         x.eM12 = 1.0;\r
1649         x.eM21 = 1.0;\r
1650         x.eM22 = 0;\r
1651         x.eDx = (FLOAT) dx;\r
1652         x.eDy = (FLOAT) dy;\r
1653         dx = 0;\r
1654         dy = 0;\r
1655         SetWorldTransform( dst, &x );\r
1656         break;\r
1657     }\r
1658 \r
1659     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1660 \r
1661     x.eM11 = 1.0;\r
1662     x.eM12 = 0;\r
1663     x.eM21 = 0;\r
1664     x.eM22 = 1.0;\r
1665     x.eDx = 0;\r
1666     x.eDy = 0;\r
1667     SetWorldTransform( dst, &x );\r
1668 \r
1669     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1670 }\r
1671 \r
1672 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1673 enum {\r
1674     PM_WP = (int) WhitePawn, \r
1675     PM_WN = (int) WhiteKnight, \r
1676     PM_WB = (int) WhiteBishop, \r
1677     PM_WR = (int) WhiteRook, \r
1678     PM_WQ = (int) WhiteQueen, \r
1679     PM_WF = (int) WhiteFerz, \r
1680     PM_WW = (int) WhiteWazir, \r
1681     PM_WE = (int) WhiteAlfil, \r
1682     PM_WM = (int) WhiteMan, \r
1683     PM_WO = (int) WhiteCannon, \r
1684     PM_WU = (int) WhiteUnicorn, \r
1685     PM_WH = (int) WhiteNightrider, \r
1686     PM_WA = (int) WhiteAngel, \r
1687     PM_WC = (int) WhiteMarshall, \r
1688     PM_WAB = (int) WhiteCardinal, \r
1689     PM_WD = (int) WhiteDragon, \r
1690     PM_WL = (int) WhiteLance, \r
1691     PM_WS = (int) WhiteCobra, \r
1692     PM_WV = (int) WhiteFalcon, \r
1693     PM_WSG = (int) WhiteSilver, \r
1694     PM_WG = (int) WhiteGrasshopper, \r
1695     PM_WK = (int) WhiteKing,\r
1696     PM_BP = (int) BlackPawn, \r
1697     PM_BN = (int) BlackKnight, \r
1698     PM_BB = (int) BlackBishop, \r
1699     PM_BR = (int) BlackRook, \r
1700     PM_BQ = (int) BlackQueen, \r
1701     PM_BF = (int) BlackFerz, \r
1702     PM_BW = (int) BlackWazir, \r
1703     PM_BE = (int) BlackAlfil, \r
1704     PM_BM = (int) BlackMan,\r
1705     PM_BO = (int) BlackCannon, \r
1706     PM_BU = (int) BlackUnicorn, \r
1707     PM_BH = (int) BlackNightrider, \r
1708     PM_BA = (int) BlackAngel, \r
1709     PM_BC = (int) BlackMarshall, \r
1710     PM_BG = (int) BlackGrasshopper, \r
1711     PM_BAB = (int) BlackCardinal,\r
1712     PM_BD = (int) BlackDragon,\r
1713     PM_BL = (int) BlackLance,\r
1714     PM_BS = (int) BlackCobra,\r
1715     PM_BV = (int) BlackFalcon,\r
1716     PM_BSG = (int) BlackSilver,\r
1717     PM_BK = (int) BlackKing\r
1718 };\r
1719 \r
1720 static HFONT hPieceFont = NULL;\r
1721 static HBITMAP hPieceMask[(int) EmptySquare];\r
1722 static HBITMAP hPieceFace[(int) EmptySquare];\r
1723 static int fontBitmapSquareSize = 0;\r
1724 static char pieceToFontChar[(int) EmptySquare] =\r
1725                               { 'p', 'n', 'b', 'r', 'q', \r
1726                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1727                       'k', 'o', 'm', 'v', 't', 'w', \r
1728                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1729                                                               'l' };\r
1730 \r
1731 extern BOOL SetCharTable( char *table, const char * map );\r
1732 /* [HGM] moved to backend.c */\r
1733 \r
1734 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1735 {\r
1736     HBRUSH hbrush;\r
1737     BYTE r1 = GetRValue( color );\r
1738     BYTE g1 = GetGValue( color );\r
1739     BYTE b1 = GetBValue( color );\r
1740     BYTE r2 = r1 / 2;\r
1741     BYTE g2 = g1 / 2;\r
1742     BYTE b2 = b1 / 2;\r
1743     RECT rc;\r
1744 \r
1745     /* Create a uniform background first */\r
1746     hbrush = CreateSolidBrush( color );\r
1747     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1748     FillRect( hdc, &rc, hbrush );\r
1749     DeleteObject( hbrush );\r
1750     \r
1751     if( mode == 1 ) {\r
1752         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1753         int steps = squareSize / 2;\r
1754         int i;\r
1755 \r
1756         for( i=0; i<steps; i++ ) {\r
1757             BYTE r = r1 - (r1-r2) * i / steps;\r
1758             BYTE g = g1 - (g1-g2) * i / steps;\r
1759             BYTE b = b1 - (b1-b2) * i / steps;\r
1760 \r
1761             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1762             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1763             FillRect( hdc, &rc, hbrush );\r
1764             DeleteObject(hbrush);\r
1765         }\r
1766     }\r
1767     else if( mode == 2 ) {\r
1768         /* Diagonal gradient, good more or less for every piece */\r
1769         POINT triangle[3];\r
1770         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1771         HBRUSH hbrush_old;\r
1772         int steps = squareSize;\r
1773         int i;\r
1774 \r
1775         triangle[0].x = squareSize - steps;\r
1776         triangle[0].y = squareSize;\r
1777         triangle[1].x = squareSize;\r
1778         triangle[1].y = squareSize;\r
1779         triangle[2].x = squareSize;\r
1780         triangle[2].y = squareSize - steps;\r
1781 \r
1782         for( i=0; i<steps; i++ ) {\r
1783             BYTE r = r1 - (r1-r2) * i / steps;\r
1784             BYTE g = g1 - (g1-g2) * i / steps;\r
1785             BYTE b = b1 - (b1-b2) * i / steps;\r
1786 \r
1787             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1788             hbrush_old = SelectObject( hdc, hbrush );\r
1789             Polygon( hdc, triangle, 3 );\r
1790             SelectObject( hdc, hbrush_old );\r
1791             DeleteObject(hbrush);\r
1792             triangle[0].x++;\r
1793             triangle[2].y++;\r
1794         }\r
1795 \r
1796         SelectObject( hdc, hpen );\r
1797     }\r
1798 }\r
1799 \r
1800 /*\r
1801     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1802     seems to work ok. The main problem here is to find the "inside" of a chess\r
1803     piece: follow the steps as explained below.\r
1804 */\r
1805 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1806 {\r
1807     HBITMAP hbm;\r
1808     HBITMAP hbm_old;\r
1809     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1810     RECT rc;\r
1811     SIZE sz;\r
1812     POINT pt;\r
1813     int backColor = whitePieceColor; \r
1814     int foreColor = blackPieceColor;\r
1815     \r
1816     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1817         backColor = appData.fontBackColorWhite;\r
1818         foreColor = appData.fontForeColorWhite;\r
1819     }\r
1820     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1821         backColor = appData.fontBackColorBlack;\r
1822         foreColor = appData.fontForeColorBlack;\r
1823     }\r
1824 \r
1825     /* Mask */\r
1826     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1827 \r
1828     hbm_old = SelectObject( hdc, hbm );\r
1829 \r
1830     rc.left = 0;\r
1831     rc.top = 0;\r
1832     rc.right = squareSize;\r
1833     rc.bottom = squareSize;\r
1834 \r
1835     /* Step 1: background is now black */\r
1836     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1837 \r
1838     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1839 \r
1840     pt.x = (squareSize - sz.cx) / 2;\r
1841     pt.y = (squareSize - sz.cy) / 2;\r
1842 \r
1843     SetBkMode( hdc, TRANSPARENT );\r
1844     SetTextColor( hdc, chroma );\r
1845     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1846     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1847 \r
1848     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1849     /* Step 3: the area outside the piece is filled with white */\r
1850 //    FloodFill( hdc, 0, 0, chroma );\r
1851     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1852     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1853     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1854     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1855     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1856     /* \r
1857         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1858         but if the start point is not inside the piece we're lost!\r
1859         There should be a better way to do this... if we could create a region or path\r
1860         from the fill operation we would be fine for example.\r
1861     */\r
1862 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1863     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1864 \r
1865     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1866         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1867         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1868 \r
1869         SelectObject( dc2, bm2 );\r
1870         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1871         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1872         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1873         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1874         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1875 \r
1876         DeleteDC( dc2 );\r
1877         DeleteObject( bm2 );\r
1878     }\r
1879 \r
1880     SetTextColor( hdc, 0 );\r
1881     /* \r
1882         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1883         draw the piece again in black for safety.\r
1884     */\r
1885     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1886 \r
1887     SelectObject( hdc, hbm_old );\r
1888 \r
1889     if( hPieceMask[index] != NULL ) {\r
1890         DeleteObject( hPieceMask[index] );\r
1891     }\r
1892 \r
1893     hPieceMask[index] = hbm;\r
1894 \r
1895     /* Face */\r
1896     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1897 \r
1898     SelectObject( hdc, hbm );\r
1899 \r
1900     {\r
1901         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1902         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1903         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1904 \r
1905         SelectObject( dc1, hPieceMask[index] );\r
1906         SelectObject( dc2, bm2 );\r
1907         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1908         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1909         \r
1910         /* \r
1911             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1912             the piece background and deletes (makes transparent) the rest.\r
1913             Thanks to that mask, we are free to paint the background with the greates\r
1914             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1915             We use this, to make gradients and give the pieces a "roundish" look.\r
1916         */\r
1917         SetPieceBackground( hdc, backColor, 2 );\r
1918         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1919 \r
1920         DeleteDC( dc2 );\r
1921         DeleteDC( dc1 );\r
1922         DeleteObject( bm2 );\r
1923     }\r
1924 \r
1925     SetTextColor( hdc, foreColor );\r
1926     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1927 \r
1928     SelectObject( hdc, hbm_old );\r
1929 \r
1930     if( hPieceFace[index] != NULL ) {\r
1931         DeleteObject( hPieceFace[index] );\r
1932     }\r
1933 \r
1934     hPieceFace[index] = hbm;\r
1935 }\r
1936 \r
1937 static int TranslatePieceToFontPiece( int piece )\r
1938 {\r
1939     switch( piece ) {\r
1940     case BlackPawn:\r
1941         return PM_BP;\r
1942     case BlackKnight:\r
1943         return PM_BN;\r
1944     case BlackBishop:\r
1945         return PM_BB;\r
1946     case BlackRook:\r
1947         return PM_BR;\r
1948     case BlackQueen:\r
1949         return PM_BQ;\r
1950     case BlackKing:\r
1951         return PM_BK;\r
1952     case WhitePawn:\r
1953         return PM_WP;\r
1954     case WhiteKnight:\r
1955         return PM_WN;\r
1956     case WhiteBishop:\r
1957         return PM_WB;\r
1958     case WhiteRook:\r
1959         return PM_WR;\r
1960     case WhiteQueen:\r
1961         return PM_WQ;\r
1962     case WhiteKing:\r
1963         return PM_WK;\r
1964 \r
1965     case BlackAngel:\r
1966         return PM_BA;\r
1967     case BlackMarshall:\r
1968         return PM_BC;\r
1969     case BlackFerz:\r
1970         return PM_BF;\r
1971     case BlackNightrider:\r
1972         return PM_BH;\r
1973     case BlackAlfil:\r
1974         return PM_BE;\r
1975     case BlackWazir:\r
1976         return PM_BW;\r
1977     case BlackUnicorn:\r
1978         return PM_BU;\r
1979     case BlackCannon:\r
1980         return PM_BO;\r
1981     case BlackGrasshopper:\r
1982         return PM_BG;\r
1983     case BlackMan:\r
1984         return PM_BM;\r
1985     case BlackSilver:\r
1986         return PM_BSG;\r
1987     case BlackLance:\r
1988         return PM_BL;\r
1989     case BlackFalcon:\r
1990         return PM_BV;\r
1991     case BlackCobra:\r
1992         return PM_BS;\r
1993     case BlackCardinal:\r
1994         return PM_BAB;\r
1995     case BlackDragon:\r
1996         return PM_BD;\r
1997 \r
1998     case WhiteAngel:\r
1999         return PM_WA;\r
2000     case WhiteMarshall:\r
2001         return PM_WC;\r
2002     case WhiteFerz:\r
2003         return PM_WF;\r
2004     case WhiteNightrider:\r
2005         return PM_WH;\r
2006     case WhiteAlfil:\r
2007         return PM_WE;\r
2008     case WhiteWazir:\r
2009         return PM_WW;\r
2010     case WhiteUnicorn:\r
2011         return PM_WU;\r
2012     case WhiteCannon:\r
2013         return PM_WO;\r
2014     case WhiteGrasshopper:\r
2015         return PM_WG;\r
2016     case WhiteMan:\r
2017         return PM_WM;\r
2018     case WhiteSilver:\r
2019         return PM_WSG;\r
2020     case WhiteLance:\r
2021         return PM_WL;\r
2022     case WhiteFalcon:\r
2023         return PM_WV;\r
2024     case WhiteCobra:\r
2025         return PM_WS;\r
2026     case WhiteCardinal:\r
2027         return PM_WAB;\r
2028     case WhiteDragon:\r
2029         return PM_WD;\r
2030     }\r
2031 \r
2032     return 0;\r
2033 }\r
2034 \r
2035 void CreatePiecesFromFont()\r
2036 {\r
2037     LOGFONT lf;\r
2038     HDC hdc_window = NULL;\r
2039     HDC hdc = NULL;\r
2040     HFONT hfont_old;\r
2041     int fontHeight;\r
2042     int i;\r
2043 \r
2044     if( fontBitmapSquareSize < 0 ) {\r
2045         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2046         return;\r
2047     }\r
2048 \r
2049     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2050             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2051         fontBitmapSquareSize = -1;\r
2052         return;\r
2053     }\r
2054 \r
2055     if( fontBitmapSquareSize != squareSize ) {\r
2056         hdc_window = GetDC( hwndMain );\r
2057         hdc = CreateCompatibleDC( hdc_window );\r
2058 \r
2059         if( hPieceFont != NULL ) {\r
2060             DeleteObject( hPieceFont );\r
2061         }\r
2062         else {\r
2063             for( i=0; i<=(int)BlackKing; i++ ) {\r
2064                 hPieceMask[i] = NULL;\r
2065                 hPieceFace[i] = NULL;\r
2066             }\r
2067         }\r
2068 \r
2069         fontHeight = 75;\r
2070 \r
2071         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2072             fontHeight = appData.fontPieceSize;\r
2073         }\r
2074 \r
2075         fontHeight = (fontHeight * squareSize) / 100;\r
2076 \r
2077         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2078         lf.lfWidth = 0;\r
2079         lf.lfEscapement = 0;\r
2080         lf.lfOrientation = 0;\r
2081         lf.lfWeight = FW_NORMAL;\r
2082         lf.lfItalic = 0;\r
2083         lf.lfUnderline = 0;\r
2084         lf.lfStrikeOut = 0;\r
2085         lf.lfCharSet = DEFAULT_CHARSET;\r
2086         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2087         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2088         lf.lfQuality = PROOF_QUALITY;\r
2089         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2090         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2091         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2092 \r
2093         hPieceFont = CreateFontIndirect( &lf );\r
2094 \r
2095         if( hPieceFont == NULL ) {\r
2096             fontBitmapSquareSize = -2;\r
2097         }\r
2098         else {\r
2099             /* Setup font-to-piece character table */\r
2100             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2101                 /* No (or wrong) global settings, try to detect the font */\r
2102                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2103                     /* Alpha */\r
2104                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2105                 }\r
2106                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2107                     /* DiagramTT* family */\r
2108                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2109                 }\r
2110                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2111                     /* Fairy symbols */\r
2112                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2113                 }\r
2114                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2115                     /* Good Companion (Some characters get warped as literal :-( */\r
2116                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2117                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2118                     SetCharTable(pieceToFontChar, s);\r
2119                 }\r
2120                 else {\r
2121                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2122                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2123                 }\r
2124             }\r
2125 \r
2126             /* Create bitmaps */\r
2127             hfont_old = SelectObject( hdc, hPieceFont );\r
2128             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2129                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2130                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2131 \r
2132             SelectObject( hdc, hfont_old );\r
2133 \r
2134             fontBitmapSquareSize = squareSize;\r
2135         }\r
2136     }\r
2137 \r
2138     if( hdc != NULL ) {\r
2139         DeleteDC( hdc );\r
2140     }\r
2141 \r
2142     if( hdc_window != NULL ) {\r
2143         ReleaseDC( hwndMain, hdc_window );\r
2144     }\r
2145 }\r
2146 \r
2147 HBITMAP\r
2148 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2149 {\r
2150   char name[128], buf[MSG_SIZ];\r
2151 \r
2152     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2153   if(appData.pieceDirectory[0]) {\r
2154     HBITMAP res;\r
2155     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2156     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2157     if(res) return res;\r
2158   }\r
2159   if (gameInfo.event &&\r
2160       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2161       strcmp(name, "k80s") == 0) {\r
2162     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2163   }\r
2164   return LoadBitmap(hinst, name);\r
2165 }\r
2166 \r
2167 \r
2168 /* Insert a color into the program's logical palette\r
2169    structure.  This code assumes the given color is\r
2170    the result of the RGB or PALETTERGB macro, and it\r
2171    knows how those macros work (which is documented).\r
2172 */\r
2173 VOID\r
2174 InsertInPalette(COLORREF color)\r
2175 {\r
2176   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2177 \r
2178   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2179     DisplayFatalError(_("Too many colors"), 0, 1);\r
2180     pLogPal->palNumEntries--;\r
2181     return;\r
2182   }\r
2183 \r
2184   pe->peFlags = (char) 0;\r
2185   pe->peRed = (char) (0xFF & color);\r
2186   pe->peGreen = (char) (0xFF & (color >> 8));\r
2187   pe->peBlue = (char) (0xFF & (color >> 16));\r
2188   return;\r
2189 }\r
2190 \r
2191 \r
2192 VOID\r
2193 InitDrawingColors()\r
2194 {\r
2195   if (pLogPal == NULL) {\r
2196     /* Allocate enough memory for a logical palette with\r
2197      * PALETTESIZE entries and set the size and version fields\r
2198      * of the logical palette structure.\r
2199      */\r
2200     pLogPal = (NPLOGPALETTE)\r
2201       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2202                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2203     pLogPal->palVersion    = 0x300;\r
2204   }\r
2205   pLogPal->palNumEntries = 0;\r
2206 \r
2207   InsertInPalette(lightSquareColor);\r
2208   InsertInPalette(darkSquareColor);\r
2209   InsertInPalette(whitePieceColor);\r
2210   InsertInPalette(blackPieceColor);\r
2211   InsertInPalette(highlightSquareColor);\r
2212   InsertInPalette(premoveHighlightColor);\r
2213 \r
2214   /*  create a logical color palette according the information\r
2215    *  in the LOGPALETTE structure.\r
2216    */\r
2217   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2218 \r
2219   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2220   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2221   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2222   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2223   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2224   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2225   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2226   markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers\r
2227   /* [AS] Force rendering of the font-based pieces */\r
2228   if( fontBitmapSquareSize > 0 ) {\r
2229     fontBitmapSquareSize = 0;\r
2230   }\r
2231 }\r
2232 \r
2233 \r
2234 int\r
2235 BoardWidth(int boardSize, int n)\r
2236 { /* [HGM] argument n added to allow different width and height */\r
2237   int lineGap = sizeInfo[boardSize].lineGap;\r
2238 \r
2239   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2240       lineGap = appData.overrideLineGap;\r
2241   }\r
2242 \r
2243   return (n + 1) * lineGap +\r
2244           n * sizeInfo[boardSize].squareSize;\r
2245 }\r
2246 \r
2247 /* Respond to board resize by dragging edge */\r
2248 VOID\r
2249 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2250 {\r
2251   BoardSize newSize = NUM_SIZES - 1;\r
2252   static int recurse = 0;\r
2253   if (IsIconic(hwndMain)) return;\r
2254   if (recurse > 0) return;\r
2255   recurse++;\r
2256   while (newSize > 0) {\r
2257         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2258         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2259            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2260     newSize--;\r
2261   } \r
2262   boardSize = newSize;\r
2263   InitDrawingSizes(boardSize, flags);\r
2264   recurse--;\r
2265 }\r
2266 \r
2267 \r
2268 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2269 \r
2270 VOID\r
2271 InitDrawingSizes(BoardSize boardSize, int flags)\r
2272 {\r
2273   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2274   ChessSquare piece;\r
2275   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2276   HDC hdc;\r
2277   SIZE clockSize, messageSize;\r
2278   HFONT oldFont;\r
2279   char buf[MSG_SIZ];\r
2280   char *str;\r
2281   HMENU hmenu = GetMenu(hwndMain);\r
2282   RECT crect, wrect, oldRect;\r
2283   int offby;\r
2284   LOGBRUSH logbrush;\r
2285   VariantClass v = gameInfo.variant;\r
2286 \r
2287   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2288   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2289 \r
2290   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2291   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2292   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2293   oldBoardSize = boardSize;\r
2294 \r
2295   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2296   { // correct board size to one where built-in pieces exist\r
2297     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2298        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2299       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2300       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2301       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {\r
2302       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2303       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2304                                    boardSize = SizeMiddling;\r
2305     }\r
2306   }\r
2307   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2308 \r
2309   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2310   oldRect.top = wpMain.y;\r
2311   oldRect.right = wpMain.x + wpMain.width;\r
2312   oldRect.bottom = wpMain.y + wpMain.height;\r
2313 \r
2314   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2315   smallLayout = sizeInfo[boardSize].smallLayout;\r
2316   squareSize = sizeInfo[boardSize].squareSize;\r
2317   lineGap = sizeInfo[boardSize].lineGap;\r
2318   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2319   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2320 \r
2321   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2322       lineGap = appData.overrideLineGap;\r
2323   }\r
2324 \r
2325   if (tinyLayout != oldTinyLayout) {\r
2326     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2327     if (tinyLayout) {\r
2328       style &= ~WS_SYSMENU;\r
2329       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2330                  "&Minimize\tCtrl+F4");\r
2331     } else {\r
2332       style |= WS_SYSMENU;\r
2333       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2334     }\r
2335     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2336 \r
2337     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2338       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2339         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2340     }\r
2341     DrawMenuBar(hwndMain);\r
2342   }\r
2343 \r
2344   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2345   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2346 \r
2347   /* Get text area sizes */\r
2348   hdc = GetDC(hwndMain);\r
2349   if (appData.clockMode) {\r
2350     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2351   } else {\r
2352     snprintf(buf, MSG_SIZ, _("White"));\r
2353   }\r
2354   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2355   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2356   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2357   str = _("We only care about the height here");\r
2358   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2359   SelectObject(hdc, oldFont);\r
2360   ReleaseDC(hwndMain, hdc);\r
2361 \r
2362   /* Compute where everything goes */\r
2363   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2364         /* [HGM] logo: if either logo is on, reserve space for it */\r
2365         logoHeight =  2*clockSize.cy;\r
2366         leftLogoRect.left   = OUTER_MARGIN;\r
2367         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2368         leftLogoRect.top    = OUTER_MARGIN;\r
2369         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2370 \r
2371         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2372         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2373         rightLogoRect.top    = OUTER_MARGIN;\r
2374         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2375 \r
2376 \r
2377     whiteRect.left = leftLogoRect.right;\r
2378     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2379     whiteRect.top = OUTER_MARGIN;\r
2380     whiteRect.bottom = whiteRect.top + logoHeight;\r
2381 \r
2382     blackRect.right = rightLogoRect.left;\r
2383     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2384     blackRect.top = whiteRect.top;\r
2385     blackRect.bottom = whiteRect.bottom;\r
2386   } else {\r
2387     whiteRect.left = OUTER_MARGIN;\r
2388     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2389     whiteRect.top = OUTER_MARGIN;\r
2390     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2391 \r
2392     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2393     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2394     blackRect.top = whiteRect.top;\r
2395     blackRect.bottom = whiteRect.bottom;\r
2396 \r
2397     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2398   }\r
2399 \r
2400   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2401   if (appData.showButtonBar) {\r
2402     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2403       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2404   } else {\r
2405     messageRect.right = OUTER_MARGIN + boardWidth;\r
2406   }\r
2407   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2408   messageRect.bottom = messageRect.top + messageSize.cy;\r
2409 \r
2410   boardRect.left = OUTER_MARGIN;\r
2411   boardRect.right = boardRect.left + boardWidth;\r
2412   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2413   boardRect.bottom = boardRect.top + boardHeight;\r
2414 \r
2415   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2416   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2417   oldTinyLayout = tinyLayout;\r
2418   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2419   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2420     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2421   winW *= 1 + twoBoards;\r
2422   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2423   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2424   wpMain.height = winH; //       without disturbing window attachments\r
2425   GetWindowRect(hwndMain, &wrect);\r
2426   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2427                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2428 \r
2429   // [HGM] placement: let attached windows follow size change.\r
2430   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2431   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2432   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2433   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2434   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2435 \r
2436   /* compensate if menu bar wrapped */\r
2437   GetClientRect(hwndMain, &crect);\r
2438   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2439   wpMain.height += offby;\r
2440   switch (flags) {\r
2441   case WMSZ_TOPLEFT:\r
2442     SetWindowPos(hwndMain, NULL, \r
2443                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2444                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2445     break;\r
2446 \r
2447   case WMSZ_TOPRIGHT:\r
2448   case WMSZ_TOP:\r
2449     SetWindowPos(hwndMain, NULL, \r
2450                  wrect.left, wrect.bottom - wpMain.height, \r
2451                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2452     break;\r
2453 \r
2454   case WMSZ_BOTTOMLEFT:\r
2455   case WMSZ_LEFT:\r
2456     SetWindowPos(hwndMain, NULL, \r
2457                  wrect.right - wpMain.width, wrect.top, \r
2458                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2459     break;\r
2460 \r
2461   case WMSZ_BOTTOMRIGHT:\r
2462   case WMSZ_BOTTOM:\r
2463   case WMSZ_RIGHT:\r
2464   default:\r
2465     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2466                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2467     break;\r
2468   }\r
2469 \r
2470   hwndPause = NULL;\r
2471   for (i = 0; i < N_BUTTONS; i++) {\r
2472     if (buttonDesc[i].hwnd != NULL) {\r
2473       DestroyWindow(buttonDesc[i].hwnd);\r
2474       buttonDesc[i].hwnd = NULL;\r
2475     }\r
2476     if (appData.showButtonBar) {\r
2477       buttonDesc[i].hwnd =\r
2478         CreateWindow("BUTTON", buttonDesc[i].label,\r
2479                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2480                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2481                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2482                      (HMENU) buttonDesc[i].id,\r
2483                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2484       if (tinyLayout) {\r
2485         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2486                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2487                     MAKELPARAM(FALSE, 0));\r
2488       }\r
2489       if (buttonDesc[i].id == IDM_Pause)\r
2490         hwndPause = buttonDesc[i].hwnd;\r
2491       buttonDesc[i].wndproc = (WNDPROC)\r
2492         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2493     }\r
2494   }\r
2495   if (gridPen != NULL) DeleteObject(gridPen);\r
2496   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2497   if (premovePen != NULL) DeleteObject(premovePen);\r
2498   if (lineGap != 0) {\r
2499     logbrush.lbStyle = BS_SOLID;\r
2500     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2501     gridPen =\r
2502       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2503                    lineGap, &logbrush, 0, NULL);\r
2504     logbrush.lbColor = highlightSquareColor;\r
2505     highlightPen =\r
2506       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2507                    lineGap, &logbrush, 0, NULL);\r
2508 \r
2509     logbrush.lbColor = premoveHighlightColor; \r
2510     premovePen =\r
2511       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2512                    lineGap, &logbrush, 0, NULL);\r
2513 \r
2514     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2515     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2516       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2517       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2518         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2519       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2520         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2521       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2522     }\r
2523     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2524       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2525       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2526         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2527         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2528       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2529         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2530       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2531     }\r
2532   }\r
2533 \r
2534   /* [HGM] Licensing requirement */\r
2535 #ifdef GOTHIC\r
2536   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2537 #endif\r
2538 #ifdef FALCON\r
2539   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2540 #endif\r
2541   GothicPopUp( "", VariantNormal);\r
2542 \r
2543 \r
2544 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2545 \r
2546   /* Load piece bitmaps for this board size */\r
2547   for (i=0; i<=2; i++) {\r
2548     for (piece = WhitePawn;\r
2549          (int) piece < (int) BlackPawn;\r
2550          piece = (ChessSquare) ((int) piece + 1)) {\r
2551       if (pieceBitmap[i][piece] != NULL)\r
2552         DeleteObject(pieceBitmap[i][piece]);\r
2553     }\r
2554   }\r
2555 \r
2556   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2557   // Orthodox Chess pieces\r
2558   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2559   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2560   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2561   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2562   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2563   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2564   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2565   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2566   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2567   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2568   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2569   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2570   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2571   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2572   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2573   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2574     // in Shogi, Hijack the unused Queen for Lance\r
2575     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2576     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2577     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2578   } else {\r
2579     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2580     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2581     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2582   }\r
2583 \r
2584   if(squareSize <= 72 && squareSize >= 33) { \r
2585     /* A & C are available in most sizes now */\r
2586     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2587       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2588       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2589       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2590       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2591       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2592       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2593       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2594       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2595       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2596       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2597       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2598       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2599     } else { // Smirf-like\r
2600       if(gameInfo.variant == VariantSChess) {\r
2601         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2602         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2603         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2604       } else {\r
2605         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2606         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2607         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2608       }\r
2609     }\r
2610     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2611       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2612       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2613       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2614     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2615       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2616       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2617       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2618     } else { // WinBoard standard\r
2619       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2620       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2621       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2622     }\r
2623   }\r
2624 \r
2625 \r
2626   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2627     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2628     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2629     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2630     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2631     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2632     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2633     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2634     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2635     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2636     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2637     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2638     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2639     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2640     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2641     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2642     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2643     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2644     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2645     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2646     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2647     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2648     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2649     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2650     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2651     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2652     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2653     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2654     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2655     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2656     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2657 \r
2658     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2659       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2660       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2661       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2662       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2663       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2664       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2665       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2666       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2667       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2668       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2669       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2670       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2671     } else {\r
2672       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2673       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2674       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2675       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2676       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2677       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2678       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2679       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2680       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2681       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2682       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2683       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2684     }\r
2685 \r
2686   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2687     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2688     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2689     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2690     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2691     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2692     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2693     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2694     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2695     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2696     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2697     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2698     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2699     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2700     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2701   }\r
2702 \r
2703 \r
2704   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2705   /* special Shogi support in this size */\r
2706   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2707       for (piece = WhitePawn;\r
2708            (int) piece < (int) BlackPawn;\r
2709            piece = (ChessSquare) ((int) piece + 1)) {\r
2710         if (pieceBitmap[i][piece] != NULL)\r
2711           DeleteObject(pieceBitmap[i][piece]);\r
2712       }\r
2713     }\r
2714   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2715   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2716   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2717   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2718   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2719   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2720   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2721   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2722   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2723   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2724   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2725   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2726   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2727   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2728   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2729   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2730   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2731   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2732   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2733   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2734   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2735   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2736   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2737   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2738   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2739   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2740   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2741   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2742   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2743   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2744   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2745   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2746   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2747   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2748   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2749   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2750   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2751   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2752   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2753   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2754   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2755   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2756   minorSize = 0;\r
2757   }\r
2758 }\r
2759 \r
2760 HBITMAP\r
2761 PieceBitmap(ChessSquare p, int kind)\r
2762 {\r
2763   if ((int) p >= (int) BlackPawn)\r
2764     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2765 \r
2766   return pieceBitmap[kind][(int) p];\r
2767 }\r
2768 \r
2769 /***************************************************************/\r
2770 \r
2771 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2772 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2773 /*\r
2774 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2775 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2776 */\r
2777 \r
2778 VOID\r
2779 SquareToPos(int row, int column, int * x, int * y)\r
2780 {\r
2781   if (flipView) {\r
2782     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2783     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2784   } else {\r
2785     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2786     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2787   }\r
2788 }\r
2789 \r
2790 VOID\r
2791 DrawCoordsOnDC(HDC hdc)\r
2792 {\r
2793   static char files[] = "0123456789012345678901221098765432109876543210";\r
2794   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2795   char str[2] = { NULLCHAR, NULLCHAR };\r
2796   int oldMode, oldAlign, x, y, start, i;\r
2797   HFONT oldFont;\r
2798   HBRUSH oldBrush;\r
2799 \r
2800   if (!appData.showCoords)\r
2801     return;\r
2802 \r
2803   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2804 \r
2805   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2806   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2807   oldAlign = GetTextAlign(hdc);\r
2808   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2809 \r
2810   y = boardRect.top + lineGap;\r
2811   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2812 \r
2813   if(border) {\r
2814     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2815     x += border - lineGap - 4; y += squareSize - 6;\r
2816   } else\r
2817   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2818   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2819     str[0] = files[start + i];\r
2820     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2821     y += squareSize + lineGap;\r
2822   }\r
2823 \r
2824   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2825 \r
2826   if(border) {\r
2827     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2828     x += -border + 4; y += border - squareSize + 6;\r
2829   } else\r
2830   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2831   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2832     str[0] = ranks[start + i];\r
2833     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2834     x += squareSize + lineGap;\r
2835   }    \r
2836 \r
2837   SelectObject(hdc, oldBrush);\r
2838   SetBkMode(hdc, oldMode);\r
2839   SetTextAlign(hdc, oldAlign);\r
2840   SelectObject(hdc, oldFont);\r
2841 }\r
2842 \r
2843 VOID\r
2844 DrawGridOnDC(HDC hdc)\r
2845 {\r
2846   HPEN oldPen;\r
2847  \r
2848   if (lineGap != 0) {\r
2849     oldPen = SelectObject(hdc, gridPen);\r
2850     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2851     SelectObject(hdc, oldPen);\r
2852   }\r
2853 }\r
2854 \r
2855 #define HIGHLIGHT_PEN 0\r
2856 #define PREMOVE_PEN   1\r
2857 \r
2858 VOID\r
2859 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2860 {\r
2861   int x1, y1;\r
2862   HPEN oldPen, hPen;\r
2863   if (lineGap == 0) return;\r
2864   if (flipView) {\r
2865     x1 = boardRect.left +\r
2866       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2867     y1 = boardRect.top +\r
2868       lineGap/2 + y * (squareSize + lineGap) + border;\r
2869   } else {\r
2870     x1 = boardRect.left +\r
2871       lineGap/2 + x * (squareSize + lineGap) + border;\r
2872     y1 = boardRect.top +\r
2873       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2874   }\r
2875   hPen = pen ? premovePen : highlightPen;\r
2876   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2877   MoveToEx(hdc, x1, y1, NULL);\r
2878   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2879   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2880   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2881   LineTo(hdc, x1, y1);\r
2882   SelectObject(hdc, oldPen);\r
2883 }\r
2884 \r
2885 VOID\r
2886 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2887 {\r
2888   int i;\r
2889   for (i=0; i<2; i++) {\r
2890     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2891       DrawHighlightOnDC(hdc, TRUE,\r
2892                         h->sq[i].x, h->sq[i].y,\r
2893                         pen);\r
2894   }\r
2895 }\r
2896 \r
2897 /* Note: sqcolor is used only in monoMode */\r
2898 /* Note that this code is largely duplicated in woptions.c,\r
2899    function DrawSampleSquare, so that needs to be updated too */\r
2900 VOID\r
2901 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2902 {\r
2903   HBITMAP oldBitmap;\r
2904   HBRUSH oldBrush;\r
2905   int tmpSize;\r
2906 \r
2907   if (appData.blindfold) return;\r
2908 \r
2909   /* [AS] Use font-based pieces if needed */\r
2910   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2911     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2912     CreatePiecesFromFont();\r
2913 \r
2914     if( fontBitmapSquareSize == squareSize ) {\r
2915         int index = TranslatePieceToFontPiece(piece);\r
2916 \r
2917         SelectObject( tmphdc, hPieceMask[ index ] );\r
2918 \r
2919       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2920         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2921       else\r
2922         BitBlt( hdc,\r
2923             x, y,\r
2924             squareSize, squareSize,\r
2925             tmphdc,\r
2926             0, 0,\r
2927             SRCAND );\r
2928 \r
2929         SelectObject( tmphdc, hPieceFace[ index ] );\r
2930 \r
2931       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2932         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2933       else\r
2934         BitBlt( hdc,\r
2935             x, y,\r
2936             squareSize, squareSize,\r
2937             tmphdc,\r
2938             0, 0,\r
2939             SRCPAINT );\r
2940 \r
2941         return;\r
2942     }\r
2943   }\r
2944 \r
2945   if (appData.monoMode) {\r
2946     SelectObject(tmphdc, PieceBitmap(piece, \r
2947       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2948     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2949            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2950   } else {\r
2951     HBRUSH xBrush = whitePieceBrush;\r
2952     tmpSize = squareSize;\r
2953     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2954     if(minorSize &&\r
2955         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2956          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2957       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2958       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2959       x += (squareSize - minorSize)>>1;\r
2960       y += squareSize - minorSize - 2;\r
2961       tmpSize = minorSize;\r
2962     }\r
2963     if (color || appData.allWhite ) {\r
2964       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2965       if( color )\r
2966               oldBrush = SelectObject(hdc, xBrush);\r
2967       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2968       if(appData.upsideDown && color==flipView)\r
2969         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2970       else\r
2971         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2972       /* Use black for outline of white pieces */\r
2973       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2974       if(appData.upsideDown && color==flipView)\r
2975         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2976       else\r
2977         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2978     } else if(appData.pieceDirectory[0]) {\r
2979       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2980       oldBrush = SelectObject(hdc, xBrush);\r
2981       if(appData.upsideDown && color==flipView)\r
2982         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2983       else\r
2984         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2985       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2986       if(appData.upsideDown && color==flipView)\r
2987         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2988       else\r
2989         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2990     } else {\r
2991       /* Use square color for details of black pieces */\r
2992       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2993       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2994       if(appData.upsideDown && !flipView)\r
2995         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2996       else\r
2997         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2998     }\r
2999     SelectObject(hdc, oldBrush);\r
3000     SelectObject(tmphdc, oldBitmap);\r
3001   }\r
3002 }\r
3003 \r
3004 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3005 int GetBackTextureMode( int algo )\r
3006 {\r
3007     int result = BACK_TEXTURE_MODE_DISABLED;\r
3008 \r
3009     switch( algo ) \r
3010     {\r
3011         case BACK_TEXTURE_MODE_PLAIN:\r
3012             result = 1; /* Always use identity map */\r
3013             break;\r
3014         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3015             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3016             break;\r
3017     }\r
3018 \r
3019     return result;\r
3020 }\r
3021 \r
3022 /* \r
3023     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3024     to handle redraws cleanly (as random numbers would always be different).\r
3025 */\r
3026 VOID RebuildTextureSquareInfo()\r
3027 {\r
3028     BITMAP bi;\r
3029     int lite_w = 0;\r
3030     int lite_h = 0;\r
3031     int dark_w = 0;\r
3032     int dark_h = 0;\r
3033     int row;\r
3034     int col;\r
3035 \r
3036     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3037 \r
3038     if( liteBackTexture != NULL ) {\r
3039         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3040             lite_w = bi.bmWidth;\r
3041             lite_h = bi.bmHeight;\r
3042         }\r
3043     }\r
3044 \r
3045     if( darkBackTexture != NULL ) {\r
3046         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3047             dark_w = bi.bmWidth;\r
3048             dark_h = bi.bmHeight;\r
3049         }\r
3050     }\r
3051 \r
3052     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3053         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3054             if( (col + row) & 1 ) {\r
3055                 /* Lite square */\r
3056                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3057                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3058                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3059                   else\r
3060                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3061                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3062                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3063                   else\r
3064                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3065                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3066                 }\r
3067             }\r
3068             else {\r
3069                 /* Dark square */\r
3070                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3071                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3072                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3073                   else\r
3074                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3075                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3076                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3077                   else\r
3078                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3079                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3080                 }\r
3081             }\r
3082         }\r
3083     }\r
3084 }\r
3085 \r
3086 /* [AS] Arrow highlighting support */\r
3087 \r
3088 static double A_WIDTH = 5; /* Width of arrow body */\r
3089 \r
3090 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3091 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3092 \r
3093 static double Sqr( double x )\r
3094 {\r
3095     return x*x;\r
3096 }\r
3097 \r
3098 static int Round( double x )\r
3099 {\r
3100     return (int) (x + 0.5);\r
3101 }\r
3102 \r
3103 /* Draw an arrow between two points using current settings */\r
3104 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3105 {\r
3106     POINT arrow[7];\r
3107     double dx, dy, j, k, x, y;\r
3108 \r
3109     if( d_x == s_x ) {\r
3110         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3111 \r
3112         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3113         arrow[0].y = s_y;\r
3114 \r
3115         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3116         arrow[1].y = d_y - h;\r
3117 \r
3118         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3119         arrow[2].y = d_y - h;\r
3120 \r
3121         arrow[3].x = d_x;\r
3122         arrow[3].y = d_y;\r
3123 \r
3124         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3125         arrow[5].y = d_y - h;\r
3126 \r
3127         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3128         arrow[4].y = d_y - h;\r
3129 \r
3130         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3131         arrow[6].y = s_y;\r
3132     }\r
3133     else if( d_y == s_y ) {\r
3134         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3135 \r
3136         arrow[0].x = s_x;\r
3137         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3138 \r
3139         arrow[1].x = d_x - w;\r
3140         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3141 \r
3142         arrow[2].x = d_x - w;\r
3143         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3144 \r
3145         arrow[3].x = d_x;\r
3146         arrow[3].y = d_y;\r
3147 \r
3148         arrow[5].x = d_x - w;\r
3149         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3150 \r
3151         arrow[4].x = d_x - w;\r
3152         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3153 \r
3154         arrow[6].x = s_x;\r
3155         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3156     }\r
3157     else {\r
3158         /* [AS] Needed a lot of paper for this! :-) */\r
3159         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3160         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3161   \r
3162         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3163 \r
3164         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3165 \r
3166         x = s_x;\r
3167         y = s_y;\r
3168 \r
3169         arrow[0].x = Round(x - j);\r
3170         arrow[0].y = Round(y + j*dx);\r
3171 \r
3172         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3173         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3174 \r
3175         if( d_x > s_x ) {\r
3176             x = (double) d_x - k;\r
3177             y = (double) d_y - k*dy;\r
3178         }\r
3179         else {\r
3180             x = (double) d_x + k;\r
3181             y = (double) d_y + k*dy;\r
3182         }\r
3183 \r
3184         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3185 \r
3186         arrow[6].x = Round(x - j);\r
3187         arrow[6].y = Round(y + j*dx);\r
3188 \r
3189         arrow[2].x = Round(arrow[6].x + 2*j);\r
3190         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3191 \r
3192         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3193         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3194 \r
3195         arrow[4].x = d_x;\r
3196         arrow[4].y = d_y;\r
3197 \r
3198         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3199         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3200     }\r
3201 \r
3202     Polygon( hdc, arrow, 7 );\r
3203 }\r
3204 \r
3205 /* [AS] Draw an arrow between two squares */\r
3206 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3207 {\r
3208     int s_x, s_y, d_x, d_y;\r
3209     HPEN hpen;\r
3210     HPEN holdpen;\r
3211     HBRUSH hbrush;\r
3212     HBRUSH holdbrush;\r
3213     LOGBRUSH stLB;\r
3214 \r
3215     if( s_col == d_col && s_row == d_row ) {\r
3216         return;\r
3217     }\r
3218 \r
3219     /* Get source and destination points */\r
3220     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3221     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3222 \r
3223     if( d_y > s_y ) {\r
3224         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3225     }\r
3226     else if( d_y < s_y ) {\r
3227         d_y += squareSize / 2 + squareSize / 4;\r
3228     }\r
3229     else {\r
3230         d_y += squareSize / 2;\r
3231     }\r
3232 \r
3233     if( d_x > s_x ) {\r
3234         d_x += squareSize / 2 - squareSize / 4;\r
3235     }\r
3236     else if( d_x < s_x ) {\r
3237         d_x += squareSize / 2 + squareSize / 4;\r
3238     }\r
3239     else {\r
3240         d_x += squareSize / 2;\r
3241     }\r
3242 \r
3243     s_x += squareSize / 2;\r
3244     s_y += squareSize / 2;\r
3245 \r
3246     /* Adjust width */\r
3247     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3248 \r
3249     /* Draw */\r
3250     stLB.lbStyle = BS_SOLID;\r
3251     stLB.lbColor = appData.highlightArrowColor;\r
3252     stLB.lbHatch = 0;\r
3253 \r
3254     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3255     holdpen = SelectObject( hdc, hpen );\r
3256     hbrush = CreateBrushIndirect( &stLB );\r
3257     holdbrush = SelectObject( hdc, hbrush );\r
3258 \r
3259     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3260 \r
3261     SelectObject( hdc, holdpen );\r
3262     SelectObject( hdc, holdbrush );\r
3263     DeleteObject( hpen );\r
3264     DeleteObject( hbrush );\r
3265 }\r
3266 \r
3267 BOOL HasHighlightInfo()\r
3268 {\r
3269     BOOL result = FALSE;\r
3270 \r
3271     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3272         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3273     {\r
3274         result = TRUE;\r
3275     }\r
3276 \r
3277     return result;\r
3278 }\r
3279 \r
3280 BOOL IsDrawArrowEnabled()\r
3281 {\r
3282     BOOL result = FALSE;\r
3283 \r
3284     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3285         result = TRUE;\r
3286     }\r
3287 \r
3288     return result;\r
3289 }\r
3290 \r
3291 VOID DrawArrowHighlight( HDC hdc )\r
3292 {\r
3293     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3294         DrawArrowBetweenSquares( hdc,\r
3295             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3296             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3297     }\r
3298 }\r
3299 \r
3300 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3301 {\r
3302     HRGN result = NULL;\r
3303 \r
3304     if( HasHighlightInfo() ) {\r
3305         int x1, y1, x2, y2;\r
3306         int sx, sy, dx, dy;\r
3307 \r
3308         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3309         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3310 \r
3311         sx = MIN( x1, x2 );\r
3312         sy = MIN( y1, y2 );\r
3313         dx = MAX( x1, x2 ) + squareSize;\r
3314         dy = MAX( y1, y2 ) + squareSize;\r
3315 \r
3316         result = CreateRectRgn( sx, sy, dx, dy );\r
3317     }\r
3318 \r
3319     return result;\r
3320 }\r
3321 \r
3322 /*\r
3323     Warning: this function modifies the behavior of several other functions. \r
3324     \r
3325     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3326     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3327     repaint is scattered all over the place, which is not good for features such as\r
3328     "arrow highlighting" that require a full repaint of the board.\r
3329 \r
3330     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3331     user interaction, when speed is not so important) but especially to avoid errors\r
3332     in the displayed graphics.\r
3333 \r
3334     In such patched places, I always try refer to this function so there is a single\r
3335     place to maintain knowledge.\r
3336     \r
3337     To restore the original behavior, just return FALSE unconditionally.\r
3338 */\r
3339 BOOL IsFullRepaintPreferrable()\r
3340 {\r
3341     BOOL result = FALSE;\r
3342 \r
3343     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3344         /* Arrow may appear on the board */\r
3345         result = TRUE;\r
3346     }\r
3347 \r
3348     return result;\r
3349 }\r
3350 \r
3351 /* \r
3352     This function is called by DrawPosition to know whether a full repaint must\r
3353     be forced or not.\r
3354 \r
3355     Only DrawPosition may directly call this function, which makes use of \r
3356     some state information. Other function should call DrawPosition specifying \r
3357     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3358 */\r
3359 BOOL DrawPositionNeedsFullRepaint()\r
3360 {\r
3361     BOOL result = FALSE;\r
3362 \r
3363     /* \r
3364         Probably a slightly better policy would be to trigger a full repaint\r
3365         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3366         but animation is fast enough that it's difficult to notice.\r
3367     */\r
3368     if( animInfo.piece == EmptySquare ) {\r
3369         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3370             result = TRUE;\r
3371         }\r
3372     }\r
3373 \r
3374     return result;\r
3375 }\r
3376 \r
3377 static HBITMAP borderBitmap;\r
3378 \r
3379 VOID\r
3380 DrawBackgroundOnDC(HDC hdc)\r
3381 {\r
3382   \r
3383   BITMAP bi;\r
3384   HDC tmphdc;\r
3385   HBITMAP hbm;\r
3386   static char oldBorder[MSG_SIZ];\r
3387   int w = 600, h = 600, mode;\r
3388 \r
3389   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3390     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3391     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3392   }\r
3393   if(borderBitmap == NULL) { // loading failed, use white\r
3394     FillRect( hdc, &boardRect, whitePieceBrush );\r
3395     return;\r
3396   }\r
3397   tmphdc = CreateCompatibleDC(hdc);\r
3398   hbm = SelectObject(tmphdc, borderBitmap);\r
3399   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3400             w = bi.bmWidth;\r
3401             h = bi.bmHeight;\r
3402   }\r
3403   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3404   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3405                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3406   SetStretchBltMode(hdc, mode);\r
3407   SelectObject(tmphdc, hbm);\r
3408   DeleteDC(tmphdc);\r
3409 }\r
3410 \r
3411 VOID\r
3412 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3413 {\r
3414   int row, column, x, y, square_color, piece_color;\r
3415   ChessSquare piece;\r
3416   HBRUSH oldBrush;\r
3417   HDC texture_hdc = NULL;\r
3418 \r
3419   /* [AS] Initialize background textures if needed */\r
3420   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3421       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3422       if( backTextureSquareSize != squareSize \r
3423        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3424           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3425           backTextureSquareSize = squareSize;\r
3426           RebuildTextureSquareInfo();\r
3427       }\r
3428 \r
3429       texture_hdc = CreateCompatibleDC( hdc );\r
3430   }\r
3431 \r
3432   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3433     for (column = 0; column < BOARD_WIDTH; column++) {\r
3434   \r
3435       SquareToPos(row, column, &x, &y);\r
3436 \r
3437       piece = board[row][column];\r
3438 \r
3439       square_color = ((column + row) % 2) == 1;\r
3440       if( gameInfo.variant == VariantXiangqi ) {\r
3441           square_color = !InPalace(row, column);\r
3442           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3443           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3444       }\r
3445       piece_color = (int) piece < (int) BlackPawn;\r
3446 \r
3447 \r
3448       /* [HGM] holdings file: light square or black */\r
3449       if(column == BOARD_LEFT-2) {\r
3450             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3451                 square_color = 1;\r
3452             else {\r
3453                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3454                 continue;\r
3455             }\r
3456       } else\r
3457       if(column == BOARD_RGHT + 1 ) {\r
3458             if( row < gameInfo.holdingsSize )\r
3459                 square_color = 1;\r
3460             else {\r
3461                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3462                 continue;\r
3463             }\r
3464       }\r
3465       if(column == BOARD_LEFT-1 ) /* left align */\r
3466             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3467       else if( column == BOARD_RGHT) /* right align */\r
3468             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3469       else\r
3470       if (appData.monoMode) {\r
3471         if (piece == EmptySquare) {\r
3472           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3473                  square_color ? WHITENESS : BLACKNESS);\r
3474         } else {\r
3475           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3476         }\r
3477       } \r
3478       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3479           /* [AS] Draw the square using a texture bitmap */\r
3480           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3481           int r = row, c = column; // [HGM] do not flip board in flipView\r
3482           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3483 \r
3484           DrawTile( x, y, \r
3485               squareSize, squareSize, \r
3486               hdc, \r
3487               texture_hdc,\r
3488               backTextureSquareInfo[r][c].mode,\r
3489               backTextureSquareInfo[r][c].x,\r
3490               backTextureSquareInfo[r][c].y );\r
3491 \r
3492           SelectObject( texture_hdc, hbm );\r
3493 \r
3494           if (piece != EmptySquare) {\r
3495               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3496           }\r
3497       }\r
3498       else {\r
3499         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3500 \r
3501         oldBrush = SelectObject(hdc, brush );\r
3502         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3503         SelectObject(hdc, oldBrush);\r
3504         if (piece != EmptySquare)\r
3505           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3506       }\r
3507     }\r
3508   }\r
3509 \r
3510   if( texture_hdc != NULL ) {\r
3511     DeleteDC( texture_hdc );\r
3512   }\r
3513 }\r
3514 \r
3515 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3516 void fputDW(FILE *f, int x)\r
3517 {\r
3518         fputc(x     & 255, f);\r
3519         fputc(x>>8  & 255, f);\r
3520         fputc(x>>16 & 255, f);\r
3521         fputc(x>>24 & 255, f);\r
3522 }\r
3523 \r
3524 #define MAX_CLIPS 200   /* more than enough */\r
3525 \r
3526 VOID\r
3527 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3528 {\r
3529 //  HBITMAP bufferBitmap;\r
3530   BITMAP bi;\r
3531 //  RECT Rect;\r
3532   HDC tmphdc;\r
3533   HBITMAP hbm;\r
3534   int w = 100, h = 50;\r
3535 \r
3536   if(logo == NULL) {\r
3537     if(!logoHeight) return;\r
3538     FillRect( hdc, &logoRect, whitePieceBrush );\r
3539   }\r
3540 //  GetClientRect(hwndMain, &Rect);\r
3541 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3542 //                                      Rect.bottom-Rect.top+1);\r
3543   tmphdc = CreateCompatibleDC(hdc);\r
3544   hbm = SelectObject(tmphdc, logo);\r
3545   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3546             w = bi.bmWidth;\r
3547             h = bi.bmHeight;\r
3548   }\r
3549   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3550                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3551   SelectObject(tmphdc, hbm);\r
3552   DeleteDC(tmphdc);\r
3553 }\r
3554 \r
3555 VOID\r
3556 DisplayLogos()\r
3557 {\r
3558   if(logoHeight) {\r
3559         HDC hdc = GetDC(hwndMain);\r
3560         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3561         if(appData.autoLogo) {\r
3562           \r
3563           switch(gameMode) { // pick logos based on game mode\r
3564             case IcsObserving:\r
3565                 whiteLogo = second.programLogo; // ICS logo\r
3566                 blackLogo = second.programLogo;\r
3567             default:\r
3568                 break;\r
3569             case IcsPlayingWhite:\r
3570                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3571                 blackLogo = second.programLogo; // ICS logo\r
3572                 break;\r
3573             case IcsPlayingBlack:\r
3574                 whiteLogo = second.programLogo; // ICS logo\r
3575                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3576                 break;\r
3577             case TwoMachinesPlay:\r
3578                 if(first.twoMachinesColor[0] == 'b') {\r
3579                     whiteLogo = second.programLogo;\r
3580                     blackLogo = first.programLogo;\r
3581                 }\r
3582                 break;\r
3583             case MachinePlaysWhite:\r
3584                 blackLogo = userLogo;\r
3585                 break;\r
3586             case MachinePlaysBlack:\r
3587                 whiteLogo = userLogo;\r
3588                 blackLogo = first.programLogo;\r
3589           }\r
3590         }\r
3591         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3592         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3593         ReleaseDC(hwndMain, hdc);\r
3594   }\r
3595 }\r
3596 \r
3597 void\r
3598 UpdateLogos(int display)\r
3599 { // called after loading new engine(s), in tourney or from menu\r
3600   LoadLogo(&first, 0, FALSE);\r
3601   LoadLogo(&second, 1, appData.icsActive);\r
3602   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3603   if(display) DisplayLogos();\r
3604 }\r
3605 \r
3606 static HDC hdcSeek;\r
3607 \r
3608 // [HGM] seekgraph\r
3609 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3610 {\r
3611     POINT stPt;\r
3612     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3613     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3614     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3615     SelectObject( hdcSeek, hp );\r
3616 }\r
3617 \r
3618 // front-end wrapper for drawing functions to do rectangles\r
3619 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3620 {\r
3621     HPEN hp;\r
3622     RECT rc;\r
3623 \r
3624     if (hdcSeek == NULL) {\r
3625     hdcSeek = GetDC(hwndMain);\r
3626       if (!appData.monoMode) {\r
3627         SelectPalette(hdcSeek, hPal, FALSE);\r
3628         RealizePalette(hdcSeek);\r
3629       }\r
3630     }\r
3631     hp = SelectObject( hdcSeek, gridPen );\r
3632     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3633     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3634     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3635     SelectObject( hdcSeek, hp );\r
3636 }\r
3637 \r
3638 // front-end wrapper for putting text in graph\r
3639 void DrawSeekText(char *buf, int x, int y)\r
3640 {\r
3641         SIZE stSize;\r
3642         SetBkMode( hdcSeek, TRANSPARENT );\r
3643         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3644         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3645 }\r
3646 \r
3647 void DrawSeekDot(int x, int y, int color)\r
3648 {\r
3649         int square = color & 0x80;\r
3650         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3651                         color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
3652         color &= 0x7F;\r
3653         if(square)\r
3654             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3655                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3656         else\r
3657             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3658                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3659             SelectObject(hdcSeek, oldBrush);\r
3660 }\r
3661 \r
3662 void DrawSeekOpen()\r
3663 {\r
3664 }\r
3665 \r
3666 void DrawSeekClose()\r
3667 {\r
3668 }\r
3669 \r
3670 VOID\r
3671 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3672 {\r
3673   static Board lastReq[2], lastDrawn[2];\r
3674   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3675   static int lastDrawnFlipView = 0;\r
3676   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3677   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3678   HDC tmphdc;\r
3679   HDC hdcmem;\r
3680   HBITMAP bufferBitmap;\r
3681   HBITMAP oldBitmap;\r
3682   RECT Rect;\r
3683   HRGN clips[MAX_CLIPS];\r
3684   ChessSquare dragged_piece = EmptySquare;\r
3685   int nr = twoBoards*partnerUp;\r
3686 \r
3687   /* I'm undecided on this - this function figures out whether a full\r
3688    * repaint is necessary on its own, so there's no real reason to have the\r
3689    * caller tell it that.  I think this can safely be set to FALSE - but\r
3690    * if we trust the callers not to request full repaints unnessesarily, then\r
3691    * we could skip some clipping work.  In other words, only request a full\r
3692    * redraw when the majority of pieces have changed positions (ie. flip, \r
3693    * gamestart and similar)  --Hawk\r
3694    */\r
3695   Boolean fullrepaint = repaint;\r
3696 \r
3697   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3698 \r
3699   if( DrawPositionNeedsFullRepaint() ) {\r
3700       fullrepaint = TRUE;\r
3701   }\r
3702 \r
3703   if (board == NULL) {\r
3704     if (!lastReqValid[nr]) {\r
3705       return;\r
3706     }\r
3707     board = lastReq[nr];\r
3708   } else {\r
3709     CopyBoard(lastReq[nr], board);\r
3710     lastReqValid[nr] = 1;\r
3711   }\r
3712 \r
3713   if (doingSizing) {\r
3714     return;\r
3715   }\r
3716 \r
3717   if (IsIconic(hwndMain)) {\r
3718     return;\r
3719   }\r
3720 \r
3721   if (hdc == NULL) {\r
3722     hdc = GetDC(hwndMain);\r
3723     if (!appData.monoMode) {\r
3724       SelectPalette(hdc, hPal, FALSE);\r
3725       RealizePalette(hdc);\r
3726     }\r
3727     releaseDC = TRUE;\r
3728   } else {\r
3729     releaseDC = FALSE;\r
3730   }\r
3731 \r
3732   /* Create some work-DCs */\r
3733   hdcmem = CreateCompatibleDC(hdc);\r
3734   tmphdc = CreateCompatibleDC(hdc);\r
3735 \r
3736   /* If dragging is in progress, we temporarely remove the piece */\r
3737   /* [HGM] or temporarily decrease count if stacked              */\r
3738   /*       !! Moved to before board compare !!                   */\r
3739   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3740     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3741     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3742             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3743         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3744     } else \r
3745     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3746             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3747         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3748     } else \r
3749         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3750   }\r
3751 \r
3752   /* Figure out which squares need updating by comparing the \r
3753    * newest board with the last drawn board and checking if\r
3754    * flipping has changed.\r
3755    */\r
3756   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3757     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3758       for (column = 0; column < BOARD_WIDTH; column++) {\r
3759         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3760           SquareToPos(row, column, &x, &y);\r
3761           clips[num_clips++] =\r
3762             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3763         }\r
3764       }\r
3765     }\r
3766    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3767     for (i=0; i<2; i++) {\r
3768       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3769           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3770         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3771             lastDrawnHighlight.sq[i].y >= 0) {\r
3772           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3773                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3774           clips[num_clips++] =\r
3775             CreateRectRgn(x - lineGap, y - lineGap, \r
3776                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3777         }\r
3778         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3779           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3780           clips[num_clips++] =\r
3781             CreateRectRgn(x - lineGap, y - lineGap, \r
3782                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3783         }\r
3784       }\r
3785     }\r
3786     for (i=0; i<2; i++) {\r
3787       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3788           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3789         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3790             lastDrawnPremove.sq[i].y >= 0) {\r
3791           SquareToPos(lastDrawnPremove.sq[i].y,\r
3792                       lastDrawnPremove.sq[i].x, &x, &y);\r
3793           clips[num_clips++] =\r
3794             CreateRectRgn(x - lineGap, y - lineGap, \r
3795                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3796         }\r
3797         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3798             premoveHighlightInfo.sq[i].y >= 0) {\r
3799           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3800                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3801           clips[num_clips++] =\r
3802             CreateRectRgn(x - lineGap, y - lineGap, \r
3803                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3804         }\r
3805       }\r
3806     }\r
3807    } else { // nr == 1\r
3808         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3809         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3810         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3811         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3812       for (i=0; i<2; i++) {\r
3813         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3814             partnerHighlightInfo.sq[i].y >= 0) {\r
3815           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3816                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3817           clips[num_clips++] =\r
3818             CreateRectRgn(x - lineGap, y - lineGap, \r
3819                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3820         }\r
3821         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3822             oldPartnerHighlight.sq[i].y >= 0) {\r
3823           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3824                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3825           clips[num_clips++] =\r
3826             CreateRectRgn(x - lineGap, y - lineGap, \r
3827                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3828         }\r
3829       }\r
3830    }\r
3831   } else {\r
3832     fullrepaint = TRUE;\r
3833   }\r
3834 \r
3835   /* Create a buffer bitmap - this is the actual bitmap\r
3836    * being written to.  When all the work is done, we can\r
3837    * copy it to the real DC (the screen).  This avoids\r
3838    * the problems with flickering.\r
3839    */\r
3840   GetClientRect(hwndMain, &Rect);\r
3841   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3842                                         Rect.bottom-Rect.top+1);\r
3843   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3844   if (!appData.monoMode) {\r
3845     SelectPalette(hdcmem, hPal, FALSE);\r
3846   }\r
3847 \r
3848   /* Create clips for dragging */\r
3849   if (!fullrepaint) {\r
3850     if (dragInfo.from.x >= 0) {\r
3851       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3852       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3853     }\r
3854     if (dragInfo.start.x >= 0) {\r
3855       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3856       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3857     }\r
3858     if (dragInfo.pos.x >= 0) {\r
3859       x = dragInfo.pos.x - squareSize / 2;\r
3860       y = dragInfo.pos.y - squareSize / 2;\r
3861       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3862     }\r
3863     if (dragInfo.lastpos.x >= 0) {\r
3864       x = dragInfo.lastpos.x - squareSize / 2;\r
3865       y = dragInfo.lastpos.y - squareSize / 2;\r
3866       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3867     }\r
3868   }\r
3869 \r
3870   /* Are we animating a move?  \r
3871    * If so, \r
3872    *   - remove the piece from the board (temporarely)\r
3873    *   - calculate the clipping region\r
3874    */\r
3875   if (!fullrepaint) {\r
3876     if (animInfo.piece != EmptySquare) {\r
3877       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3878       x = boardRect.left + animInfo.lastpos.x;\r
3879       y = boardRect.top + animInfo.lastpos.y;\r
3880       x2 = boardRect.left + animInfo.pos.x;\r
3881       y2 = boardRect.top + animInfo.pos.y;\r
3882       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3883       /* Slight kludge.  The real problem is that after AnimateMove is\r
3884          done, the position on the screen does not match lastDrawn.\r
3885          This currently causes trouble only on e.p. captures in\r
3886          atomic, where the piece moves to an empty square and then\r
3887          explodes.  The old and new positions both had an empty square\r
3888          at the destination, but animation has drawn a piece there and\r
3889          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3890       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3891     }\r
3892   }\r
3893 \r
3894   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3895   if (num_clips == 0)\r
3896     fullrepaint = TRUE;\r
3897 \r
3898   /* Set clipping on the memory DC */\r
3899   if (!fullrepaint) {\r
3900     SelectClipRgn(hdcmem, clips[0]);\r
3901     for (x = 1; x < num_clips; x++) {\r
3902       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3903         abort();  // this should never ever happen!\r
3904     }\r
3905   }\r
3906 \r
3907   /* Do all the drawing to the memory DC */\r
3908   if(explodeInfo.radius) { // [HGM] atomic\r
3909         HBRUSH oldBrush;\r
3910         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3911         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3912         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3913         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3914         x += squareSize/2;\r
3915         y += squareSize/2;\r
3916         if(!fullrepaint) {\r
3917           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3918           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3919         }\r
3920         DrawGridOnDC(hdcmem);\r
3921         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3922         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3923         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3924         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3925         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3926         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3927         SelectObject(hdcmem, oldBrush);\r
3928   } else {\r
3929     if(border) DrawBackgroundOnDC(hdcmem);\r
3930     DrawGridOnDC(hdcmem);\r
3931     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3932         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3933         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3934     } else {\r
3935         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3936         oldPartnerHighlight = partnerHighlightInfo;\r
3937     }\r
3938     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3939   }\r
3940   if(nr == 0) // [HGM] dual: markers only on left board\r
3941   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3942     for (column = 0; column < BOARD_WIDTH; column++) {\r
3943         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3944             HBRUSH oldBrush = SelectObject(hdcmem, \r
3945                         marker[row][column] == 2 ? markerBrush : explodeBrush);\r
3946             SquareToPos(row, column, &x, &y);\r
3947             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3948                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3949             SelectObject(hdcmem, oldBrush);\r
3950         }\r
3951     }\r
3952   }\r
3953 \r
3954   if( appData.highlightMoveWithArrow ) {\r
3955     DrawArrowHighlight(hdcmem);\r
3956   }\r
3957 \r
3958   DrawCoordsOnDC(hdcmem);\r
3959 \r
3960   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3961                  /* to make sure lastDrawn contains what is actually drawn */\r
3962 \r
3963   /* Put the dragged piece back into place and draw it (out of place!) */\r
3964     if (dragged_piece != EmptySquare) {\r
3965     /* [HGM] or restack */\r
3966     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3967                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3968     else\r
3969     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3970                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3971     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3972     x = dragInfo.pos.x - squareSize / 2;\r
3973     y = dragInfo.pos.y - squareSize / 2;\r
3974     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3975                   ((int) dragInfo.piece < (int) BlackPawn), \r
3976                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3977   }   \r
3978   \r
3979   /* Put the animated piece back into place and draw it */\r
3980   if (animInfo.piece != EmptySquare) {\r
3981     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3982     x = boardRect.left + animInfo.pos.x;\r
3983     y = boardRect.top + animInfo.pos.y;\r
3984     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3985                   ((int) animInfo.piece < (int) BlackPawn),\r
3986                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3987   }\r
3988 \r
3989   /* Release the bufferBitmap by selecting in the old bitmap \r
3990    * and delete the memory DC\r
3991    */\r
3992   SelectObject(hdcmem, oldBitmap);\r
3993   DeleteDC(hdcmem);\r
3994 \r
3995   /* Set clipping on the target DC */\r
3996   if (!fullrepaint) {\r
3997     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
3998         RECT rect;\r
3999         GetRgnBox(clips[x], &rect);\r
4000         DeleteObject(clips[x]);\r
4001         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4002                           rect.right + wpMain.width/2, rect.bottom);\r
4003     }\r
4004     SelectClipRgn(hdc, clips[0]);\r
4005     for (x = 1; x < num_clips; x++) {\r
4006       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4007         abort();   // this should never ever happen!\r
4008     } \r
4009   }\r
4010 \r
4011   /* Copy the new bitmap onto the screen in one go.\r
4012    * This way we avoid any flickering\r
4013    */\r
4014   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4015   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4016          boardRect.right - boardRect.left,\r
4017          boardRect.bottom - boardRect.top,\r
4018          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4019   if(saveDiagFlag) { \r
4020     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4021     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4022 \r
4023     GetObject(bufferBitmap, sizeof(b), &b);\r
4024     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4025         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4026         bih.biWidth = b.bmWidth;\r
4027         bih.biHeight = b.bmHeight;\r
4028         bih.biPlanes = 1;\r
4029         bih.biBitCount = b.bmBitsPixel;\r
4030         bih.biCompression = 0;\r
4031         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4032         bih.biXPelsPerMeter = 0;\r
4033         bih.biYPelsPerMeter = 0;\r
4034         bih.biClrUsed = 0;\r
4035         bih.biClrImportant = 0;\r
4036 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4037 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4038         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4039 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4040 \r
4041         wb = b.bmWidthBytes;\r
4042         // count colors\r
4043         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4044                 int k = ((int*) pData)[i];\r
4045                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4046                 if(j >= 16) break;\r
4047                 color[j] = k;\r
4048                 if(j >= nrColors) nrColors = j+1;\r
4049         }\r
4050         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4051                 INT p = 0;\r
4052                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4053                     for(w=0; w<(wb>>2); w+=2) {\r
4054                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4055                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4056                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4057                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4058                         pData[p++] = m | j<<4;\r
4059                     }\r
4060                     while(p&3) pData[p++] = 0;\r
4061                 }\r
4062                 fac = 3;\r
4063                 wb = ((wb+31)>>5)<<2;\r
4064         }\r
4065         // write BITMAPFILEHEADER\r
4066         fprintf(diagFile, "BM");\r
4067         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4068         fputDW(diagFile, 0);\r
4069         fputDW(diagFile, 0x36 + (fac?64:0));\r
4070         // write BITMAPINFOHEADER\r
4071         fputDW(diagFile, 40);\r
4072         fputDW(diagFile, b.bmWidth);\r
4073         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4074         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4075         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4076         fputDW(diagFile, 0);\r
4077         fputDW(diagFile, 0);\r
4078         fputDW(diagFile, 0);\r
4079         fputDW(diagFile, 0);\r
4080         fputDW(diagFile, 0);\r
4081         fputDW(diagFile, 0);\r
4082         // write color table\r
4083         if(fac)\r
4084         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4085         // write bitmap data\r
4086         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4087                 fputc(pData[i], diagFile);\r
4088         free(pData);\r
4089      }\r
4090   }\r
4091 \r
4092   SelectObject(tmphdc, oldBitmap);\r
4093 \r
4094   /* Massive cleanup */\r
4095   for (x = 0; x < num_clips; x++)\r
4096     DeleteObject(clips[x]);\r
4097 \r
4098   DeleteDC(tmphdc);\r
4099   DeleteObject(bufferBitmap);\r
4100 \r
4101   if (releaseDC) \r
4102     ReleaseDC(hwndMain, hdc);\r
4103   \r
4104   if (lastDrawnFlipView != flipView && nr == 0) {\r
4105     if (flipView)\r
4106       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4107     else\r
4108       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4109   }\r
4110 \r
4111 /*  CopyBoard(lastDrawn, board);*/\r
4112   lastDrawnHighlight = highlightInfo;\r
4113   lastDrawnPremove   = premoveHighlightInfo;\r
4114   lastDrawnFlipView = flipView;\r
4115   lastDrawnValid[nr] = 1;\r
4116 }\r
4117 \r
4118 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4119 int\r
4120 SaveDiagram(f)\r
4121      FILE *f;\r
4122 {\r
4123     saveDiagFlag = 1; diagFile = f;\r
4124     HDCDrawPosition(NULL, TRUE, NULL);\r
4125     saveDiagFlag = 0;\r
4126 \r
4127     fclose(f);\r
4128     return TRUE;\r
4129 }\r
4130 \r
4131 \r
4132 /*---------------------------------------------------------------------------*\\r
4133 | CLIENT PAINT PROCEDURE\r
4134 |   This is the main event-handler for the WM_PAINT message.\r
4135 |\r
4136 \*---------------------------------------------------------------------------*/\r
4137 VOID\r
4138 PaintProc(HWND hwnd)\r
4139 {\r
4140   HDC         hdc;\r
4141   PAINTSTRUCT ps;\r
4142   HFONT       oldFont;\r
4143 \r
4144   if((hdc = BeginPaint(hwnd, &ps))) {\r
4145     if (IsIconic(hwnd)) {\r
4146       DrawIcon(hdc, 2, 2, iconCurrent);\r
4147     } else {\r
4148       if (!appData.monoMode) {\r
4149         SelectPalette(hdc, hPal, FALSE);\r
4150         RealizePalette(hdc);\r
4151       }\r
4152       HDCDrawPosition(hdc, 1, NULL);\r
4153       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4154         flipView = !flipView; partnerUp = !partnerUp;\r
4155         HDCDrawPosition(hdc, 1, NULL);\r
4156         flipView = !flipView; partnerUp = !partnerUp;\r
4157       }\r
4158       oldFont =\r
4159         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4160       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4161                  ETO_CLIPPED|ETO_OPAQUE,\r
4162                  &messageRect, messageText, strlen(messageText), NULL);\r
4163       SelectObject(hdc, oldFont);\r
4164       DisplayBothClocks();\r
4165       DisplayLogos();\r
4166     }\r
4167     EndPaint(hwnd,&ps);\r
4168   }\r
4169 \r
4170   return;\r
4171 }\r
4172 \r
4173 \r
4174 /*\r
4175  * If the user selects on a border boundary, return -1; if off the board,\r
4176  *   return -2.  Otherwise map the event coordinate to the square.\r
4177  * The offset boardRect.left or boardRect.top must already have been\r
4178  *   subtracted from x.\r
4179  */\r
4180 int EventToSquare(x, limit)\r
4181      int x, limit;\r
4182 {\r
4183   if (x <= border)\r
4184     return -2;\r
4185   if (x < lineGap + border)\r
4186     return -1;\r
4187   x -= lineGap + border;\r
4188   if ((x % (squareSize + lineGap)) >= squareSize)\r
4189     return -1;\r
4190   x /= (squareSize + lineGap);\r
4191     if (x >= limit)\r
4192     return -2;\r
4193   return x;\r
4194 }\r
4195 \r
4196 typedef struct {\r
4197   char piece;\r
4198   int command;\r
4199   char* name;\r
4200 } DropEnable;\r
4201 \r
4202 DropEnable dropEnables[] = {\r
4203   { 'P', DP_Pawn, N_("Pawn") },\r
4204   { 'N', DP_Knight, N_("Knight") },\r
4205   { 'B', DP_Bishop, N_("Bishop") },\r
4206   { 'R', DP_Rook, N_("Rook") },\r
4207   { 'Q', DP_Queen, N_("Queen") },\r
4208 };\r
4209 \r
4210 VOID\r
4211 SetupDropMenu(HMENU hmenu)\r
4212 {\r
4213   int i, count, enable;\r
4214   char *p;\r
4215   extern char white_holding[], black_holding[];\r
4216   char item[MSG_SIZ];\r
4217 \r
4218   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4219     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4220                dropEnables[i].piece);\r
4221     count = 0;\r
4222     while (p && *p++ == dropEnables[i].piece) count++;\r
4223       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4224     enable = count > 0 || !appData.testLegality\r
4225       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4226                       && !appData.icsActive);\r
4227     ModifyMenu(hmenu, dropEnables[i].command,\r
4228                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4229                dropEnables[i].command, item);\r
4230   }\r
4231 }\r
4232 \r
4233 void DragPieceBegin(int x, int y, Boolean instantly)\r
4234 {\r
4235       dragInfo.lastpos.x = boardRect.left + x;\r
4236       dragInfo.lastpos.y = boardRect.top + y;\r
4237       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4238       dragInfo.from.x = fromX;\r
4239       dragInfo.from.y = fromY;\r
4240       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4241       dragInfo.start = dragInfo.from;\r
4242       SetCapture(hwndMain);\r
4243 }\r
4244 \r
4245 void DragPieceEnd(int x, int y)\r
4246 {\r
4247     ReleaseCapture();\r
4248     dragInfo.start.x = dragInfo.start.y = -1;\r
4249     dragInfo.from = dragInfo.start;\r
4250     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4251 }\r
4252 \r
4253 void ChangeDragPiece(ChessSquare piece)\r
4254 {\r
4255     dragInfo.piece = piece;\r
4256 }\r
4257 \r
4258 /* Event handler for mouse messages */\r
4259 VOID\r
4260 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4261 {\r
4262   int x, y, menuNr;\r
4263   POINT pt;\r
4264   static int recursive = 0;\r
4265   HMENU hmenu;\r
4266   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4267 \r
4268   if (recursive) {\r
4269     if (message == WM_MBUTTONUP) {\r
4270       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4271          to the middle button: we simulate pressing the left button too!\r
4272          */\r
4273       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4274       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4275     }\r
4276     return;\r
4277   }\r
4278   recursive++;\r
4279   \r
4280   pt.x = LOWORD(lParam);\r
4281   pt.y = HIWORD(lParam);\r
4282   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4283   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4284   if (!flipView && y >= 0) {\r
4285     y = BOARD_HEIGHT - 1 - y;\r
4286   }\r
4287   if (flipView && x >= 0) {\r
4288     x = BOARD_WIDTH - 1 - x;\r
4289   }\r
4290 \r
4291   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4292   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4293 \r
4294   switch (message) {\r
4295   case WM_LBUTTONDOWN:\r
4296       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4297         ClockClick(flipClock); break;\r
4298       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4299         ClockClick(!flipClock); break;\r
4300       }\r
4301       dragInfo.start.x = dragInfo.start.y = -1;\r
4302       dragInfo.from = dragInfo.start;\r
4303     if(fromX == -1 && frozen) { // not sure where this is for\r
4304                 fromX = fromY = -1; \r
4305       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4306       break;\r
4307     }\r
4308       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4309       DrawPosition(TRUE, NULL);\r
4310     break;\r
4311 \r
4312   case WM_LBUTTONUP:\r
4313       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4314       DrawPosition(TRUE, NULL);\r
4315     break;\r
4316 \r
4317   case WM_MOUSEMOVE:\r
4318     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4319     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4320     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4321     if ((appData.animateDragging || appData.highlightDragging)\r
4322         && (wParam & MK_LBUTTON)\r
4323         && dragInfo.from.x >= 0) \r
4324     {\r
4325       BOOL full_repaint = FALSE;\r
4326 \r
4327       if (appData.animateDragging) {\r
4328         dragInfo.pos = pt;\r
4329       }\r
4330       if (appData.highlightDragging) {\r
4331         SetHighlights(fromX, fromY, x, y);\r
4332         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4333             full_repaint = TRUE;\r
4334         }\r
4335       }\r
4336       \r
4337       DrawPosition( full_repaint, NULL);\r
4338       \r
4339       dragInfo.lastpos = dragInfo.pos;\r
4340     }\r
4341     break;\r
4342 \r
4343   case WM_MOUSEWHEEL: // [DM]\r
4344     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4345        /* Mouse Wheel is being rolled forward\r
4346         * Play moves forward\r
4347         */\r
4348        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4349                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4350        /* Mouse Wheel is being rolled backward\r
4351         * Play moves backward\r
4352         */\r
4353        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4354                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4355     }\r
4356     break;\r
4357 \r
4358   case WM_MBUTTONUP:\r
4359   case WM_RBUTTONUP:\r
4360     ReleaseCapture();\r
4361     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4362     break;\r
4363  \r
4364   case WM_MBUTTONDOWN:\r
4365   case WM_RBUTTONDOWN:\r
4366     ErrorPopDown();\r
4367     ReleaseCapture();\r
4368     fromX = fromY = -1;\r
4369     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4370     dragInfo.start.x = dragInfo.start.y = -1;\r
4371     dragInfo.from = dragInfo.start;\r
4372     dragInfo.lastpos = dragInfo.pos;\r
4373     if (appData.highlightDragging) {\r
4374       ClearHighlights();\r
4375     }\r
4376     if(y == -2) {\r
4377       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4378       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4379           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4380       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4381           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4382       }\r
4383       break;\r
4384     }\r
4385     DrawPosition(TRUE, NULL);\r
4386 \r
4387     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4388     switch (menuNr) {\r
4389     case 0:\r
4390       if (message == WM_MBUTTONDOWN) {\r
4391         buttonCount = 3;  /* even if system didn't think so */\r
4392         if (wParam & MK_SHIFT) \r
4393           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4394         else\r
4395           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4396       } else { /* message == WM_RBUTTONDOWN */\r
4397         /* Just have one menu, on the right button.  Windows users don't\r
4398            think to try the middle one, and sometimes other software steals\r
4399            it, or it doesn't really exist. */\r
4400         if(gameInfo.variant != VariantShogi)\r
4401             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4402         else\r
4403             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4404       }\r
4405       break;\r
4406     case 2:\r
4407       SetCapture(hwndMain);\r
4408       break;\r
4409     case 1:\r
4410       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4411       SetupDropMenu(hmenu);\r
4412       MenuPopup(hwnd, pt, hmenu, -1);\r
4413     default:\r
4414       break;\r
4415     }\r
4416     break;\r
4417   }\r
4418 \r
4419   recursive--;\r
4420 }\r
4421 \r
4422 /* Preprocess messages for buttons in main window */\r
4423 LRESULT CALLBACK\r
4424 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4425 {\r
4426   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4427   int i, dir;\r
4428 \r
4429   for (i=0; i<N_BUTTONS; i++) {\r
4430     if (buttonDesc[i].id == id) break;\r
4431   }\r
4432   if (i == N_BUTTONS) return 0;\r
4433   switch (message) {\r
4434   case WM_KEYDOWN:\r
4435     switch (wParam) {\r
4436     case VK_LEFT:\r
4437     case VK_RIGHT:\r
4438       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4439       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4440       return TRUE;\r
4441     }\r
4442     break;\r
4443   case WM_CHAR:\r
4444     switch (wParam) {\r
4445     case '\r':\r
4446       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4447       return TRUE;\r
4448     default:\r
4449       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4450         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4451         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4452         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4453         SetFocus(h);\r
4454         SendMessage(h, WM_CHAR, wParam, lParam);\r
4455         return TRUE;\r
4456       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4457         TypeInEvent((char)wParam);\r
4458       }\r
4459       break;\r
4460     }\r
4461     break;\r
4462   }\r
4463   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4464 }\r
4465 \r
4466 /* Process messages for Promotion dialog box */\r
4467 LRESULT CALLBACK\r
4468 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4469 {\r
4470   char promoChar;\r
4471 \r
4472   switch (message) {\r
4473   case WM_INITDIALOG: /* message: initialize dialog box */\r
4474     /* Center the dialog over the application window */\r
4475     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4476     Translate(hDlg, DLG_PromotionKing);\r
4477     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4478       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4479        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4480        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4481                SW_SHOW : SW_HIDE);\r
4482     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4483     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4484        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4485          PieceToChar(WhiteAngel) != '~') ||\r
4486         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4487          PieceToChar(BlackAngel) != '~')   ) ?\r
4488                SW_SHOW : SW_HIDE);\r
4489     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4490        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4491          PieceToChar(WhiteMarshall) != '~') ||\r
4492         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4493          PieceToChar(BlackMarshall) != '~')   ) ?\r
4494                SW_SHOW : SW_HIDE);\r
4495     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4496     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4497        gameInfo.variant != VariantShogi ?\r
4498                SW_SHOW : SW_HIDE);\r
4499     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4500        gameInfo.variant != VariantShogi ?\r
4501                SW_SHOW : SW_HIDE);\r
4502     if(gameInfo.variant == VariantShogi) {\r
4503         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4504         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4505         SetWindowText(hDlg, "Promote?");\r
4506     }\r
4507     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4508        gameInfo.variant == VariantSuper ?\r
4509                SW_SHOW : SW_HIDE);\r
4510     return TRUE;\r
4511 \r
4512   case WM_COMMAND: /* message: received a command */\r
4513     switch (LOWORD(wParam)) {\r
4514     case IDCANCEL:\r
4515       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4516       ClearHighlights();\r
4517       DrawPosition(FALSE, NULL);\r
4518       return TRUE;\r
4519     case PB_King:\r
4520       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4521       break;\r
4522     case PB_Queen:\r
4523       promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4524       break;\r
4525     case PB_Rook:\r
4526       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4527       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4528       break;\r
4529     case PB_Bishop:\r
4530       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4531       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4532       break;\r
4533     case PB_Chancellor:\r
4534       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4535       break;\r
4536     case PB_Archbishop:\r
4537       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4538       break;\r
4539     case PB_Knight:\r
4540       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);\r
4541       break;\r
4542     default:\r
4543       return FALSE;\r
4544     }\r
4545     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4546     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4547     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4548     fromX = fromY = -1;\r
4549     if (!appData.highlightLastMove) {\r
4550       ClearHighlights();\r
4551       DrawPosition(FALSE, NULL);\r
4552     }\r
4553     return TRUE;\r
4554   }\r
4555   return FALSE;\r
4556 }\r
4557 \r
4558 /* Pop up promotion dialog */\r
4559 VOID\r
4560 PromotionPopup(HWND hwnd)\r
4561 {\r
4562   FARPROC lpProc;\r
4563 \r
4564   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4565   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4566     hwnd, (DLGPROC)lpProc);\r
4567   FreeProcInstance(lpProc);\r
4568 }\r
4569 \r
4570 void\r
4571 PromotionPopUp()\r
4572 {\r
4573   DrawPosition(TRUE, NULL);\r
4574   PromotionPopup(hwndMain);\r
4575 }\r
4576 \r
4577 VOID\r
4578 LoadGameDialog(HWND hwnd, char* title)\r
4579 {\r
4580   UINT number = 0;\r
4581   FILE *f;\r
4582   char fileTitle[MSG_SIZ];\r
4583   f = OpenFileDialog(hwnd, "rb", "",\r
4584                      appData.oldSaveStyle ? "gam" : "pgn",\r
4585                      GAME_FILT,\r
4586                      title, &number, fileTitle, NULL);\r
4587   if (f != NULL) {\r
4588     cmailMsgLoaded = FALSE;\r
4589     if (number == 0) {\r
4590       int error = GameListBuild(f);\r
4591       if (error) {\r
4592         DisplayError(_("Cannot build game list"), error);\r
4593       } else if (!ListEmpty(&gameList) &&\r
4594                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4595         GameListPopUp(f, fileTitle);\r
4596         return;\r
4597       }\r
4598       GameListDestroy();\r
4599       number = 1;\r
4600     }\r
4601     LoadGame(f, number, fileTitle, FALSE);\r
4602   }\r
4603 }\r
4604 \r
4605 int get_term_width()\r
4606 {\r
4607     HDC hdc;\r
4608     TEXTMETRIC tm;\r
4609     RECT rc;\r
4610     HFONT hfont, hold_font;\r
4611     LOGFONT lf;\r
4612     HWND hText;\r
4613 \r
4614     if (hwndConsole)\r
4615         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4616     else\r
4617         return 79;\r
4618 \r
4619     // get the text metrics\r
4620     hdc = GetDC(hText);\r
4621     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4622     if (consoleCF.dwEffects & CFE_BOLD)\r
4623         lf.lfWeight = FW_BOLD;\r
4624     if (consoleCF.dwEffects & CFE_ITALIC)\r
4625         lf.lfItalic = TRUE;\r
4626     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4627         lf.lfStrikeOut = TRUE;\r
4628     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4629         lf.lfUnderline = TRUE;\r
4630     hfont = CreateFontIndirect(&lf);\r
4631     hold_font = SelectObject(hdc, hfont);\r
4632     GetTextMetrics(hdc, &tm);\r
4633     SelectObject(hdc, hold_font);\r
4634     DeleteObject(hfont);\r
4635     ReleaseDC(hText, hdc);\r
4636 \r
4637     // get the rectangle\r
4638     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4639 \r
4640     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4641 }\r
4642 \r
4643 void UpdateICSWidth(HWND hText)\r
4644 {\r
4645     LONG old_width, new_width;\r
4646 \r
4647     new_width = get_term_width(hText, FALSE);\r
4648     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4649     if (new_width != old_width)\r
4650     {\r
4651         ics_update_width(new_width);\r
4652         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4653     }\r
4654 }\r
4655 \r
4656 VOID\r
4657 ChangedConsoleFont()\r
4658 {\r
4659   CHARFORMAT cfmt;\r
4660   CHARRANGE tmpsel, sel;\r
4661   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4662   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4663   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4664   PARAFORMAT paraf;\r
4665 \r
4666   cfmt.cbSize = sizeof(CHARFORMAT);\r
4667   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4668     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4669                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4670   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4671    * size.  This was undocumented in the version of MSVC++ that I had\r
4672    * when I wrote the code, but is apparently documented now.\r
4673    */\r
4674   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4675   cfmt.bCharSet = f->lf.lfCharSet;\r
4676   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4677   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4678   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4679   /* Why are the following seemingly needed too? */\r
4680   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4681   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4682   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4683   tmpsel.cpMin = 0;\r
4684   tmpsel.cpMax = -1; /*999999?*/\r
4685   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4686   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4687   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4688    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4689    */\r
4690   paraf.cbSize = sizeof(paraf);\r
4691   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4692   paraf.dxStartIndent = 0;\r
4693   paraf.dxOffset = WRAP_INDENT;\r
4694   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4695   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4696   UpdateICSWidth(hText);\r
4697 }\r
4698 \r
4699 /*---------------------------------------------------------------------------*\\r
4700  *\r
4701  * Window Proc for main window\r
4702  *\r
4703 \*---------------------------------------------------------------------------*/\r
4704 \r
4705 /* Process messages for main window, etc. */\r
4706 LRESULT CALLBACK\r
4707 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4708 {\r
4709   FARPROC lpProc;\r
4710   int wmId, wmEvent;\r
4711   char *defName;\r
4712   FILE *f;\r
4713   UINT number;\r
4714   char fileTitle[MSG_SIZ];\r
4715   static SnapData sd;\r
4716   static int peek=0;\r
4717 \r
4718   switch (message) {\r
4719 \r
4720   case WM_PAINT: /* message: repaint portion of window */\r
4721     PaintProc(hwnd);\r
4722     break;\r
4723 \r
4724   case WM_ERASEBKGND:\r
4725     if (IsIconic(hwnd)) {\r
4726       /* Cheat; change the message */\r
4727       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4728     } else {\r
4729       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4730     }\r
4731     break;\r
4732 \r
4733   case WM_LBUTTONDOWN:\r
4734   case WM_MBUTTONDOWN:\r
4735   case WM_RBUTTONDOWN:\r
4736   case WM_LBUTTONUP:\r
4737   case WM_MBUTTONUP:\r
4738   case WM_RBUTTONUP:\r
4739   case WM_MOUSEMOVE:\r
4740   case WM_MOUSEWHEEL:\r
4741     MouseEvent(hwnd, message, wParam, lParam);\r
4742     break;\r
4743 \r
4744   case WM_KEYUP:\r
4745     if((char)wParam == '\b') {\r
4746       ForwardEvent(); peek = 0;\r
4747     }\r
4748 \r
4749     JAWS_KBUP_NAVIGATION\r
4750 \r
4751     break;\r
4752 \r
4753   case WM_KEYDOWN:\r
4754     if((char)wParam == '\b') {\r
4755       if(!peek) BackwardEvent(), peek = 1;\r
4756     }\r
4757 \r
4758     JAWS_KBDOWN_NAVIGATION\r
4759 \r
4760     break;\r
4761 \r
4762   case WM_CHAR:\r
4763     \r
4764     JAWS_ALT_INTERCEPT\r
4765 \r
4766     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4767         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4768         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4769         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4770         SetFocus(h);\r
4771         SendMessage(h, message, wParam, lParam);\r
4772     } else if(lParam != KF_REPEAT) {\r
4773         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4774                 TypeInEvent((char)wParam);\r
4775         } else if((char)wParam == 003) CopyGameToClipboard();\r
4776          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4777     }\r
4778 \r
4779     break;\r
4780 \r
4781   case WM_PALETTECHANGED:\r
4782     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4783       int nnew;\r
4784       HDC hdc = GetDC(hwndMain);\r
4785       SelectPalette(hdc, hPal, TRUE);\r
4786       nnew = RealizePalette(hdc);\r
4787       if (nnew > 0) {\r
4788         paletteChanged = TRUE;\r
4789         InvalidateRect(hwnd, &boardRect, FALSE);\r
4790       }\r
4791       ReleaseDC(hwnd, hdc);\r
4792     }\r
4793     break;\r
4794 \r
4795   case WM_QUERYNEWPALETTE:\r
4796     if (!appData.monoMode /*&& paletteChanged*/) {\r
4797       int nnew;\r
4798       HDC hdc = GetDC(hwndMain);\r
4799       paletteChanged = FALSE;\r
4800       SelectPalette(hdc, hPal, FALSE);\r
4801       nnew = RealizePalette(hdc);\r
4802       if (nnew > 0) {\r
4803         InvalidateRect(hwnd, &boardRect, FALSE);\r
4804       }\r
4805       ReleaseDC(hwnd, hdc);\r
4806       return TRUE;\r
4807     }\r
4808     return FALSE;\r
4809 \r
4810   case WM_COMMAND: /* message: command from application menu */\r
4811     wmId    = LOWORD(wParam);\r
4812     wmEvent = HIWORD(wParam);\r
4813 \r
4814     switch (wmId) {\r
4815     case IDM_NewGame:\r
4816       ResetGameEvent();\r
4817       SAY("new game enter a move to play against the computer with white");\r
4818       break;\r
4819 \r
4820     case IDM_NewGameFRC:\r
4821       if( NewGameFRC() == 0 ) {\r
4822         ResetGameEvent();\r
4823       }\r
4824       break;\r
4825 \r
4826     case IDM_NewVariant:\r
4827       NewVariantPopup(hwnd);\r
4828       break;\r
4829 \r
4830     case IDM_LoadGame:\r
4831       LoadGameDialog(hwnd, _("Load Game from File"));\r
4832       break;\r
4833 \r
4834     case IDM_LoadNextGame:\r
4835       ReloadGame(1);\r
4836       break;\r
4837 \r
4838     case IDM_LoadPrevGame:\r
4839       ReloadGame(-1);\r
4840       break;\r
4841 \r
4842     case IDM_ReloadGame:\r
4843       ReloadGame(0);\r
4844       break;\r
4845 \r
4846     case IDM_LoadPosition:\r
4847       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4848         Reset(FALSE, TRUE);\r
4849       }\r
4850       number = 1;\r
4851       f = OpenFileDialog(hwnd, "rb", "",\r
4852                          appData.oldSaveStyle ? "pos" : "fen",\r
4853                          POSITION_FILT,\r
4854                          _("Load Position from File"), &number, fileTitle, NULL);\r
4855       if (f != NULL) {\r
4856         LoadPosition(f, number, fileTitle);\r
4857       }\r
4858       break;\r
4859 \r
4860     case IDM_LoadNextPosition:\r
4861       ReloadPosition(1);\r
4862       break;\r
4863 \r
4864     case IDM_LoadPrevPosition:\r
4865       ReloadPosition(-1);\r
4866       break;\r
4867 \r
4868     case IDM_ReloadPosition:\r
4869       ReloadPosition(0);\r
4870       break;\r
4871 \r
4872     case IDM_SaveGame:\r
4873       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4874       f = OpenFileDialog(hwnd, "a", defName,\r
4875                          appData.oldSaveStyle ? "gam" : "pgn",\r
4876                          GAME_FILT,\r
4877                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4878       if (f != NULL) {\r
4879         SaveGame(f, 0, "");\r
4880       }\r
4881       break;\r
4882 \r
4883     case IDM_SavePosition:\r
4884       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4885       f = OpenFileDialog(hwnd, "a", defName,\r
4886                          appData.oldSaveStyle ? "pos" : "fen",\r
4887                          POSITION_FILT,\r
4888                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4889       if (f != NULL) {\r
4890         SavePosition(f, 0, "");\r
4891       }\r
4892       break;\r
4893 \r
4894     case IDM_SaveDiagram:\r
4895       defName = "diagram";\r
4896       f = OpenFileDialog(hwnd, "wb", defName,\r
4897                          "bmp",\r
4898                          DIAGRAM_FILT,\r
4899                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4900       if (f != NULL) {\r
4901         SaveDiagram(f);\r
4902       }\r
4903       break;\r
4904 \r
4905     case IDM_CreateBook:\r
4906       CreateBookEvent();\r
4907       break;\r
4908 \r
4909     case IDM_CopyGame:\r
4910       CopyGameToClipboard();\r
4911       break;\r
4912 \r
4913     case IDM_PasteGame:\r
4914       PasteGameFromClipboard();\r
4915       break;\r
4916 \r
4917     case IDM_CopyGameListToClipboard:\r
4918       CopyGameListToClipboard();\r
4919       break;\r
4920 \r
4921     /* [AS] Autodetect FEN or PGN data */\r
4922     case IDM_PasteAny:\r
4923       PasteGameOrFENFromClipboard();\r
4924       break;\r
4925 \r
4926     /* [AS] Move history */\r
4927     case IDM_ShowMoveHistory:\r
4928         if( MoveHistoryIsUp() ) {\r
4929             MoveHistoryPopDown();\r
4930         }\r
4931         else {\r
4932             MoveHistoryPopUp();\r
4933         }\r
4934         break;\r
4935 \r
4936     /* [AS] Eval graph */\r
4937     case IDM_ShowEvalGraph:\r
4938         if( EvalGraphIsUp() ) {\r
4939             EvalGraphPopDown();\r
4940         }\r
4941         else {\r
4942             EvalGraphPopUp();\r
4943             SetFocus(hwndMain);\r
4944         }\r
4945         break;\r
4946 \r
4947     /* [AS] Engine output */\r
4948     case IDM_ShowEngineOutput:\r
4949         if( EngineOutputIsUp() ) {\r
4950             EngineOutputPopDown();\r
4951         }\r
4952         else {\r
4953             EngineOutputPopUp();\r
4954         }\r
4955         break;\r
4956 \r
4957     /* [AS] User adjudication */\r
4958     case IDM_UserAdjudication_White:\r
4959         UserAdjudicationEvent( +1 );\r
4960         break;\r
4961 \r
4962     case IDM_UserAdjudication_Black:\r
4963         UserAdjudicationEvent( -1 );\r
4964         break;\r
4965 \r
4966     case IDM_UserAdjudication_Draw:\r
4967         UserAdjudicationEvent( 0 );\r
4968         break;\r
4969 \r
4970     /* [AS] Game list options dialog */\r
4971     case IDM_GameListOptions:\r
4972       GameListOptions();\r
4973       break;\r
4974 \r
4975     case IDM_NewChat:\r
4976       ChatPopUp(NULL);\r
4977       break;\r
4978 \r
4979     case IDM_CopyPosition:\r
4980       CopyFENToClipboard();\r
4981       break;\r
4982 \r
4983     case IDM_PastePosition:\r
4984       PasteFENFromClipboard();\r
4985       break;\r
4986 \r
4987     case IDM_MailMove:\r
4988       MailMoveEvent();\r
4989       break;\r
4990 \r
4991     case IDM_ReloadCMailMsg:\r
4992       Reset(TRUE, TRUE);\r
4993       ReloadCmailMsgEvent(FALSE);\r
4994       break;\r
4995 \r
4996     case IDM_Minimize:\r
4997       ShowWindow(hwnd, SW_MINIMIZE);\r
4998       break;\r
4999 \r
5000     case IDM_Exit:\r
5001       ExitEvent(0);\r
5002       break;\r
5003 \r
5004     case IDM_MachineWhite:\r
5005       MachineWhiteEvent();\r
5006       /*\r
5007        * refresh the tags dialog only if it's visible\r
5008        */\r
5009       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5010           char *tags;\r
5011           tags = PGNTags(&gameInfo);\r
5012           TagsPopUp(tags, CmailMsg());\r
5013           free(tags);\r
5014       }\r
5015       SAY("computer starts playing white");\r
5016       break;\r
5017 \r
5018     case IDM_MachineBlack:\r
5019       MachineBlackEvent();\r
5020       /*\r
5021        * refresh the tags dialog only if it's visible\r
5022        */\r
5023       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5024           char *tags;\r
5025           tags = PGNTags(&gameInfo);\r
5026           TagsPopUp(tags, CmailMsg());\r
5027           free(tags);\r
5028       }\r
5029       SAY("computer starts playing black");\r
5030       break;\r
5031 \r
5032     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5033       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5034       break;\r
5035 \r
5036     case IDM_TwoMachines:\r
5037       TwoMachinesEvent();\r
5038       /*\r
5039        * refresh the tags dialog only if it's visible\r
5040        */\r
5041       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5042           char *tags;\r
5043           tags = PGNTags(&gameInfo);\r
5044           TagsPopUp(tags, CmailMsg());\r
5045           free(tags);\r
5046       }\r
5047       SAY("computer starts playing both sides");\r
5048       break;\r
5049 \r
5050     case IDM_AnalysisMode:\r
5051       if(AnalyzeModeEvent()) {\r
5052         SAY("analyzing current position");\r
5053       }\r
5054       break;\r
5055 \r
5056     case IDM_AnalyzeFile:\r
5057       AnalyzeFileEvent();\r
5058       break;\r
5059 \r
5060     case IDM_IcsClient:\r
5061       IcsClientEvent();\r
5062       break;\r
5063 \r
5064     case IDM_EditGame:\r
5065     case IDM_EditGame2:\r
5066       EditGameEvent();\r
5067       SAY("edit game");\r
5068       break;\r
5069 \r
5070     case IDM_EditPosition:\r
5071     case IDM_EditPosition2:\r
5072       EditPositionEvent();\r
5073       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5074       break;\r
5075 \r
5076     case IDM_Training:\r
5077       TrainingEvent();\r
5078       break;\r
5079 \r
5080     case IDM_ShowGameList:\r
5081       ShowGameListProc();\r
5082       break;\r
5083 \r
5084     case IDM_EditProgs1:\r
5085       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5086       break;\r
5087 \r
5088     case IDM_LoadProg1:\r
5089      LoadEnginePopUp(hwndMain, 0);\r
5090       break;\r
5091 \r
5092     case IDM_LoadProg2:\r
5093      LoadEnginePopUp(hwndMain, 1);\r
5094       break;\r
5095 \r
5096     case IDM_EditServers:\r
5097       EditTagsPopUp(icsNames, &icsNames);\r
5098       break;\r
5099 \r
5100     case IDM_EditTags:\r
5101     case IDM_Tags:\r
5102       EditTagsProc();\r
5103       break;\r
5104 \r
5105     case IDM_EditBook:\r
5106       EditBookEvent();\r
5107       break;\r
5108 \r
5109     case IDM_EditComment:\r
5110     case IDM_Comment:\r
5111       if (commentUp && editComment) {\r
5112         CommentPopDown();\r
5113       } else {\r
5114         EditCommentEvent();\r
5115       }\r
5116       break;\r
5117 \r
5118     case IDM_Pause:\r
5119       PauseEvent();\r
5120       break;\r
5121 \r
5122     case IDM_Accept:\r
5123       AcceptEvent();\r
5124       break;\r
5125 \r
5126     case IDM_Decline:\r
5127       DeclineEvent();\r
5128       break;\r
5129 \r
5130     case IDM_Rematch:\r
5131       RematchEvent();\r
5132       break;\r
5133 \r
5134     case IDM_CallFlag:\r
5135       CallFlagEvent();\r
5136       break;\r
5137 \r
5138     case IDM_Draw:\r
5139       DrawEvent();\r
5140       break;\r
5141 \r
5142     case IDM_Adjourn:\r
5143       AdjournEvent();\r
5144       break;\r
5145 \r
5146     case IDM_Abort:\r
5147       AbortEvent();\r
5148       break;\r
5149 \r
5150     case IDM_Resign:\r
5151       ResignEvent();\r
5152       break;\r
5153 \r
5154     case IDM_StopObserving:\r
5155       StopObservingEvent();\r
5156       break;\r
5157 \r
5158     case IDM_StopExamining:\r
5159       StopExaminingEvent();\r
5160       break;\r
5161 \r
5162     case IDM_Upload:\r
5163       UploadGameEvent();\r
5164       break;\r
5165 \r
5166     case IDM_TypeInMove:\r
5167       TypeInEvent('\000');\r
5168       break;\r
5169 \r
5170     case IDM_TypeInName:\r
5171       PopUpNameDialog('\000');\r
5172       break;\r
5173 \r
5174     case IDM_Backward:\r
5175       BackwardEvent();\r
5176       SetFocus(hwndMain);\r
5177       break;\r
5178 \r
5179     JAWS_MENU_ITEMS\r
5180 \r
5181     case IDM_Forward:\r
5182       ForwardEvent();\r
5183       SetFocus(hwndMain);\r
5184       break;\r
5185 \r
5186     case IDM_ToStart:\r
5187       ToStartEvent();\r
5188       SetFocus(hwndMain);\r
5189       break;\r
5190 \r
5191     case IDM_ToEnd:\r
5192       ToEndEvent();\r
5193       SetFocus(hwndMain);\r
5194       break;\r
5195 \r
5196     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5197     case OPT_GameListPrev:\r
5198       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5199       break;\r
5200 \r
5201     case IDM_Revert:\r
5202       RevertEvent(FALSE);\r
5203       break;\r
5204 \r
5205     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5206       RevertEvent(TRUE);\r
5207       break;\r
5208 \r
5209     case IDM_TruncateGame:\r
5210       TruncateGameEvent();\r
5211       break;\r
5212 \r
5213     case IDM_MoveNow:\r
5214       MoveNowEvent();\r
5215       break;\r
5216 \r
5217     case IDM_RetractMove:\r
5218       RetractMoveEvent();\r
5219       break;\r
5220 \r
5221     case IDM_FlipView:\r
5222       flipView = !flipView;\r
5223       DrawPosition(FALSE, NULL);\r
5224       break;\r
5225 \r
5226     case IDM_FlipClock:\r
5227       flipClock = !flipClock;\r
5228       DisplayBothClocks();\r
5229       DisplayLogos();\r
5230       break;\r
5231 \r
5232     case IDM_MuteSounds:\r
5233       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5234       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5235                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5236       break;\r
5237 \r
5238     case IDM_GeneralOptions:\r
5239       GeneralOptionsPopup(hwnd);\r
5240       DrawPosition(TRUE, NULL);\r
5241       break;\r
5242 \r
5243     case IDM_BoardOptions:\r
5244       BoardOptionsPopup(hwnd);\r
5245       break;\r
5246 \r
5247     case IDM_ThemeOptions:\r
5248       ThemeOptionsPopup(hwnd);\r
5249       break;\r
5250 \r
5251     case IDM_EnginePlayOptions:\r
5252       EnginePlayOptionsPopup(hwnd);\r
5253       break;\r
5254 \r
5255     case IDM_Engine1Options:\r
5256       EngineOptionsPopup(hwnd, &first);\r
5257       break;\r
5258 \r
5259     case IDM_Engine2Options:\r
5260       savedHwnd = hwnd;\r
5261       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5262       EngineOptionsPopup(hwnd, &second);\r
5263       break;\r
5264 \r
5265     case IDM_OptionsUCI:\r
5266       UciOptionsPopup(hwnd);\r
5267       break;\r
5268 \r
5269     case IDM_Tourney:\r
5270       TourneyPopup(hwnd);\r
5271       break;\r
5272 \r
5273     case IDM_IcsOptions:\r
5274       IcsOptionsPopup(hwnd);\r
5275       break;\r
5276 \r
5277     case IDM_Fonts:\r
5278       FontsOptionsPopup(hwnd);\r
5279       break;\r
5280 \r
5281     case IDM_Sounds:\r
5282       SoundOptionsPopup(hwnd);\r
5283       break;\r
5284 \r
5285     case IDM_CommPort:\r
5286       CommPortOptionsPopup(hwnd);\r
5287       break;\r
5288 \r
5289     case IDM_LoadOptions:\r
5290       LoadOptionsPopup(hwnd);\r
5291       break;\r
5292 \r
5293     case IDM_SaveOptions:\r
5294       SaveOptionsPopup(hwnd);\r
5295       break;\r
5296 \r
5297     case IDM_TimeControl:\r
5298       TimeControlOptionsPopup(hwnd);\r
5299       break;\r
5300 \r
5301     case IDM_SaveSettings:\r
5302       SaveSettings(settingsFileName);\r
5303       break;\r
5304 \r
5305     case IDM_SaveSettingsOnExit:\r
5306       saveSettingsOnExit = !saveSettingsOnExit;\r
5307       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5308                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5309                                          MF_CHECKED : MF_UNCHECKED));\r
5310       break;\r
5311 \r
5312     case IDM_Hint:\r
5313       HintEvent();\r
5314       break;\r
5315 \r
5316     case IDM_Book:\r
5317       BookEvent();\r
5318       break;\r
5319 \r
5320     case IDM_AboutGame:\r
5321       AboutGameEvent();\r
5322       break;\r
5323 \r
5324     case IDM_Debug:\r
5325       appData.debugMode = !appData.debugMode;\r
5326       if (appData.debugMode) {\r
5327         char dir[MSG_SIZ];\r
5328         GetCurrentDirectory(MSG_SIZ, dir);\r
5329         SetCurrentDirectory(installDir);\r
5330         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5331         SetCurrentDirectory(dir);\r
5332         setbuf(debugFP, NULL);\r
5333       } else {\r
5334         fclose(debugFP);\r
5335         debugFP = NULL;\r
5336       }\r
5337       break;\r
5338 \r
5339     case IDM_HELPCONTENTS:\r
5340       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5341           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5342           MessageBox (GetFocus(),\r
5343                     _("Unable to activate help"),\r
5344                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5345       }\r
5346       break;\r
5347 \r
5348     case IDM_HELPSEARCH:\r
5349         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5350             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5351         MessageBox (GetFocus(),\r
5352                     _("Unable to activate help"),\r
5353                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5354       }\r
5355       break;\r
5356 \r
5357     case IDM_HELPHELP:\r
5358       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5359         MessageBox (GetFocus(),\r
5360                     _("Unable to activate help"),\r
5361                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5362       }\r
5363       break;\r
5364 \r
5365     case IDM_ABOUT:\r
5366       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5367       DialogBox(hInst, \r
5368         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5369         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5370       FreeProcInstance(lpProc);\r
5371       break;\r
5372 \r
5373     case IDM_DirectCommand1:\r
5374       AskQuestionEvent(_("Direct Command"),\r
5375                        _("Send to chess program:"), "", "1");\r
5376       break;\r
5377     case IDM_DirectCommand2:\r
5378       AskQuestionEvent(_("Direct Command"),\r
5379                        _("Send to second chess program:"), "", "2");\r
5380       break;\r
5381 \r
5382     case EP_WhitePawn:\r
5383       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5384       fromX = fromY = -1;\r
5385       break;\r
5386 \r
5387     case EP_WhiteKnight:\r
5388       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5389       fromX = fromY = -1;\r
5390       break;\r
5391 \r
5392     case EP_WhiteBishop:\r
5393       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5394       fromX = fromY = -1;\r
5395       break;\r
5396 \r
5397     case EP_WhiteRook:\r
5398       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5399       fromX = fromY = -1;\r
5400       break;\r
5401 \r
5402     case EP_WhiteQueen:\r
5403       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5404       fromX = fromY = -1;\r
5405       break;\r
5406 \r
5407     case EP_WhiteFerz:\r
5408       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5409       fromX = fromY = -1;\r
5410       break;\r
5411 \r
5412     case EP_WhiteWazir:\r
5413       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5414       fromX = fromY = -1;\r
5415       break;\r
5416 \r
5417     case EP_WhiteAlfil:\r
5418       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5419       fromX = fromY = -1;\r
5420       break;\r
5421 \r
5422     case EP_WhiteCannon:\r
5423       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5424       fromX = fromY = -1;\r
5425       break;\r
5426 \r
5427     case EP_WhiteCardinal:\r
5428       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5429       fromX = fromY = -1;\r
5430       break;\r
5431 \r
5432     case EP_WhiteMarshall:\r
5433       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5434       fromX = fromY = -1;\r
5435       break;\r
5436 \r
5437     case EP_WhiteKing:\r
5438       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5439       fromX = fromY = -1;\r
5440       break;\r
5441 \r
5442     case EP_BlackPawn:\r
5443       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5444       fromX = fromY = -1;\r
5445       break;\r
5446 \r
5447     case EP_BlackKnight:\r
5448       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5449       fromX = fromY = -1;\r
5450       break;\r
5451 \r
5452     case EP_BlackBishop:\r
5453       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5454       fromX = fromY = -1;\r
5455       break;\r
5456 \r
5457     case EP_BlackRook:\r
5458       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5459       fromX = fromY = -1;\r
5460       break;\r
5461 \r
5462     case EP_BlackQueen:\r
5463       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5464       fromX = fromY = -1;\r
5465       break;\r
5466 \r
5467     case EP_BlackFerz:\r
5468       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5469       fromX = fromY = -1;\r
5470       break;\r
5471 \r
5472     case EP_BlackWazir:\r
5473       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5474       fromX = fromY = -1;\r
5475       break;\r
5476 \r
5477     case EP_BlackAlfil:\r
5478       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5479       fromX = fromY = -1;\r
5480       break;\r
5481 \r
5482     case EP_BlackCannon:\r
5483       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5484       fromX = fromY = -1;\r
5485       break;\r
5486 \r
5487     case EP_BlackCardinal:\r
5488       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5489       fromX = fromY = -1;\r
5490       break;\r
5491 \r
5492     case EP_BlackMarshall:\r
5493       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5494       fromX = fromY = -1;\r
5495       break;\r
5496 \r
5497     case EP_BlackKing:\r
5498       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5499       fromX = fromY = -1;\r
5500       break;\r
5501 \r
5502     case EP_EmptySquare:\r
5503       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5504       fromX = fromY = -1;\r
5505       break;\r
5506 \r
5507     case EP_ClearBoard:\r
5508       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5509       fromX = fromY = -1;\r
5510       break;\r
5511 \r
5512     case EP_White:\r
5513       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5514       fromX = fromY = -1;\r
5515       break;\r
5516 \r
5517     case EP_Black:\r
5518       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5519       fromX = fromY = -1;\r
5520       break;\r
5521 \r
5522     case EP_Promote:\r
5523       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5524       fromX = fromY = -1;\r
5525       break;\r
5526 \r
5527     case EP_Demote:\r
5528       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5529       fromX = fromY = -1;\r
5530       break;\r
5531 \r
5532     case DP_Pawn:\r
5533       DropMenuEvent(WhitePawn, fromX, fromY);\r
5534       fromX = fromY = -1;\r
5535       break;\r
5536 \r
5537     case DP_Knight:\r
5538       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5539       fromX = fromY = -1;\r
5540       break;\r
5541 \r
5542     case DP_Bishop:\r
5543       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5544       fromX = fromY = -1;\r
5545       break;\r
5546 \r
5547     case DP_Rook:\r
5548       DropMenuEvent(WhiteRook, fromX, fromY);\r
5549       fromX = fromY = -1;\r
5550       break;\r
5551 \r
5552     case DP_Queen:\r
5553       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5554       fromX = fromY = -1;\r
5555       break;\r
5556 \r
5557     case IDM_English:\r
5558       barbaric = 0; appData.language = "";\r
5559       TranslateMenus(0);\r
5560       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5561       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5562       lastChecked = wmId;\r
5563       break;\r
5564 \r
5565     default:\r
5566       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5567           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5568       else\r
5569       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5570           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5571           TranslateMenus(0);\r
5572           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5573           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5574           lastChecked = wmId;\r
5575           break;\r
5576       }\r
5577       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5578     }\r
5579     break;\r
5580 \r
5581   case WM_TIMER:\r
5582     switch (wParam) {\r
5583     case CLOCK_TIMER_ID:\r
5584       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5585       clockTimerEvent = 0;\r
5586       DecrementClocks(); /* call into back end */\r
5587       break;\r
5588     case LOAD_GAME_TIMER_ID:\r
5589       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5590       loadGameTimerEvent = 0;\r
5591       AutoPlayGameLoop(); /* call into back end */\r
5592       break;\r
5593     case ANALYSIS_TIMER_ID:\r
5594       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5595                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5596         AnalysisPeriodicEvent(0);\r
5597       } else {\r
5598         KillTimer(hwnd, analysisTimerEvent);\r
5599         analysisTimerEvent = 0;\r
5600       }\r
5601       break;\r
5602     case DELAYED_TIMER_ID:\r
5603       KillTimer(hwnd, delayedTimerEvent);\r
5604       delayedTimerEvent = 0;\r
5605       delayedTimerCallback();\r
5606       break;\r
5607     }\r
5608     break;\r
5609 \r
5610   case WM_USER_Input:\r
5611     InputEvent(hwnd, message, wParam, lParam);\r
5612     break;\r
5613 \r
5614   /* [AS] Also move "attached" child windows */\r
5615   case WM_WINDOWPOSCHANGING:\r
5616 \r
5617     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5618         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5619 \r
5620         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5621             /* Window is moving */\r
5622             RECT rcMain;\r
5623 \r
5624 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5625             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5626             rcMain.right  = wpMain.x + wpMain.width;\r
5627             rcMain.top    = wpMain.y;\r
5628             rcMain.bottom = wpMain.y + wpMain.height;\r
5629             \r
5630             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5631             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5632             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5633             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5634             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5635             wpMain.x = lpwp->x;\r
5636             wpMain.y = lpwp->y;\r
5637         }\r
5638     }\r
5639     break;\r
5640 \r
5641   /* [AS] Snapping */\r
5642   case WM_ENTERSIZEMOVE:\r
5643     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5644     if (hwnd == hwndMain) {\r
5645       doingSizing = TRUE;\r
5646       lastSizing = 0;\r
5647     }\r
5648     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5649     break;\r
5650 \r
5651   case WM_SIZING:\r
5652     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5653     if (hwnd == hwndMain) {\r
5654       lastSizing = wParam;\r
5655     }\r
5656     break;\r
5657 \r
5658   case WM_MOVING:\r
5659     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5660       return OnMoving( &sd, hwnd, wParam, lParam );\r
5661 \r
5662   case WM_EXITSIZEMOVE:\r
5663     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5664     if (hwnd == hwndMain) {\r
5665       RECT client;\r
5666       doingSizing = FALSE;\r
5667       InvalidateRect(hwnd, &boardRect, FALSE);\r
5668       GetClientRect(hwnd, &client);\r
5669       ResizeBoard(client.right, client.bottom, lastSizing);\r
5670       lastSizing = 0;\r
5671       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5672     }\r
5673     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5674     break;\r
5675 \r
5676   case WM_DESTROY: /* message: window being destroyed */\r
5677     PostQuitMessage(0);\r
5678     break;\r
5679 \r
5680   case WM_CLOSE:\r
5681     if (hwnd == hwndMain) {\r
5682       ExitEvent(0);\r
5683     }\r
5684     break;\r
5685 \r
5686   default:      /* Passes it on if unprocessed */\r
5687     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5688   }\r
5689   return 0;\r
5690 }\r
5691 \r
5692 /*---------------------------------------------------------------------------*\\r
5693  *\r
5694  * Misc utility routines\r
5695  *\r
5696 \*---------------------------------------------------------------------------*/\r
5697 \r
5698 /*\r
5699  * Decent random number generator, at least not as bad as Windows\r
5700  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5701  */\r
5702 unsigned int randstate;\r
5703 \r
5704 int\r
5705 myrandom(void)\r
5706 {\r
5707   randstate = randstate * 1664525 + 1013904223;\r
5708   return (int) randstate & 0x7fffffff;\r
5709 }\r
5710 \r
5711 void\r
5712 mysrandom(unsigned int seed)\r
5713 {\r
5714   randstate = seed;\r
5715 }\r
5716 \r
5717 \r
5718 /* \r
5719  * returns TRUE if user selects a different color, FALSE otherwise \r
5720  */\r
5721 \r
5722 BOOL\r
5723 ChangeColor(HWND hwnd, COLORREF *which)\r
5724 {\r
5725   static BOOL firstTime = TRUE;\r
5726   static DWORD customColors[16];\r
5727   CHOOSECOLOR cc;\r
5728   COLORREF newcolor;\r
5729   int i;\r
5730   ColorClass ccl;\r
5731 \r
5732   if (firstTime) {\r
5733     /* Make initial colors in use available as custom colors */\r
5734     /* Should we put the compiled-in defaults here instead? */\r
5735     i = 0;\r
5736     customColors[i++] = lightSquareColor & 0xffffff;\r
5737     customColors[i++] = darkSquareColor & 0xffffff;\r
5738     customColors[i++] = whitePieceColor & 0xffffff;\r
5739     customColors[i++] = blackPieceColor & 0xffffff;\r
5740     customColors[i++] = highlightSquareColor & 0xffffff;\r
5741     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5742 \r
5743     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5744       customColors[i++] = textAttribs[ccl].color;\r
5745     }\r
5746     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5747     firstTime = FALSE;\r
5748   }\r
5749 \r
5750   cc.lStructSize = sizeof(cc);\r
5751   cc.hwndOwner = hwnd;\r
5752   cc.hInstance = NULL;\r
5753   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5754   cc.lpCustColors = (LPDWORD) customColors;\r
5755   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5756 \r
5757   if (!ChooseColor(&cc)) return FALSE;\r
5758 \r
5759   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5760   if (newcolor == *which) return FALSE;\r
5761   *which = newcolor;\r
5762   return TRUE;\r
5763 \r
5764   /*\r
5765   InitDrawingColors();\r
5766   InvalidateRect(hwnd, &boardRect, FALSE);\r
5767   */\r
5768 }\r
5769 \r
5770 BOOLEAN\r
5771 MyLoadSound(MySound *ms)\r
5772 {\r
5773   BOOL ok = FALSE;\r
5774   struct stat st;\r
5775   FILE *f;\r
5776 \r
5777   if (ms->data && ms->flag) free(ms->data);\r
5778   ms->data = NULL;\r
5779 \r
5780   switch (ms->name[0]) {\r
5781   case NULLCHAR:\r
5782     /* Silence */\r
5783     ok = TRUE;\r
5784     break;\r
5785   case '$':\r
5786     /* System sound from Control Panel.  Don't preload here. */\r
5787     ok = TRUE;\r
5788     break;\r
5789   case '!':\r
5790     if (ms->name[1] == NULLCHAR) {\r
5791       /* "!" alone = silence */\r
5792       ok = TRUE;\r
5793     } else {\r
5794       /* Builtin wave resource.  Error if not found. */\r
5795       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5796       if (h == NULL) break;\r
5797       ms->data = (void *)LoadResource(hInst, h);\r
5798       ms->flag = 0; // not maloced, so cannot be freed!\r
5799       if (h == NULL) break;\r
5800       ok = TRUE;\r
5801     }\r
5802     break;\r
5803   default:\r
5804     /* .wav file.  Error if not found. */\r
5805     f = fopen(ms->name, "rb");\r
5806     if (f == NULL) break;\r
5807     if (fstat(fileno(f), &st) < 0) break;\r
5808     ms->data = malloc(st.st_size);\r
5809     ms->flag = 1;\r
5810     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5811     fclose(f);\r
5812     ok = TRUE;\r
5813     break;\r
5814   }\r
5815   if (!ok) {\r
5816     char buf[MSG_SIZ];\r
5817       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5818     DisplayError(buf, GetLastError());\r
5819   }\r
5820   return ok;\r
5821 }\r
5822 \r
5823 BOOLEAN\r
5824 MyPlaySound(MySound *ms)\r
5825 {\r
5826   BOOLEAN ok = FALSE;\r
5827 \r
5828   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5829   switch (ms->name[0]) {\r
5830   case NULLCHAR:\r
5831         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5832     /* Silence */\r
5833     ok = TRUE;\r
5834     break;\r
5835   case '$':\r
5836     /* System sound from Control Panel (deprecated feature).\r
5837        "$" alone or an unset sound name gets default beep (still in use). */\r
5838     if (ms->name[1]) {\r
5839       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5840     }\r
5841     if (!ok) ok = MessageBeep(MB_OK);\r
5842     break; \r
5843   case '!':\r
5844     /* Builtin wave resource, or "!" alone for silence */\r
5845     if (ms->name[1]) {\r
5846       if (ms->data == NULL) return FALSE;\r
5847       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5848     } else {\r
5849       ok = TRUE;\r
5850     }\r
5851     break;\r
5852   default:\r
5853     /* .wav file.  Error if not found. */\r
5854     if (ms->data == NULL) return FALSE;\r
5855     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5856     break;\r
5857   }\r
5858   /* Don't print an error: this can happen innocently if the sound driver\r
5859      is busy; for instance, if another instance of WinBoard is playing\r
5860      a sound at about the same time. */\r
5861   return ok;\r
5862 }\r
5863 \r
5864 \r
5865 LRESULT CALLBACK\r
5866 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5867 {\r
5868   BOOL ok;\r
5869   OPENFILENAME *ofn;\r
5870   static UINT *number; /* gross that this is static */\r
5871 \r
5872   switch (message) {\r
5873   case WM_INITDIALOG: /* message: initialize dialog box */\r
5874     /* Center the dialog over the application window */\r
5875     ofn = (OPENFILENAME *) lParam;\r
5876     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5877       number = (UINT *) ofn->lCustData;\r
5878       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5879     } else {\r
5880       number = NULL;\r
5881     }\r
5882     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5883     Translate(hDlg, 1536);\r
5884     return FALSE;  /* Allow for further processing */\r
5885 \r
5886   case WM_COMMAND:\r
5887     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5888       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5889     }\r
5890     return FALSE;  /* Allow for further processing */\r
5891   }\r
5892   return FALSE;\r
5893 }\r
5894 \r
5895 UINT APIENTRY\r
5896 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5897 {\r
5898   static UINT *number;\r
5899   OPENFILENAME *ofname;\r
5900   OFNOTIFY *ofnot;\r
5901   switch (uiMsg) {\r
5902   case WM_INITDIALOG:\r
5903     Translate(hdlg, DLG_IndexNumber);\r
5904     ofname = (OPENFILENAME *)lParam;\r
5905     number = (UINT *)(ofname->lCustData);\r
5906     break;\r
5907   case WM_NOTIFY:\r
5908     ofnot = (OFNOTIFY *)lParam;\r
5909     if (ofnot->hdr.code == CDN_FILEOK) {\r
5910       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5911     }\r
5912     break;\r
5913   }\r
5914   return 0;\r
5915 }\r
5916 \r
5917 \r
5918 FILE *\r
5919 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5920                char *nameFilt, char *dlgTitle, UINT *number,\r
5921                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5922 {\r
5923   OPENFILENAME openFileName;\r
5924   char buf1[MSG_SIZ];\r
5925   FILE *f;\r
5926 \r
5927   if (fileName == NULL) fileName = buf1;\r
5928   if (defName == NULL) {\r
5929     safeStrCpy(fileName, "*.", 3 );\r
5930     strcat(fileName, defExt);\r
5931   } else {\r
5932     safeStrCpy(fileName, defName, MSG_SIZ );\r
5933   }\r
5934     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5935   if (number) *number = 0;\r
5936 \r
5937   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5938   openFileName.hwndOwner         = hwnd;\r
5939   openFileName.hInstance         = (HANDLE) hInst;\r
5940   openFileName.lpstrFilter       = nameFilt;\r
5941   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5942   openFileName.nMaxCustFilter    = 0L;\r
5943   openFileName.nFilterIndex      = 1L;\r
5944   openFileName.lpstrFile         = fileName;\r
5945   openFileName.nMaxFile          = MSG_SIZ;\r
5946   openFileName.lpstrFileTitle    = fileTitle;\r
5947   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5948   openFileName.lpstrInitialDir   = NULL;\r
5949   openFileName.lpstrTitle        = dlgTitle;\r
5950   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5951     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5952     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5953     | (oldDialog ? 0 : OFN_EXPLORER);\r
5954   openFileName.nFileOffset       = 0;\r
5955   openFileName.nFileExtension    = 0;\r
5956   openFileName.lpstrDefExt       = defExt;\r
5957   openFileName.lCustData         = (LONG) number;\r
5958   openFileName.lpfnHook          = oldDialog ?\r
5959     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5960   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5961 \r
5962   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5963                         GetOpenFileName(&openFileName)) {\r
5964     /* open the file */\r
5965     f = fopen(openFileName.lpstrFile, write);\r
5966     if (f == NULL) {\r
5967       MessageBox(hwnd, _("File open failed"), NULL,\r
5968                  MB_OK|MB_ICONEXCLAMATION);\r
5969       return NULL;\r
5970     }\r
5971   } else {\r
5972     int err = CommDlgExtendedError();\r
5973     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5974     return FALSE;\r
5975   }\r
5976   return f;\r
5977 }\r
5978 \r
5979 \r
5980 \r
5981 VOID APIENTRY\r
5982 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5983 {\r
5984   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5985 \r
5986   /*\r
5987    * Get the first pop-up menu in the menu template. This is the\r
5988    * menu that TrackPopupMenu displays.\r
5989    */\r
5990   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5991   TranslateOneMenu(10, hmenuTrackPopup);\r
5992 \r
5993   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5994 \r
5995   /*\r
5996    * TrackPopup uses screen coordinates, so convert the\r
5997    * coordinates of the mouse click to screen coordinates.\r
5998    */\r
5999   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6000 \r
6001   /* Draw and track the floating pop-up menu. */\r
6002   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6003                  pt.x, pt.y, 0, hwnd, NULL);\r
6004 \r
6005   /* Destroy the menu.*/\r
6006   DestroyMenu(hmenu);\r
6007 }\r
6008    \r
6009 typedef struct {\r
6010   HWND hDlg, hText;\r
6011   int sizeX, sizeY, newSizeX, newSizeY;\r
6012   HDWP hdwp;\r
6013 } ResizeEditPlusButtonsClosure;\r
6014 \r
6015 BOOL CALLBACK\r
6016 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6017 {\r
6018   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6019   RECT rect;\r
6020   POINT pt;\r
6021 \r
6022   if (hChild == cl->hText) return TRUE;\r
6023   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6024   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6025   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6026   ScreenToClient(cl->hDlg, &pt);\r
6027   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6028     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6029   return TRUE;\r
6030 }\r
6031 \r
6032 /* Resize a dialog that has a (rich) edit field filling most of\r
6033    the top, with a row of buttons below */\r
6034 VOID\r
6035 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6036 {\r
6037   RECT rectText;\r
6038   int newTextHeight, newTextWidth;\r
6039   ResizeEditPlusButtonsClosure cl;\r
6040   \r
6041   /*if (IsIconic(hDlg)) return;*/\r
6042   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6043   \r
6044   cl.hdwp = BeginDeferWindowPos(8);\r
6045 \r
6046   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6047   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6048   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6049   if (newTextHeight < 0) {\r
6050     newSizeY += -newTextHeight;\r
6051     newTextHeight = 0;\r
6052   }\r
6053   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6054     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6055 \r
6056   cl.hDlg = hDlg;\r
6057   cl.hText = hText;\r
6058   cl.sizeX = sizeX;\r
6059   cl.sizeY = sizeY;\r
6060   cl.newSizeX = newSizeX;\r
6061   cl.newSizeY = newSizeY;\r
6062   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6063 \r
6064   EndDeferWindowPos(cl.hdwp);\r
6065 }\r
6066 \r
6067 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6068 {\r
6069     RECT    rChild, rParent;\r
6070     int     wChild, hChild, wParent, hParent;\r
6071     int     wScreen, hScreen, xNew, yNew;\r
6072     HDC     hdc;\r
6073 \r
6074     /* Get the Height and Width of the child window */\r
6075     GetWindowRect (hwndChild, &rChild);\r
6076     wChild = rChild.right - rChild.left;\r
6077     hChild = rChild.bottom - rChild.top;\r
6078 \r
6079     /* Get the Height and Width of the parent window */\r
6080     GetWindowRect (hwndParent, &rParent);\r
6081     wParent = rParent.right - rParent.left;\r
6082     hParent = rParent.bottom - rParent.top;\r
6083 \r
6084     /* Get the display limits */\r
6085     hdc = GetDC (hwndChild);\r
6086     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6087     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6088     ReleaseDC(hwndChild, hdc);\r
6089 \r
6090     /* Calculate new X position, then adjust for screen */\r
6091     xNew = rParent.left + ((wParent - wChild) /2);\r
6092     if (xNew < 0) {\r
6093         xNew = 0;\r
6094     } else if ((xNew+wChild) > wScreen) {\r
6095         xNew = wScreen - wChild;\r
6096     }\r
6097 \r
6098     /* Calculate new Y position, then adjust for screen */\r
6099     if( mode == 0 ) {\r
6100         yNew = rParent.top  + ((hParent - hChild) /2);\r
6101     }\r
6102     else {\r
6103         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6104     }\r
6105 \r
6106     if (yNew < 0) {\r
6107         yNew = 0;\r
6108     } else if ((yNew+hChild) > hScreen) {\r
6109         yNew = hScreen - hChild;\r
6110     }\r
6111 \r
6112     /* Set it, and return */\r
6113     return SetWindowPos (hwndChild, NULL,\r
6114                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6115 }\r
6116 \r
6117 /* Center one window over another */\r
6118 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6119 {\r
6120     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6121 }\r
6122 \r
6123 /*---------------------------------------------------------------------------*\\r
6124  *\r
6125  * Startup Dialog functions\r
6126  *\r
6127 \*---------------------------------------------------------------------------*/\r
6128 void\r
6129 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6130 {\r
6131   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6132 \r
6133   while (*cd != NULL) {\r
6134     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6135     cd++;\r
6136   }\r
6137 }\r
6138 \r
6139 void\r
6140 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6141 {\r
6142   char buf1[MAX_ARG_LEN];\r
6143   int len;\r
6144 \r
6145   if (str[0] == '@') {\r
6146     FILE* f = fopen(str + 1, "r");\r
6147     if (f == NULL) {\r
6148       DisplayFatalError(str + 1, errno, 2);\r
6149       return;\r
6150     }\r
6151     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6152     fclose(f);\r
6153     buf1[len] = NULLCHAR;\r
6154     str = buf1;\r
6155   }\r
6156 \r
6157   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6158 \r
6159   for (;;) {\r
6160     char buf[MSG_SIZ];\r
6161     char *end = strchr(str, '\n');\r
6162     if (end == NULL) return;\r
6163     memcpy(buf, str, end - str);\r
6164     buf[end - str] = NULLCHAR;\r
6165     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6166     str = end + 1;\r
6167   }\r
6168 }\r
6169 \r
6170 void\r
6171 SetStartupDialogEnables(HWND hDlg)\r
6172 {\r
6173   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6174     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6175     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6176   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6177     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6178   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6179     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6180   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6181     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6182   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6183     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6184     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6185     IsDlgButtonChecked(hDlg, OPT_View));\r
6186 }\r
6187 \r
6188 char *\r
6189 QuoteForFilename(char *filename)\r
6190 {\r
6191   int dquote, space;\r
6192   dquote = strchr(filename, '"') != NULL;\r
6193   space = strchr(filename, ' ') != NULL;\r
6194   if (dquote || space) {\r
6195     if (dquote) {\r
6196       return "'";\r
6197     } else {\r
6198       return "\"";\r
6199     }\r
6200   } else {\r
6201     return "";\r
6202   }\r
6203 }\r
6204 \r
6205 VOID\r
6206 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6207 {\r
6208   char buf[MSG_SIZ];\r
6209   char *q;\r
6210 \r
6211   InitComboStringsFromOption(hwndCombo, nthnames);\r
6212   q = QuoteForFilename(nthcp);\r
6213     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6214   if (*nthdir != NULLCHAR) {\r
6215     q = QuoteForFilename(nthdir);\r
6216       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6217   }\r
6218   if (*nthcp == NULLCHAR) {\r
6219     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6220   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6221     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6222     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6223   }\r
6224 }\r
6225 \r
6226 LRESULT CALLBACK\r
6227 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6228 {\r
6229   char buf[MSG_SIZ];\r
6230   HANDLE hwndCombo;\r
6231   char *p;\r
6232 \r
6233   switch (message) {\r
6234   case WM_INITDIALOG:\r
6235     /* Center the dialog */\r
6236     CenterWindow (hDlg, GetDesktopWindow());\r
6237     Translate(hDlg, DLG_Startup);\r
6238     /* Initialize the dialog items */\r
6239     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6240                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6241                   firstChessProgramNames);\r
6242     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6243                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6244                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6245     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6246     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6247       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6248     if (*appData.icsHelper != NULLCHAR) {\r
6249       char *q = QuoteForFilename(appData.icsHelper);\r
6250       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6251     }\r
6252     if (*appData.icsHost == NULLCHAR) {\r
6253       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6254       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6255     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6256       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6257       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6258     }\r
6259 \r
6260     if (appData.icsActive) {\r
6261       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6262     }\r
6263     else if (appData.noChessProgram) {\r
6264       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6265     }\r
6266     else {\r
6267       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6268     }\r
6269 \r
6270     SetStartupDialogEnables(hDlg);\r
6271     return TRUE;\r
6272 \r
6273   case WM_COMMAND:\r
6274     switch (LOWORD(wParam)) {\r
6275     case IDOK:\r
6276       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6277         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6278         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6279         p = buf;\r
6280         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6281         ParseArgs(StringGet, &p);\r
6282         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6283         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6284         p = buf;\r
6285         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6286         ParseArgs(StringGet, &p);\r
6287         SwapEngines(singleList); // ... and then make it 'second'\r
6288         appData.noChessProgram = FALSE;\r
6289         appData.icsActive = FALSE;\r
6290       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6291         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6292         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6293         p = buf;\r
6294         ParseArgs(StringGet, &p);\r
6295         if (appData.zippyPlay) {\r
6296           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6297           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6298           p = buf;\r
6299           ParseArgs(StringGet, &p);\r
6300         }\r
6301       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6302         appData.noChessProgram = TRUE;\r
6303         appData.icsActive = FALSE;\r
6304       } else {\r
6305         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6306                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6307         return TRUE;\r
6308       }\r
6309       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6310         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6311         p = buf;\r
6312         ParseArgs(StringGet, &p);\r
6313       }\r
6314       EndDialog(hDlg, TRUE);\r
6315       return TRUE;\r
6316 \r
6317     case IDCANCEL:\r
6318       ExitEvent(0);\r
6319       return TRUE;\r
6320 \r
6321     case IDM_HELPCONTENTS:\r
6322       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6323         MessageBox (GetFocus(),\r
6324                     _("Unable to activate help"),\r
6325                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6326       }\r
6327       break;\r
6328 \r
6329     default:\r
6330       SetStartupDialogEnables(hDlg);\r
6331       break;\r
6332     }\r
6333     break;\r
6334   }\r
6335   return FALSE;\r
6336 }\r
6337 \r
6338 /*---------------------------------------------------------------------------*\\r
6339  *\r
6340  * About box dialog functions\r
6341  *\r
6342 \*---------------------------------------------------------------------------*/\r
6343 \r
6344 /* Process messages for "About" dialog box */\r
6345 LRESULT CALLBACK\r
6346 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6347 {\r
6348   switch (message) {\r
6349   case WM_INITDIALOG: /* message: initialize dialog box */\r
6350     /* Center the dialog over the application window */\r
6351     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6352     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6353     Translate(hDlg, ABOUTBOX);\r
6354     JAWS_COPYRIGHT\r
6355     return (TRUE);\r
6356 \r
6357   case WM_COMMAND: /* message: received a command */\r
6358     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6359         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6360       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6361       return (TRUE);\r
6362     }\r
6363     break;\r
6364   }\r
6365   return (FALSE);\r
6366 }\r
6367 \r
6368 /*---------------------------------------------------------------------------*\\r
6369  *\r
6370  * Comment Dialog functions\r
6371  *\r
6372 \*---------------------------------------------------------------------------*/\r
6373 \r
6374 LRESULT CALLBACK\r
6375 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6376 {\r
6377   static HANDLE hwndText = NULL;\r
6378   int len, newSizeX, newSizeY, flags;\r
6379   static int sizeX, sizeY;\r
6380   char *str;\r
6381   RECT rect;\r
6382   MINMAXINFO *mmi;\r
6383 \r
6384   switch (message) {\r
6385   case WM_INITDIALOG: /* message: initialize dialog box */\r
6386     /* Initialize the dialog items */\r
6387     Translate(hDlg, DLG_EditComment);\r
6388     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6389     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6390     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6391     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6392     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6393     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6394     SetWindowText(hDlg, commentTitle);\r
6395     if (editComment) {\r
6396       SetFocus(hwndText);\r
6397     } else {\r
6398       SetFocus(GetDlgItem(hDlg, IDOK));\r
6399     }\r
6400     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6401                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6402                 MAKELPARAM(FALSE, 0));\r
6403     /* Size and position the dialog */\r
6404     if (!commentDialog) {\r
6405       commentDialog = hDlg;\r
6406       flags = SWP_NOZORDER;\r
6407       GetClientRect(hDlg, &rect);\r
6408       sizeX = rect.right;\r
6409       sizeY = rect.bottom;\r
6410       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6411           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6412         WINDOWPLACEMENT wp;\r
6413         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6414         wp.length = sizeof(WINDOWPLACEMENT);\r
6415         wp.flags = 0;\r
6416         wp.showCmd = SW_SHOW;\r
6417         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6418         wp.rcNormalPosition.left = wpComment.x;\r
6419         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6420         wp.rcNormalPosition.top = wpComment.y;\r
6421         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6422         SetWindowPlacement(hDlg, &wp);\r
6423 \r
6424         GetClientRect(hDlg, &rect);\r
6425         newSizeX = rect.right;\r
6426         newSizeY = rect.bottom;\r
6427         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6428                               newSizeX, newSizeY);\r
6429         sizeX = newSizeX;\r
6430         sizeY = newSizeY;\r
6431       }\r
6432     }\r
6433     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6434     return FALSE;\r
6435 \r
6436   case WM_COMMAND: /* message: received a command */\r
6437     switch (LOWORD(wParam)) {\r
6438     case IDOK:\r
6439       if (editComment) {\r
6440         char *p, *q;\r
6441         /* Read changed options from the dialog box */\r
6442         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6443         len = GetWindowTextLength(hwndText);\r
6444         str = (char *) malloc(len + 1);\r
6445         GetWindowText(hwndText, str, len + 1);\r
6446         p = q = str;\r
6447         while (*q) {\r
6448           if (*q == '\r')\r
6449             q++;\r
6450           else\r
6451             *p++ = *q++;\r
6452         }\r
6453         *p = NULLCHAR;\r
6454         ReplaceComment(commentIndex, str);\r
6455         free(str);\r
6456       }\r
6457       CommentPopDown();\r
6458       return TRUE;\r
6459 \r
6460     case IDCANCEL:\r
6461     case OPT_CancelComment:\r
6462       CommentPopDown();\r
6463       return TRUE;\r
6464 \r
6465     case OPT_ClearComment:\r
6466       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6467       break;\r
6468 \r
6469     case OPT_EditComment:\r
6470       EditCommentEvent();\r
6471       return TRUE;\r
6472 \r
6473     default:\r
6474       break;\r
6475     }\r
6476     break;\r
6477 \r
6478   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6479         if( wParam == OPT_CommentText ) {\r
6480             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6481 \r
6482             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6483                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6484                 POINTL pt;\r
6485                 LRESULT index;\r
6486 \r
6487                 pt.x = LOWORD( lpMF->lParam );\r
6488                 pt.y = HIWORD( lpMF->lParam );\r
6489 \r
6490                 if(lpMF->msg == WM_CHAR) {\r
6491                         CHARRANGE sel;\r
6492                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6493                         index = sel.cpMin;\r
6494                 } else\r
6495                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6496 \r
6497                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6498                 len = GetWindowTextLength(hwndText);\r
6499                 str = (char *) malloc(len + 1);\r
6500                 GetWindowText(hwndText, str, len + 1);\r
6501                 ReplaceComment(commentIndex, str);\r
6502                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6503                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6504                 free(str);\r
6505 \r
6506                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6507                 lpMF->msg = WM_USER;\r
6508 \r
6509                 return TRUE;\r
6510             }\r
6511         }\r
6512         break;\r
6513 \r
6514   case WM_SIZE:\r
6515     newSizeX = LOWORD(lParam);\r
6516     newSizeY = HIWORD(lParam);\r
6517     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6518     sizeX = newSizeX;\r
6519     sizeY = newSizeY;\r
6520     break;\r
6521 \r
6522   case WM_GETMINMAXINFO:\r
6523     /* Prevent resizing window too small */\r
6524     mmi = (MINMAXINFO *) lParam;\r
6525     mmi->ptMinTrackSize.x = 100;\r
6526     mmi->ptMinTrackSize.y = 100;\r
6527     break;\r
6528   }\r
6529   return FALSE;\r
6530 }\r
6531 \r
6532 VOID\r
6533 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6534 {\r
6535   FARPROC lpProc;\r
6536   char *p, *q;\r
6537 \r
6538   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6539 \r
6540   if (str == NULL) str = "";\r
6541   p = (char *) malloc(2 * strlen(str) + 2);\r
6542   q = p;\r
6543   while (*str) {\r
6544     if (*str == '\n') *q++ = '\r';\r
6545     *q++ = *str++;\r
6546   }\r
6547   *q = NULLCHAR;\r
6548   if (commentText != NULL) free(commentText);\r
6549 \r
6550   commentIndex = index;\r
6551   commentTitle = title;\r
6552   commentText = p;\r
6553   editComment = edit;\r
6554 \r
6555   if (commentDialog) {\r
6556     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6557     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6558   } else {\r
6559     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6560     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6561                  hwndMain, (DLGPROC)lpProc);\r
6562     FreeProcInstance(lpProc);\r
6563   }\r
6564   commentUp = TRUE;\r
6565 }\r
6566 \r
6567 \r
6568 /*---------------------------------------------------------------------------*\\r
6569  *\r
6570  * Type-in move dialog functions\r
6571  * \r
6572 \*---------------------------------------------------------------------------*/\r
6573 \r
6574 LRESULT CALLBACK\r
6575 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6576 {\r
6577   char move[MSG_SIZ];\r
6578   HWND hInput;\r
6579 \r
6580   switch (message) {\r
6581   case WM_INITDIALOG:\r
6582     move[0] = (char) lParam;\r
6583     move[1] = NULLCHAR;\r
6584     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6585     Translate(hDlg, DLG_TypeInMove);\r
6586     hInput = GetDlgItem(hDlg, OPT_Move);\r
6587     SetWindowText(hInput, move);\r
6588     SetFocus(hInput);\r
6589     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6590     return FALSE;\r
6591 \r
6592   case WM_COMMAND:\r
6593     switch (LOWORD(wParam)) {\r
6594     case IDOK:\r
6595 \r
6596       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6597       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6598       TypeInDoneEvent(move);\r
6599       EndDialog(hDlg, TRUE);\r
6600       return TRUE;\r
6601     case IDCANCEL:\r
6602       EndDialog(hDlg, FALSE);\r
6603       return TRUE;\r
6604     default:\r
6605       break;\r
6606     }\r
6607     break;\r
6608   }\r
6609   return FALSE;\r
6610 }\r
6611 \r
6612 VOID\r
6613 PopUpMoveDialog(char firstchar)\r
6614 {\r
6615     FARPROC lpProc;\r
6616 \r
6617       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6618       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6619         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6620       FreeProcInstance(lpProc);\r
6621 }\r
6622 \r
6623 /*---------------------------------------------------------------------------*\\r
6624  *\r
6625  * Type-in name dialog functions\r
6626  * \r
6627 \*---------------------------------------------------------------------------*/\r
6628 \r
6629 LRESULT CALLBACK\r
6630 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6631 {\r
6632   char move[MSG_SIZ];\r
6633   HWND hInput;\r
6634 \r
6635   switch (message) {\r
6636   case WM_INITDIALOG:\r
6637     move[0] = (char) lParam;\r
6638     move[1] = NULLCHAR;\r
6639     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6640     Translate(hDlg, DLG_TypeInName);\r
6641     hInput = GetDlgItem(hDlg, OPT_Name);\r
6642     SetWindowText(hInput, move);\r
6643     SetFocus(hInput);\r
6644     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6645     return FALSE;\r
6646 \r
6647   case WM_COMMAND:\r
6648     switch (LOWORD(wParam)) {\r
6649     case IDOK:\r
6650       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6651       appData.userName = strdup(move);\r
6652       SetUserLogo();\r
6653       SetGameInfo();\r
6654       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6655         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6656         DisplayTitle(move);\r
6657       }\r
6658 \r
6659 \r
6660       EndDialog(hDlg, TRUE);\r
6661       return TRUE;\r
6662     case IDCANCEL:\r
6663       EndDialog(hDlg, FALSE);\r
6664       return TRUE;\r
6665     default:\r
6666       break;\r
6667     }\r
6668     break;\r
6669   }\r
6670   return FALSE;\r
6671 }\r
6672 \r
6673 VOID\r
6674 PopUpNameDialog(char firstchar)\r
6675 {\r
6676     FARPROC lpProc;\r
6677     \r
6678       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6679       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6680         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6681       FreeProcInstance(lpProc);\r
6682 }\r
6683 \r
6684 /*---------------------------------------------------------------------------*\\r
6685  *\r
6686  *  Error dialogs\r
6687  * \r
6688 \*---------------------------------------------------------------------------*/\r
6689 \r
6690 /* Nonmodal error box */\r
6691 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6692                              WPARAM wParam, LPARAM lParam);\r
6693 \r
6694 VOID\r
6695 ErrorPopUp(char *title, char *content)\r
6696 {\r
6697   FARPROC lpProc;\r
6698   char *p, *q;\r
6699   BOOLEAN modal = hwndMain == NULL;\r
6700 \r
6701   p = content;\r
6702   q = errorMessage;\r
6703   while (*p) {\r
6704     if (*p == '\n') {\r
6705       if (modal) {\r
6706         *q++ = ' ';\r
6707         p++;\r
6708       } else {\r
6709         *q++ = '\r';\r
6710         *q++ = *p++;\r
6711       }\r
6712     } else {\r
6713       *q++ = *p++;\r
6714     }\r
6715   }\r
6716   *q = NULLCHAR;\r
6717   strncpy(errorTitle, title, sizeof(errorTitle));\r
6718   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6719   \r
6720   if (modal) {\r
6721     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6722   } else {\r
6723     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6724     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6725                  hwndMain, (DLGPROC)lpProc);\r
6726     FreeProcInstance(lpProc);\r
6727   }\r
6728 }\r
6729 \r
6730 VOID\r
6731 ErrorPopDown()\r
6732 {\r
6733   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6734   if (errorDialog == NULL) return;\r
6735   DestroyWindow(errorDialog);\r
6736   errorDialog = NULL;\r
6737   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6738 }\r
6739 \r
6740 LRESULT CALLBACK\r
6741 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6742 {\r
6743   HANDLE hwndText;\r
6744   RECT rChild;\r
6745 \r
6746   switch (message) {\r
6747   case WM_INITDIALOG:\r
6748     GetWindowRect(hDlg, &rChild);\r
6749 \r
6750     /*\r
6751     SetWindowPos(hDlg, NULL, rChild.left,\r
6752       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6753       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6754     */\r
6755 \r
6756     /* \r
6757         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6758         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6759         and it doesn't work when you resize the dialog.\r
6760         For now, just give it a default position.\r
6761     */\r
6762     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6763     Translate(hDlg, DLG_Error);\r
6764 \r
6765     errorDialog = hDlg;\r
6766     SetWindowText(hDlg, errorTitle);\r
6767     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6768     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6769     return FALSE;\r
6770 \r
6771   case WM_COMMAND:\r
6772     switch (LOWORD(wParam)) {\r
6773     case IDOK:\r
6774     case IDCANCEL:\r
6775       if (errorDialog == hDlg) errorDialog = NULL;\r
6776       DestroyWindow(hDlg);\r
6777       return TRUE;\r
6778 \r
6779     default:\r
6780       break;\r
6781     }\r
6782     break;\r
6783   }\r
6784   return FALSE;\r
6785 }\r
6786 \r
6787 #ifdef GOTHIC\r
6788 HWND gothicDialog = NULL;\r
6789 \r
6790 LRESULT CALLBACK\r
6791 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6792 {\r
6793   HANDLE hwndText;\r
6794   RECT rChild;\r
6795   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6796 \r
6797   switch (message) {\r
6798   case WM_INITDIALOG:\r
6799     GetWindowRect(hDlg, &rChild);\r
6800 \r
6801     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6802                                                              SWP_NOZORDER);\r
6803 \r
6804     /* \r
6805         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6806         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6807         and it doesn't work when you resize the dialog.\r
6808         For now, just give it a default position.\r
6809     */\r
6810     gothicDialog = hDlg;\r
6811     SetWindowText(hDlg, errorTitle);\r
6812     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6813     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6814     return FALSE;\r
6815 \r
6816   case WM_COMMAND:\r
6817     switch (LOWORD(wParam)) {\r
6818     case IDOK:\r
6819     case IDCANCEL:\r
6820       if (errorDialog == hDlg) errorDialog = NULL;\r
6821       DestroyWindow(hDlg);\r
6822       return TRUE;\r
6823 \r
6824     default:\r
6825       break;\r
6826     }\r
6827     break;\r
6828   }\r
6829   return FALSE;\r
6830 }\r
6831 \r
6832 VOID\r
6833 GothicPopUp(char *title, VariantClass variant)\r
6834 {\r
6835   FARPROC lpProc;\r
6836   static char *lastTitle;\r
6837 \r
6838   strncpy(errorTitle, title, sizeof(errorTitle));\r
6839   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6840 \r
6841   if(lastTitle != title && gothicDialog != NULL) {\r
6842     DestroyWindow(gothicDialog);\r
6843     gothicDialog = NULL;\r
6844   }\r
6845   if(variant != VariantNormal && gothicDialog == NULL) {\r
6846     title = lastTitle;\r
6847     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6848     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6849                  hwndMain, (DLGPROC)lpProc);\r
6850     FreeProcInstance(lpProc);\r
6851   }\r
6852 }\r
6853 #endif\r
6854 \r
6855 /*---------------------------------------------------------------------------*\\r
6856  *\r
6857  *  Ics Interaction console functions\r
6858  *\r
6859 \*---------------------------------------------------------------------------*/\r
6860 \r
6861 #define HISTORY_SIZE 64\r
6862 static char *history[HISTORY_SIZE];\r
6863 int histIn = 0, histP = 0;\r
6864 \r
6865 VOID\r
6866 SaveInHistory(char *cmd)\r
6867 {\r
6868   if (history[histIn] != NULL) {\r
6869     free(history[histIn]);\r
6870     history[histIn] = NULL;\r
6871   }\r
6872   if (*cmd == NULLCHAR) return;\r
6873   history[histIn] = StrSave(cmd);\r
6874   histIn = (histIn + 1) % HISTORY_SIZE;\r
6875   if (history[histIn] != NULL) {\r
6876     free(history[histIn]);\r
6877     history[histIn] = NULL;\r
6878   }\r
6879   histP = histIn;\r
6880 }\r
6881 \r
6882 char *\r
6883 PrevInHistory(char *cmd)\r
6884 {\r
6885   int newhp;\r
6886   if (histP == histIn) {\r
6887     if (history[histIn] != NULL) free(history[histIn]);\r
6888     history[histIn] = StrSave(cmd);\r
6889   }\r
6890   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6891   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6892   histP = newhp;\r
6893   return history[histP];\r
6894 }\r
6895 \r
6896 char *\r
6897 NextInHistory()\r
6898 {\r
6899   if (histP == histIn) return NULL;\r
6900   histP = (histP + 1) % HISTORY_SIZE;\r
6901   return history[histP];   \r
6902 }\r
6903 \r
6904 HMENU\r
6905 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6906 {\r
6907   HMENU hmenu, h;\r
6908   int i = 0;\r
6909   hmenu = LoadMenu(hInst, "TextMenu");\r
6910   h = GetSubMenu(hmenu, 0);\r
6911   while (e->item) {\r
6912     if (strcmp(e->item, "-") == 0) {\r
6913       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6914     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6915       int flags = MF_STRING, j = 0;\r
6916       if (e->item[0] == '|') {\r
6917         flags |= MF_MENUBARBREAK;\r
6918         j++;\r
6919       }\r
6920       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6921       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6922     }\r
6923     e++;\r
6924     i++;\r
6925   } \r
6926   return hmenu;\r
6927 }\r
6928 \r
6929 WNDPROC consoleTextWindowProc;\r
6930 \r
6931 void\r
6932 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6933 {\r
6934   char buf[MSG_SIZ], name[MSG_SIZ];\r
6935   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6936   CHARRANGE sel;\r
6937 \r
6938   if (!getname) {\r
6939     SetWindowText(hInput, command);\r
6940     if (immediate) {\r
6941       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6942     } else {\r
6943       sel.cpMin = 999999;\r
6944       sel.cpMax = 999999;\r
6945       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6946       SetFocus(hInput);\r
6947     }\r
6948     return;\r
6949   }    \r
6950   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6951   if (sel.cpMin == sel.cpMax) {\r
6952     /* Expand to surrounding word */\r
6953     TEXTRANGE tr;\r
6954     do {\r
6955       tr.chrg.cpMax = sel.cpMin;\r
6956       tr.chrg.cpMin = --sel.cpMin;\r
6957       if (sel.cpMin < 0) break;\r
6958       tr.lpstrText = name;\r
6959       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6960     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6961     sel.cpMin++;\r
6962 \r
6963     do {\r
6964       tr.chrg.cpMin = sel.cpMax;\r
6965       tr.chrg.cpMax = ++sel.cpMax;\r
6966       tr.lpstrText = name;\r
6967       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6968     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6969     sel.cpMax--;\r
6970 \r
6971     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6972       MessageBeep(MB_ICONEXCLAMATION);\r
6973       return;\r
6974     }\r
6975     tr.chrg = sel;\r
6976     tr.lpstrText = name;\r
6977     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6978   } else {\r
6979     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6980       MessageBeep(MB_ICONEXCLAMATION);\r
6981       return;\r
6982     }\r
6983     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6984   }\r
6985   if (immediate) {\r
6986     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
6987     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
6988     SetWindowText(hInput, buf);\r
6989     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6990   } else {\r
6991     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
6992       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
6993     SetWindowText(hInput, buf);\r
6994     sel.cpMin = 999999;\r
6995     sel.cpMax = 999999;\r
6996     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6997     SetFocus(hInput);\r
6998   }\r
6999 }\r
7000 \r
7001 LRESULT CALLBACK \r
7002 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7003 {\r
7004   HWND hInput;\r
7005   CHARRANGE sel;\r
7006 \r
7007   switch (message) {\r
7008   case WM_KEYDOWN:\r
7009     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7010     if(wParam=='R') return 0;\r
7011     switch (wParam) {\r
7012     case VK_PRIOR:\r
7013       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7014       return 0;\r
7015     case VK_NEXT:\r
7016       sel.cpMin = 999999;\r
7017       sel.cpMax = 999999;\r
7018       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7019       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7020       return 0;\r
7021     }\r
7022     break;\r
7023   case WM_CHAR:\r
7024    if(wParam != '\022') {\r
7025     if (wParam == '\t') {\r
7026       if (GetKeyState(VK_SHIFT) < 0) {\r
7027         /* shifted */\r
7028         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7029         if (buttonDesc[0].hwnd) {\r
7030           SetFocus(buttonDesc[0].hwnd);\r
7031         } else {\r
7032           SetFocus(hwndMain);\r
7033         }\r
7034       } else {\r
7035         /* unshifted */\r
7036         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7037       }\r
7038     } else {\r
7039       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7040       JAWS_DELETE( SetFocus(hInput); )\r
7041       SendMessage(hInput, message, wParam, lParam);\r
7042     }\r
7043     return 0;\r
7044    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7045    lParam = -1;\r
7046   case WM_RBUTTONDOWN:\r
7047     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7048       /* Move selection here if it was empty */\r
7049       POINT pt;\r
7050       pt.x = LOWORD(lParam);\r
7051       pt.y = HIWORD(lParam);\r
7052       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7053       if (sel.cpMin == sel.cpMax) {\r
7054         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7055         sel.cpMax = sel.cpMin;\r
7056         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7057       }\r
7058       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7059 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7060       POINT pt;\r
7061       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7062       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7063       if (sel.cpMin == sel.cpMax) {\r
7064         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7065         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7066       }\r
7067       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7068         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7069       }\r
7070       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7071       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7072       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7073       MenuPopup(hwnd, pt, hmenu, -1);\r
7074 }\r
7075     }\r
7076     return 0;\r
7077   case WM_RBUTTONUP:\r
7078     if (GetKeyState(VK_SHIFT) & ~1) {\r
7079       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7080         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7081     }\r
7082     return 0;\r
7083   case WM_PASTE:\r
7084     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7085     SetFocus(hInput);\r
7086     return SendMessage(hInput, message, wParam, lParam);\r
7087   case WM_MBUTTONDOWN:\r
7088     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7089   case WM_COMMAND:\r
7090     switch (LOWORD(wParam)) {\r
7091     case IDM_QuickPaste:\r
7092       {\r
7093         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7094         if (sel.cpMin == sel.cpMax) {\r
7095           MessageBeep(MB_ICONEXCLAMATION);\r
7096           return 0;\r
7097         }\r
7098         SendMessage(hwnd, WM_COPY, 0, 0);\r
7099         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7100         SendMessage(hInput, WM_PASTE, 0, 0);\r
7101         SetFocus(hInput);\r
7102         return 0;\r
7103       }\r
7104     case IDM_Cut:\r
7105       SendMessage(hwnd, WM_CUT, 0, 0);\r
7106       return 0;\r
7107     case IDM_Paste:\r
7108       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7109       return 0;\r
7110     case IDM_Copy:\r
7111       SendMessage(hwnd, WM_COPY, 0, 0);\r
7112       return 0;\r
7113     default:\r
7114       {\r
7115         int i = LOWORD(wParam) - IDM_CommandX;\r
7116         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7117             icsTextMenuEntry[i].command != NULL) {\r
7118           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7119                    icsTextMenuEntry[i].getname,\r
7120                    icsTextMenuEntry[i].immediate);\r
7121           return 0;\r
7122         }\r
7123       }\r
7124       break;\r
7125     }\r
7126     break;\r
7127   }\r
7128   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7129 }\r
7130 \r
7131 WNDPROC consoleInputWindowProc;\r
7132 \r
7133 LRESULT CALLBACK\r
7134 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7135 {\r
7136   char buf[MSG_SIZ];\r
7137   char *p;\r
7138   static BOOL sendNextChar = FALSE;\r
7139   static BOOL quoteNextChar = FALSE;\r
7140   InputSource *is = consoleInputSource;\r
7141   CHARFORMAT cf;\r
7142   CHARRANGE sel;\r
7143 \r
7144   switch (message) {\r
7145   case WM_CHAR:\r
7146     if (!appData.localLineEditing || sendNextChar) {\r
7147       is->buf[0] = (CHAR) wParam;\r
7148       is->count = 1;\r
7149       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7150       sendNextChar = FALSE;\r
7151       return 0;\r
7152     }\r
7153     if (quoteNextChar) {\r
7154       buf[0] = (char) wParam;\r
7155       buf[1] = NULLCHAR;\r
7156       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7157       quoteNextChar = FALSE;\r
7158       return 0;\r
7159     }\r
7160     switch (wParam) {\r
7161     case '\r':   /* Enter key */\r
7162       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7163       if (consoleEcho) SaveInHistory(is->buf);\r
7164       is->buf[is->count++] = '\n';\r
7165       is->buf[is->count] = NULLCHAR;\r
7166       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7167       if (consoleEcho) {\r
7168         ConsoleOutput(is->buf, is->count, TRUE);\r
7169       } else if (appData.localLineEditing) {\r
7170         ConsoleOutput("\n", 1, TRUE);\r
7171       }\r
7172       /* fall thru */\r
7173     case '\033': /* Escape key */\r
7174       SetWindowText(hwnd, "");\r
7175       cf.cbSize = sizeof(CHARFORMAT);\r
7176       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7177       if (consoleEcho) {\r
7178         cf.crTextColor = textAttribs[ColorNormal].color;\r
7179       } else {\r
7180         cf.crTextColor = COLOR_ECHOOFF;\r
7181       }\r
7182       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7183       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7184       return 0;\r
7185     case '\t':   /* Tab key */\r
7186       if (GetKeyState(VK_SHIFT) < 0) {\r
7187         /* shifted */\r
7188         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7189       } else {\r
7190         /* unshifted */\r
7191         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7192         if (buttonDesc[0].hwnd) {\r
7193           SetFocus(buttonDesc[0].hwnd);\r
7194         } else {\r
7195           SetFocus(hwndMain);\r
7196         }\r
7197       }\r
7198       return 0;\r
7199     case '\023': /* Ctrl+S */\r
7200       sendNextChar = TRUE;\r
7201       return 0;\r
7202     case '\021': /* Ctrl+Q */\r
7203       quoteNextChar = TRUE;\r
7204       return 0;\r
7205     JAWS_REPLAY\r
7206     default:\r
7207       break;\r
7208     }\r
7209     break;\r
7210   case WM_KEYDOWN:\r
7211     switch (wParam) {\r
7212     case VK_UP:\r
7213       GetWindowText(hwnd, buf, MSG_SIZ);\r
7214       p = PrevInHistory(buf);\r
7215       if (p != NULL) {\r
7216         SetWindowText(hwnd, p);\r
7217         sel.cpMin = 999999;\r
7218         sel.cpMax = 999999;\r
7219         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7220         return 0;\r
7221       }\r
7222       break;\r
7223     case VK_DOWN:\r
7224       p = NextInHistory();\r
7225       if (p != NULL) {\r
7226         SetWindowText(hwnd, p);\r
7227         sel.cpMin = 999999;\r
7228         sel.cpMax = 999999;\r
7229         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7230         return 0;\r
7231       }\r
7232       break;\r
7233     case VK_HOME:\r
7234     case VK_END:\r
7235       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7236       /* fall thru */\r
7237     case VK_PRIOR:\r
7238     case VK_NEXT:\r
7239       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7240       return 0;\r
7241     }\r
7242     break;\r
7243   case WM_MBUTTONDOWN:\r
7244     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7245       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7246     break;\r
7247   case WM_RBUTTONUP:\r
7248     if (GetKeyState(VK_SHIFT) & ~1) {\r
7249       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7250         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7251     } else {\r
7252       POINT pt;\r
7253       HMENU hmenu;\r
7254       hmenu = LoadMenu(hInst, "InputMenu");\r
7255       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7256       if (sel.cpMin == sel.cpMax) {\r
7257         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7258         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7259       }\r
7260       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7261         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7262       }\r
7263       pt.x = LOWORD(lParam);\r
7264       pt.y = HIWORD(lParam);\r
7265       MenuPopup(hwnd, pt, hmenu, -1);\r
7266     }\r
7267     return 0;\r
7268   case WM_COMMAND:\r
7269     switch (LOWORD(wParam)) { \r
7270     case IDM_Undo:\r
7271       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7272       return 0;\r
7273     case IDM_SelectAll:\r
7274       sel.cpMin = 0;\r
7275       sel.cpMax = -1; /*999999?*/\r
7276       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7277       return 0;\r
7278     case IDM_Cut:\r
7279       SendMessage(hwnd, WM_CUT, 0, 0);\r
7280       return 0;\r
7281     case IDM_Paste:\r
7282       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7283       return 0;\r
7284     case IDM_Copy:\r
7285       SendMessage(hwnd, WM_COPY, 0, 0);\r
7286       return 0;\r
7287     }\r
7288     break;\r
7289   }\r
7290   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7291 }\r
7292 \r
7293 #define CO_MAX  100000\r
7294 #define CO_TRIM   1000\r
7295 \r
7296 LRESULT CALLBACK\r
7297 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7298 {\r
7299   static SnapData sd;\r
7300   HWND hText, hInput;\r
7301   RECT rect;\r
7302   static int sizeX, sizeY;\r
7303   int newSizeX, newSizeY;\r
7304   MINMAXINFO *mmi;\r
7305   WORD wMask;\r
7306 \r
7307   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7308   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7309 \r
7310   switch (message) {\r
7311   case WM_NOTIFY:\r
7312     if (((NMHDR*)lParam)->code == EN_LINK)\r
7313     {\r
7314       ENLINK *pLink = (ENLINK*)lParam;\r
7315       if (pLink->msg == WM_LBUTTONUP)\r
7316       {\r
7317         TEXTRANGE tr;\r
7318 \r
7319         tr.chrg = pLink->chrg;\r
7320         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7321         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7322         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7323         free(tr.lpstrText);\r
7324       }\r
7325     }\r
7326     break;\r
7327   case WM_INITDIALOG: /* message: initialize dialog box */\r
7328     hwndConsole = hDlg;\r
7329     SetFocus(hInput);\r
7330     consoleTextWindowProc = (WNDPROC)\r
7331       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7332     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7333     consoleInputWindowProc = (WNDPROC)\r
7334       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7335     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7336     Colorize(ColorNormal, TRUE);\r
7337     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7338     ChangedConsoleFont();\r
7339     GetClientRect(hDlg, &rect);\r
7340     sizeX = rect.right;\r
7341     sizeY = rect.bottom;\r
7342     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7343         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7344       WINDOWPLACEMENT wp;\r
7345       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7346       wp.length = sizeof(WINDOWPLACEMENT);\r
7347       wp.flags = 0;\r
7348       wp.showCmd = SW_SHOW;\r
7349       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7350       wp.rcNormalPosition.left = wpConsole.x;\r
7351       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7352       wp.rcNormalPosition.top = wpConsole.y;\r
7353       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7354       SetWindowPlacement(hDlg, &wp);\r
7355     }\r
7356 \r
7357    // [HGM] Chessknight's change 2004-07-13\r
7358    else { /* Determine Defaults */\r
7359        WINDOWPLACEMENT wp;\r
7360        wpConsole.x = wpMain.width + 1;\r
7361        wpConsole.y = wpMain.y;\r
7362        wpConsole.width = screenWidth -  wpMain.width;\r
7363        wpConsole.height = wpMain.height;\r
7364        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7365        wp.length = sizeof(WINDOWPLACEMENT);\r
7366        wp.flags = 0;\r
7367        wp.showCmd = SW_SHOW;\r
7368        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7369        wp.rcNormalPosition.left = wpConsole.x;\r
7370        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7371        wp.rcNormalPosition.top = wpConsole.y;\r
7372        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7373        SetWindowPlacement(hDlg, &wp);\r
7374     }\r
7375 \r
7376    // Allow hText to highlight URLs and send notifications on them\r
7377    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7378    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7379    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7380    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7381 \r
7382     return FALSE;\r
7383 \r
7384   case WM_SETFOCUS:\r
7385     SetFocus(hInput);\r
7386     return 0;\r
7387 \r
7388   case WM_CLOSE:\r
7389     ExitEvent(0);\r
7390     /* not reached */\r
7391     break;\r
7392 \r
7393   case WM_SIZE:\r
7394     if (IsIconic(hDlg)) break;\r
7395     newSizeX = LOWORD(lParam);\r
7396     newSizeY = HIWORD(lParam);\r
7397     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7398       RECT rectText, rectInput;\r
7399       POINT pt;\r
7400       int newTextHeight, newTextWidth;\r
7401       GetWindowRect(hText, &rectText);\r
7402       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7403       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7404       if (newTextHeight < 0) {\r
7405         newSizeY += -newTextHeight;\r
7406         newTextHeight = 0;\r
7407       }\r
7408       SetWindowPos(hText, NULL, 0, 0,\r
7409         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7410       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7411       pt.x = rectInput.left;\r
7412       pt.y = rectInput.top + newSizeY - sizeY;\r
7413       ScreenToClient(hDlg, &pt);\r
7414       SetWindowPos(hInput, NULL, \r
7415         pt.x, pt.y, /* needs client coords */   \r
7416         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7417         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7418     }\r
7419     sizeX = newSizeX;\r
7420     sizeY = newSizeY;\r
7421     break;\r
7422 \r
7423   case WM_GETMINMAXINFO:\r
7424     /* Prevent resizing window too small */\r
7425     mmi = (MINMAXINFO *) lParam;\r
7426     mmi->ptMinTrackSize.x = 100;\r
7427     mmi->ptMinTrackSize.y = 100;\r
7428     break;\r
7429 \r
7430   /* [AS] Snapping */\r
7431   case WM_ENTERSIZEMOVE:\r
7432     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7433 \r
7434   case WM_SIZING:\r
7435     return OnSizing( &sd, hDlg, wParam, lParam );\r
7436 \r
7437   case WM_MOVING:\r
7438     return OnMoving( &sd, hDlg, wParam, lParam );\r
7439 \r
7440   case WM_EXITSIZEMOVE:\r
7441         UpdateICSWidth(hText);\r
7442     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7443   }\r
7444 \r
7445   return DefWindowProc(hDlg, message, wParam, lParam);\r
7446 }\r
7447 \r
7448 \r
7449 VOID\r
7450 ConsoleCreate()\r
7451 {\r
7452   HWND hCons;\r
7453   if (hwndConsole) return;\r
7454   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7455   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7456 }\r
7457 \r
7458 \r
7459 VOID\r
7460 ConsoleOutput(char* data, int length, int forceVisible)\r
7461 {\r
7462   HWND hText;\r
7463   int trim, exlen;\r
7464   char *p, *q;\r
7465   char buf[CO_MAX+1];\r
7466   POINT pEnd;\r
7467   RECT rect;\r
7468   static int delayLF = 0;\r
7469   CHARRANGE savesel, sel;\r
7470 \r
7471   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7472   p = data;\r
7473   q = buf;\r
7474   if (delayLF) {\r
7475     *q++ = '\r';\r
7476     *q++ = '\n';\r
7477     delayLF = 0;\r
7478   }\r
7479   while (length--) {\r
7480     if (*p == '\n') {\r
7481       if (*++p) {\r
7482         *q++ = '\r';\r
7483         *q++ = '\n';\r
7484       } else {\r
7485         delayLF = 1;\r
7486       }\r
7487     } else if (*p == '\007') {\r
7488        MyPlaySound(&sounds[(int)SoundBell]);\r
7489        p++;\r
7490     } else {\r
7491       *q++ = *p++;\r
7492     }\r
7493   }\r
7494   *q = NULLCHAR;\r
7495   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7496   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7497   /* Save current selection */\r
7498   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7499   exlen = GetWindowTextLength(hText);\r
7500   /* Find out whether current end of text is visible */\r
7501   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7502   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7503   /* Trim existing text if it's too long */\r
7504   if (exlen + (q - buf) > CO_MAX) {\r
7505     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7506     sel.cpMin = 0;\r
7507     sel.cpMax = trim;\r
7508     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7509     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7510     exlen -= trim;\r
7511     savesel.cpMin -= trim;\r
7512     savesel.cpMax -= trim;\r
7513     if (exlen < 0) exlen = 0;\r
7514     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7515     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7516   }\r
7517   /* Append the new text */\r
7518   sel.cpMin = exlen;\r
7519   sel.cpMax = exlen;\r
7520   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7521   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7522   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7523   if (forceVisible || exlen == 0 ||\r
7524       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7525        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7526     /* Scroll to make new end of text visible if old end of text\r
7527        was visible or new text is an echo of user typein */\r
7528     sel.cpMin = 9999999;\r
7529     sel.cpMax = 9999999;\r
7530     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7531     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7532     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7533     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7534   }\r
7535   if (savesel.cpMax == exlen || forceVisible) {\r
7536     /* Move insert point to new end of text if it was at the old\r
7537        end of text or if the new text is an echo of user typein */\r
7538     sel.cpMin = 9999999;\r
7539     sel.cpMax = 9999999;\r
7540     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7541   } else {\r
7542     /* Restore previous selection */\r
7543     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7544   }\r
7545   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7546 }\r
7547 \r
7548 /*---------*/\r
7549 \r
7550 \r
7551 void\r
7552 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7553 {\r
7554   char buf[100];\r
7555   char *str;\r
7556   COLORREF oldFg, oldBg;\r
7557   HFONT oldFont;\r
7558   RECT rect;\r
7559 \r
7560   if(copyNumber > 1)\r
7561     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7562 \r
7563   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7564   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7565   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7566 \r
7567   rect.left = x;\r
7568   rect.right = x + squareSize;\r
7569   rect.top  = y;\r
7570   rect.bottom = y + squareSize;\r
7571   str = buf;\r
7572 \r
7573   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7574                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7575              y, ETO_CLIPPED|ETO_OPAQUE,\r
7576              &rect, str, strlen(str), NULL);\r
7577 \r
7578   (void) SetTextColor(hdc, oldFg);\r
7579   (void) SetBkColor(hdc, oldBg);\r
7580   (void) SelectObject(hdc, oldFont);\r
7581 }\r
7582 \r
7583 void\r
7584 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7585               RECT *rect, char *color, char *flagFell)\r
7586 {\r
7587   char buf[100];\r
7588   char *str;\r
7589   COLORREF oldFg, oldBg;\r
7590   HFONT oldFont;\r
7591 \r
7592   if (twoBoards && partnerUp) return;\r
7593   if (appData.clockMode) {\r
7594     if (tinyLayout)\r
7595       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7596     else\r
7597       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7598     str = buf;\r
7599   } else {\r
7600     str = color;\r
7601   }\r
7602 \r
7603   if (highlight) {\r
7604     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7605     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7606   } else {\r
7607     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7608     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7609   }\r
7610   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7611 \r
7612   JAWS_SILENCE\r
7613 \r
7614   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7615              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7616              rect, str, strlen(str), NULL);\r
7617   if(logoHeight > 0 && appData.clockMode) {\r
7618       RECT r;\r
7619       str += strlen(color)+2;\r
7620       r.top = rect->top + logoHeight/2;\r
7621       r.left = rect->left;\r
7622       r.right = rect->right;\r
7623       r.bottom = rect->bottom;\r
7624       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7625                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7626                  &r, str, strlen(str), NULL);\r
7627   }\r
7628   (void) SetTextColor(hdc, oldFg);\r
7629   (void) SetBkColor(hdc, oldBg);\r
7630   (void) SelectObject(hdc, oldFont);\r
7631 }\r
7632 \r
7633 \r
7634 int\r
7635 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7636            OVERLAPPED *ovl)\r
7637 {\r
7638   int ok, err;\r
7639 \r
7640   /* [AS]  */\r
7641   if( count <= 0 ) {\r
7642     if (appData.debugMode) {\r
7643       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7644     }\r
7645 \r
7646     return ERROR_INVALID_USER_BUFFER;\r
7647   }\r
7648 \r
7649   ResetEvent(ovl->hEvent);\r
7650   ovl->Offset = ovl->OffsetHigh = 0;\r
7651   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7652   if (ok) {\r
7653     err = NO_ERROR;\r
7654   } else {\r
7655     err = GetLastError();\r
7656     if (err == ERROR_IO_PENDING) {\r
7657       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7658       if (ok)\r
7659         err = NO_ERROR;\r
7660       else\r
7661         err = GetLastError();\r
7662     }\r
7663   }\r
7664   return err;\r
7665 }\r
7666 \r
7667 int\r
7668 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7669             OVERLAPPED *ovl)\r
7670 {\r
7671   int ok, err;\r
7672 \r
7673   ResetEvent(ovl->hEvent);\r
7674   ovl->Offset = ovl->OffsetHigh = 0;\r
7675   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7676   if (ok) {\r
7677     err = NO_ERROR;\r
7678   } else {\r
7679     err = GetLastError();\r
7680     if (err == ERROR_IO_PENDING) {\r
7681       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7682       if (ok)\r
7683         err = NO_ERROR;\r
7684       else\r
7685         err = GetLastError();\r
7686     }\r
7687   }\r
7688   return err;\r
7689 }\r
7690 \r
7691 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7692 void CheckForInputBufferFull( InputSource * is )\r
7693 {\r
7694     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7695         /* Look for end of line */\r
7696         char * p = is->buf;\r
7697         \r
7698         while( p < is->next && *p != '\n' ) {\r
7699             p++;\r
7700         }\r
7701 \r
7702         if( p >= is->next ) {\r
7703             if (appData.debugMode) {\r
7704                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7705             }\r
7706 \r
7707             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7708             is->count = (DWORD) -1;\r
7709             is->next = is->buf;\r
7710         }\r
7711     }\r
7712 }\r
7713 \r
7714 DWORD\r
7715 InputThread(LPVOID arg)\r
7716 {\r
7717   InputSource *is;\r
7718   OVERLAPPED ovl;\r
7719 \r
7720   is = (InputSource *) arg;\r
7721   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7722   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7723   while (is->hThread != NULL) {\r
7724     is->error = DoReadFile(is->hFile, is->next,\r
7725                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7726                            &is->count, &ovl);\r
7727     if (is->error == NO_ERROR) {\r
7728       is->next += is->count;\r
7729     } else {\r
7730       if (is->error == ERROR_BROKEN_PIPE) {\r
7731         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7732         is->count = 0;\r
7733       } else {\r
7734         is->count = (DWORD) -1;\r
7735         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7736         break; \r
7737       }\r
7738     }\r
7739 \r
7740     CheckForInputBufferFull( is );\r
7741 \r
7742     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7743 \r
7744     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7745 \r
7746     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7747   }\r
7748 \r
7749   CloseHandle(ovl.hEvent);\r
7750   CloseHandle(is->hFile);\r
7751 \r
7752   if (appData.debugMode) {\r
7753     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7754   }\r
7755 \r
7756   return 0;\r
7757 }\r
7758 \r
7759 \r
7760 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7761 DWORD\r
7762 NonOvlInputThread(LPVOID arg)\r
7763 {\r
7764   InputSource *is;\r
7765   char *p, *q;\r
7766   int i;\r
7767   char prev;\r
7768 \r
7769   is = (InputSource *) arg;\r
7770   while (is->hThread != NULL) {\r
7771     is->error = ReadFile(is->hFile, is->next,\r
7772                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7773                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7774     if (is->error == NO_ERROR) {\r
7775       /* Change CRLF to LF */\r
7776       if (is->next > is->buf) {\r
7777         p = is->next - 1;\r
7778         i = is->count + 1;\r
7779       } else {\r
7780         p = is->next;\r
7781         i = is->count;\r
7782       }\r
7783       q = p;\r
7784       prev = NULLCHAR;\r
7785       while (i > 0) {\r
7786         if (prev == '\r' && *p == '\n') {\r
7787           *(q-1) = '\n';\r
7788           is->count--;\r
7789         } else { \r
7790           *q++ = *p;\r
7791         }\r
7792         prev = *p++;\r
7793         i--;\r
7794       }\r
7795       *q = NULLCHAR;\r
7796       is->next = q;\r
7797     } else {\r
7798       if (is->error == ERROR_BROKEN_PIPE) {\r
7799         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7800         is->count = 0; \r
7801       } else {\r
7802         is->count = (DWORD) -1;\r
7803       }\r
7804     }\r
7805 \r
7806     CheckForInputBufferFull( is );\r
7807 \r
7808     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7809 \r
7810     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7811 \r
7812     if (is->count < 0) break;  /* Quit on error */\r
7813   }\r
7814   CloseHandle(is->hFile);\r
7815   return 0;\r
7816 }\r
7817 \r
7818 DWORD\r
7819 SocketInputThread(LPVOID arg)\r
7820 {\r
7821   InputSource *is;\r
7822 \r
7823   is = (InputSource *) arg;\r
7824   while (is->hThread != NULL) {\r
7825     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7826     if ((int)is->count == SOCKET_ERROR) {\r
7827       is->count = (DWORD) -1;\r
7828       is->error = WSAGetLastError();\r
7829     } else {\r
7830       is->error = NO_ERROR;\r
7831       is->next += is->count;\r
7832       if (is->count == 0 && is->second == is) {\r
7833         /* End of file on stderr; quit with no message */\r
7834         break;\r
7835       }\r
7836     }\r
7837     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7838 \r
7839     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7840 \r
7841     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7842   }\r
7843   return 0;\r
7844 }\r
7845 \r
7846 VOID\r
7847 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7848 {\r
7849   InputSource *is;\r
7850 \r
7851   is = (InputSource *) lParam;\r
7852   if (is->lineByLine) {\r
7853     /* Feed in lines one by one */\r
7854     char *p = is->buf;\r
7855     char *q = p;\r
7856     while (q < is->next) {\r
7857       if (*q++ == '\n') {\r
7858         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7859         p = q;\r
7860       }\r
7861     }\r
7862     \r
7863     /* Move any partial line to the start of the buffer */\r
7864     q = is->buf;\r
7865     while (p < is->next) {\r
7866       *q++ = *p++;\r
7867     }\r
7868     is->next = q;\r
7869 \r
7870     if (is->error != NO_ERROR || is->count == 0) {\r
7871       /* Notify backend of the error.  Note: If there was a partial\r
7872          line at the end, it is not flushed through. */\r
7873       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7874     }\r
7875   } else {\r
7876     /* Feed in the whole chunk of input at once */\r
7877     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7878     is->next = is->buf;\r
7879   }\r
7880 }\r
7881 \r
7882 /*---------------------------------------------------------------------------*\\r
7883  *\r
7884  *  Menu enables. Used when setting various modes.\r
7885  *\r
7886 \*---------------------------------------------------------------------------*/\r
7887 \r
7888 typedef struct {\r
7889   int item;\r
7890   int flags;\r
7891 } Enables;\r
7892 \r
7893 VOID\r
7894 GreyRevert(Boolean grey)\r
7895 { // [HGM] vari: for retracting variations in local mode\r
7896   HMENU hmenu = GetMenu(hwndMain);\r
7897   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7898   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7899 }\r
7900 \r
7901 VOID\r
7902 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7903 {\r
7904   while (enab->item > 0) {\r
7905     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7906     enab++;\r
7907   }\r
7908 }\r
7909 \r
7910 Enables gnuEnables[] = {\r
7911   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7913   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7918   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7919   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7920   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7921   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7922   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7923   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7924 \r
7925   // Needed to switch from ncp to GNU mode on Engine Load\r
7926   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7927   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7928   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7929   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7930   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7931   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7932   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7933   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7934   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7935   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7936   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7937   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7938   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7939   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7940   { -1, -1 }\r
7941 };\r
7942 \r
7943 Enables icsEnables[] = {\r
7944   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7945   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7946   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7952   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7960   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7964   { -1, -1 }\r
7965 };\r
7966 \r
7967 #if ZIPPY\r
7968 Enables zippyEnables[] = {\r
7969   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7970   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7971   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7972   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7973   { -1, -1 }\r
7974 };\r
7975 #endif\r
7976 \r
7977 Enables ncpEnables[] = {\r
7978   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7981   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7982   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7983   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7984   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7985   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7986   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7987   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7988   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7989   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7990   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7991   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7992   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7993   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7994   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7995   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8000   { -1, -1 }\r
8001 };\r
8002 \r
8003 Enables trainingOnEnables[] = {\r
8004   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8005   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8013   { -1, -1 }\r
8014 };\r
8015 \r
8016 Enables trainingOffEnables[] = {\r
8017   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8018   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8019   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8020   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8021   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8022   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8023   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8024   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8025   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8026   { -1, -1 }\r
8027 };\r
8028 \r
8029 /* These modify either ncpEnables or gnuEnables */\r
8030 Enables cmailEnables[] = {\r
8031   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8032   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8033   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8034   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8038   { -1, -1 }\r
8039 };\r
8040 \r
8041 Enables machineThinkingEnables[] = {\r
8042   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8043   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8044   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8045   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8046   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8047   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8048   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8050   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8051   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8052   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8053   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8054   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8055 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8056   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8057   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8058   { -1, -1 }\r
8059 };\r
8060 \r
8061 Enables userThinkingEnables[] = {\r
8062   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8063   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8064   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8065   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8066   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8067   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8068   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8069   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8070   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8071   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8072   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8073   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8074   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8075 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8076   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8077   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8078   { -1, -1 }\r
8079 };\r
8080 \r
8081 /*---------------------------------------------------------------------------*\\r
8082  *\r
8083  *  Front-end interface functions exported by XBoard.\r
8084  *  Functions appear in same order as prototypes in frontend.h.\r
8085  * \r
8086 \*---------------------------------------------------------------------------*/\r
8087 VOID\r
8088 CheckMark(UINT item, int state)\r
8089 {\r
8090     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8091 }\r
8092 \r
8093 VOID\r
8094 ModeHighlight()\r
8095 {\r
8096   static UINT prevChecked = 0;\r
8097   static int prevPausing = 0;\r
8098   UINT nowChecked;\r
8099 \r
8100   if (pausing != prevPausing) {\r
8101     prevPausing = pausing;\r
8102     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8103                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8104     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8105   }\r
8106 \r
8107   switch (gameMode) {\r
8108   case BeginningOfGame:\r
8109     if (appData.icsActive)\r
8110       nowChecked = IDM_IcsClient;\r
8111     else if (appData.noChessProgram)\r
8112       nowChecked = IDM_EditGame;\r
8113     else\r
8114       nowChecked = IDM_MachineBlack;\r
8115     break;\r
8116   case MachinePlaysBlack:\r
8117     nowChecked = IDM_MachineBlack;\r
8118     break;\r
8119   case MachinePlaysWhite:\r
8120     nowChecked = IDM_MachineWhite;\r
8121     break;\r
8122   case TwoMachinesPlay:\r
8123     nowChecked = IDM_TwoMachines;\r
8124     break;\r
8125   case AnalyzeMode:\r
8126     nowChecked = IDM_AnalysisMode;\r
8127     break;\r
8128   case AnalyzeFile:\r
8129     nowChecked = IDM_AnalyzeFile;\r
8130     break;\r
8131   case EditGame:\r
8132     nowChecked = IDM_EditGame;\r
8133     break;\r
8134   case PlayFromGameFile:\r
8135     nowChecked = IDM_LoadGame;\r
8136     break;\r
8137   case EditPosition:\r
8138     nowChecked = IDM_EditPosition;\r
8139     break;\r
8140   case Training:\r
8141     nowChecked = IDM_Training;\r
8142     break;\r
8143   case IcsPlayingWhite:\r
8144   case IcsPlayingBlack:\r
8145   case IcsObserving:\r
8146   case IcsIdle:\r
8147     nowChecked = IDM_IcsClient;\r
8148     break;\r
8149   default:\r
8150   case EndOfGame:\r
8151     nowChecked = 0;\r
8152     break;\r
8153   }\r
8154   CheckMark(prevChecked, MF_UNCHECKED);\r
8155   CheckMark(nowChecked, MF_CHECKED);\r
8156   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8157 \r
8158   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8159     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8160                           MF_BYCOMMAND|MF_ENABLED);\r
8161   } else {\r
8162     (void) EnableMenuItem(GetMenu(hwndMain), \r
8163                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8164   }\r
8165 \r
8166   prevChecked = nowChecked;\r
8167 \r
8168   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8169   if (appData.icsActive) {\r
8170        if (appData.icsEngineAnalyze) {\r
8171                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8172        } else {\r
8173                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8174        }\r
8175   }\r
8176   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8177 }\r
8178 \r
8179 VOID\r
8180 SetICSMode()\r
8181 {\r
8182   HMENU hmenu = GetMenu(hwndMain);\r
8183   SetMenuEnables(hmenu, icsEnables);\r
8184   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8185     MF_BYCOMMAND|MF_ENABLED);\r
8186 #if ZIPPY\r
8187   if (appData.zippyPlay) {\r
8188     SetMenuEnables(hmenu, zippyEnables);\r
8189     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8190          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8191           MF_BYCOMMAND|MF_ENABLED);\r
8192   }\r
8193 #endif\r
8194 }\r
8195 \r
8196 VOID\r
8197 SetGNUMode()\r
8198 {\r
8199   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8200 }\r
8201 \r
8202 VOID\r
8203 SetNCPMode()\r
8204 {\r
8205   HMENU hmenu = GetMenu(hwndMain);\r
8206   SetMenuEnables(hmenu, ncpEnables);\r
8207     DrawMenuBar(hwndMain);\r
8208 }\r
8209 \r
8210 VOID\r
8211 SetCmailMode()\r
8212 {\r
8213   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8214 }\r
8215 \r
8216 VOID \r
8217 SetTrainingModeOn()\r
8218 {\r
8219   int i;\r
8220   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8221   for (i = 0; i < N_BUTTONS; i++) {\r
8222     if (buttonDesc[i].hwnd != NULL)\r
8223       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8224   }\r
8225   CommentPopDown();\r
8226 }\r
8227 \r
8228 VOID SetTrainingModeOff()\r
8229 {\r
8230   int i;\r
8231   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8232   for (i = 0; i < N_BUTTONS; i++) {\r
8233     if (buttonDesc[i].hwnd != NULL)\r
8234       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8235   }\r
8236 }\r
8237 \r
8238 \r
8239 VOID\r
8240 SetUserThinkingEnables()\r
8241 {\r
8242   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8243 }\r
8244 \r
8245 VOID\r
8246 SetMachineThinkingEnables()\r
8247 {\r
8248   HMENU hMenu = GetMenu(hwndMain);\r
8249   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8250 \r
8251   SetMenuEnables(hMenu, machineThinkingEnables);\r
8252 \r
8253   if (gameMode == MachinePlaysBlack) {\r
8254     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8255   } else if (gameMode == MachinePlaysWhite) {\r
8256     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8257   } else if (gameMode == TwoMachinesPlay) {\r
8258     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8259   }\r
8260 }\r
8261 \r
8262 \r
8263 VOID\r
8264 DisplayTitle(char *str)\r
8265 {\r
8266   char title[MSG_SIZ], *host;\r
8267   if (str[0] != NULLCHAR) {\r
8268     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8269   } else if (appData.icsActive) {\r
8270     if (appData.icsCommPort[0] != NULLCHAR)\r
8271       host = "ICS";\r
8272     else \r
8273       host = appData.icsHost;\r
8274       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8275   } else if (appData.noChessProgram) {\r
8276     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8277   } else {\r
8278     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8279     strcat(title, ": ");\r
8280     strcat(title, first.tidy);\r
8281   }\r
8282   SetWindowText(hwndMain, title);\r
8283 }\r
8284 \r
8285 \r
8286 VOID\r
8287 DisplayMessage(char *str1, char *str2)\r
8288 {\r
8289   HDC hdc;\r
8290   HFONT oldFont;\r
8291   int remain = MESSAGE_TEXT_MAX - 1;\r
8292   int len;\r
8293 \r
8294   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8295   messageText[0] = NULLCHAR;\r
8296   if (*str1) {\r
8297     len = strlen(str1);\r
8298     if (len > remain) len = remain;\r
8299     strncpy(messageText, str1, len);\r
8300     messageText[len] = NULLCHAR;\r
8301     remain -= len;\r
8302   }\r
8303   if (*str2 && remain >= 2) {\r
8304     if (*str1) {\r
8305       strcat(messageText, "  ");\r
8306       remain -= 2;\r
8307     }\r
8308     len = strlen(str2);\r
8309     if (len > remain) len = remain;\r
8310     strncat(messageText, str2, len);\r
8311   }\r
8312   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8313   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8314 \r
8315   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8316 \r
8317   SAYMACHINEMOVE();\r
8318 \r
8319   hdc = GetDC(hwndMain);\r
8320   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8321   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8322              &messageRect, messageText, strlen(messageText), NULL);\r
8323   (void) SelectObject(hdc, oldFont);\r
8324   (void) ReleaseDC(hwndMain, hdc);\r
8325 }\r
8326 \r
8327 VOID\r
8328 DisplayError(char *str, int error)\r
8329 {\r
8330   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8331   int len;\r
8332 \r
8333   if (error == 0) {\r
8334     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8335   } else {\r
8336     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8337                         NULL, error, LANG_NEUTRAL,\r
8338                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8339     if (len > 0) {\r
8340       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8341     } else {\r
8342       ErrorMap *em = errmap;\r
8343       while (em->err != 0 && em->err != error) em++;\r
8344       if (em->err != 0) {\r
8345         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8346       } else {\r
8347         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8348       }\r
8349     }\r
8350   }\r
8351   \r
8352   ErrorPopUp(_("Error"), buf);\r
8353 }\r
8354 \r
8355 \r
8356 VOID\r
8357 DisplayMoveError(char *str)\r
8358 {\r
8359   fromX = fromY = -1;\r
8360   ClearHighlights();\r
8361   DrawPosition(FALSE, NULL);\r
8362   if (appData.popupMoveErrors) {\r
8363     ErrorPopUp(_("Error"), str);\r
8364   } else {\r
8365     DisplayMessage(str, "");\r
8366     moveErrorMessageUp = TRUE;\r
8367   }\r
8368 }\r
8369 \r
8370 VOID\r
8371 DisplayFatalError(char *str, int error, int exitStatus)\r
8372 {\r
8373   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8374   int len;\r
8375   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8376 \r
8377   if (error != 0) {\r
8378     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8379                         NULL, error, LANG_NEUTRAL,\r
8380                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8381     if (len > 0) {\r
8382       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8383     } else {\r
8384       ErrorMap *em = errmap;\r
8385       while (em->err != 0 && em->err != error) em++;\r
8386       if (em->err != 0) {\r
8387         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8388       } else {\r
8389         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8390       }\r
8391     }\r
8392     str = buf;\r
8393   }\r
8394   if (appData.debugMode) {\r
8395     fprintf(debugFP, "%s: %s\n", label, str);\r
8396   }\r
8397   if (appData.popupExitMessage) {\r
8398     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8399                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8400   }\r
8401   ExitEvent(exitStatus);\r
8402 }\r
8403 \r
8404 \r
8405 VOID\r
8406 DisplayInformation(char *str)\r
8407 {\r
8408   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8409 }\r
8410 \r
8411 \r
8412 VOID\r
8413 DisplayNote(char *str)\r
8414 {\r
8415   ErrorPopUp(_("Note"), str);\r
8416 }\r
8417 \r
8418 \r
8419 typedef struct {\r
8420   char *title, *question, *replyPrefix;\r
8421   ProcRef pr;\r
8422 } QuestionParams;\r
8423 \r
8424 LRESULT CALLBACK\r
8425 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8426 {\r
8427   static QuestionParams *qp;\r
8428   char reply[MSG_SIZ];\r
8429   int len, err;\r
8430 \r
8431   switch (message) {\r
8432   case WM_INITDIALOG:\r
8433     qp = (QuestionParams *) lParam;\r
8434     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8435     Translate(hDlg, DLG_Question);\r
8436     SetWindowText(hDlg, qp->title);\r
8437     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8438     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8439     return FALSE;\r
8440 \r
8441   case WM_COMMAND:\r
8442     switch (LOWORD(wParam)) {\r
8443     case IDOK:\r
8444       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8445       if (*reply) strcat(reply, " ");\r
8446       len = strlen(reply);\r
8447       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8448       strcat(reply, "\n");\r
8449       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8450       EndDialog(hDlg, TRUE);\r
8451       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8452       return TRUE;\r
8453     case IDCANCEL:\r
8454       EndDialog(hDlg, FALSE);\r
8455       return TRUE;\r
8456     default:\r
8457       break;\r
8458     }\r
8459     break;\r
8460   }\r
8461   return FALSE;\r
8462 }\r
8463 \r
8464 VOID\r
8465 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8466 {\r
8467     QuestionParams qp;\r
8468     FARPROC lpProc;\r
8469     \r
8470     qp.title = title;\r
8471     qp.question = question;\r
8472     qp.replyPrefix = replyPrefix;\r
8473     qp.pr = pr;\r
8474     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8475     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8476       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8477     FreeProcInstance(lpProc);\r
8478 }\r
8479 \r
8480 /* [AS] Pick FRC position */\r
8481 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8482 {\r
8483     static int * lpIndexFRC;\r
8484     BOOL index_is_ok;\r
8485     char buf[16];\r
8486 \r
8487     switch( message )\r
8488     {\r
8489     case WM_INITDIALOG:\r
8490         lpIndexFRC = (int *) lParam;\r
8491 \r
8492         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8493         Translate(hDlg, DLG_NewGameFRC);\r
8494 \r
8495         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8496         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8497         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8498         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8499 \r
8500         break;\r
8501 \r
8502     case WM_COMMAND:\r
8503         switch( LOWORD(wParam) ) {\r
8504         case IDOK:\r
8505             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8506             EndDialog( hDlg, 0 );\r
8507             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8508             return TRUE;\r
8509         case IDCANCEL:\r
8510             EndDialog( hDlg, 1 );   \r
8511             return TRUE;\r
8512         case IDC_NFG_Edit:\r
8513             if( HIWORD(wParam) == EN_CHANGE ) {\r
8514                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8515 \r
8516                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8517             }\r
8518             return TRUE;\r
8519         case IDC_NFG_Random:\r
8520           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8521             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8522             return TRUE;\r
8523         }\r
8524 \r
8525         break;\r
8526     }\r
8527 \r
8528     return FALSE;\r
8529 }\r
8530 \r
8531 int NewGameFRC()\r
8532 {\r
8533     int result;\r
8534     int index = appData.defaultFrcPosition;\r
8535     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8536 \r
8537     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8538 \r
8539     if( result == 0 ) {\r
8540         appData.defaultFrcPosition = index;\r
8541     }\r
8542 \r
8543     return result;\r
8544 }\r
8545 \r
8546 /* [AS] Game list options. Refactored by HGM */\r
8547 \r
8548 HWND gameListOptionsDialog;\r
8549 \r
8550 // low-level front-end: clear text edit / list widget\r
8551 void\r
8552 GLT_ClearList()\r
8553 {\r
8554     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8555 }\r
8556 \r
8557 // low-level front-end: clear text edit / list widget\r
8558 void\r
8559 GLT_DeSelectList()\r
8560 {\r
8561     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8562 }\r
8563 \r
8564 // low-level front-end: append line to text edit / list widget\r
8565 void\r
8566 GLT_AddToList( char *name )\r
8567 {\r
8568     if( name != 0 ) {\r
8569             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8570     }\r
8571 }\r
8572 \r
8573 // low-level front-end: get line from text edit / list widget\r
8574 Boolean\r
8575 GLT_GetFromList( int index, char *name )\r
8576 {\r
8577     if( name != 0 ) {\r
8578             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8579                 return TRUE;\r
8580     }\r
8581     return FALSE;\r
8582 }\r
8583 \r
8584 void GLT_MoveSelection( HWND hDlg, int delta )\r
8585 {\r
8586     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8587     int idx2 = idx1 + delta;\r
8588     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8589 \r
8590     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8591         char buf[128];\r
8592 \r
8593         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8594         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8595         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8596         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8597     }\r
8598 }\r
8599 \r
8600 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8601 {\r
8602     switch( message )\r
8603     {\r
8604     case WM_INITDIALOG:\r
8605         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8606         \r
8607         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8608         Translate(hDlg, DLG_GameListOptions);\r
8609 \r
8610         /* Initialize list */\r
8611         GLT_TagsToList( lpUserGLT );\r
8612 \r
8613         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8614 \r
8615         break;\r
8616 \r
8617     case WM_COMMAND:\r
8618         switch( LOWORD(wParam) ) {\r
8619         case IDOK:\r
8620             GLT_ParseList();\r
8621             EndDialog( hDlg, 0 );\r
8622             return TRUE;\r
8623         case IDCANCEL:\r
8624             EndDialog( hDlg, 1 );\r
8625             return TRUE;\r
8626 \r
8627         case IDC_GLT_Default:\r
8628             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8629             return TRUE;\r
8630 \r
8631         case IDC_GLT_Restore:\r
8632             GLT_TagsToList( appData.gameListTags );\r
8633             return TRUE;\r
8634 \r
8635         case IDC_GLT_Up:\r
8636             GLT_MoveSelection( hDlg, -1 );\r
8637             return TRUE;\r
8638 \r
8639         case IDC_GLT_Down:\r
8640             GLT_MoveSelection( hDlg, +1 );\r
8641             return TRUE;\r
8642         }\r
8643 \r
8644         break;\r
8645     }\r
8646 \r
8647     return FALSE;\r
8648 }\r
8649 \r
8650 int GameListOptions()\r
8651 {\r
8652     int result;\r
8653     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8654 \r
8655       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8656 \r
8657     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8658 \r
8659     if( result == 0 ) {\r
8660         /* [AS] Memory leak here! */\r
8661         appData.gameListTags = strdup( lpUserGLT ); \r
8662     }\r
8663 \r
8664     return result;\r
8665 }\r
8666 \r
8667 VOID\r
8668 DisplayIcsInteractionTitle(char *str)\r
8669 {\r
8670   char consoleTitle[MSG_SIZ];\r
8671 \r
8672     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8673     SetWindowText(hwndConsole, consoleTitle);\r
8674 \r
8675     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8676       char buf[MSG_SIZ], *p = buf, *q;\r
8677         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8678       do {\r
8679         q = strchr(p, ';');\r
8680         if(q) *q++ = 0;\r
8681         if(*p) ChatPopUp(p);\r
8682       } while(p=q);\r
8683     }\r
8684 \r
8685     SetActiveWindow(hwndMain);\r
8686 }\r
8687 \r
8688 void\r
8689 DrawPosition(int fullRedraw, Board board)\r
8690 {\r
8691   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8692 }\r
8693 \r
8694 void NotifyFrontendLogin()\r
8695 {\r
8696         if (hwndConsole)\r
8697                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8698 }\r
8699 \r
8700 VOID\r
8701 ResetFrontEnd()\r
8702 {\r
8703   fromX = fromY = -1;\r
8704   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8705     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8706     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8707     dragInfo.lastpos = dragInfo.pos;\r
8708     dragInfo.start.x = dragInfo.start.y = -1;\r
8709     dragInfo.from = dragInfo.start;\r
8710     ReleaseCapture();\r
8711     DrawPosition(TRUE, NULL);\r
8712   }\r
8713   TagsPopDown();\r
8714 }\r
8715 \r
8716 \r
8717 VOID\r
8718 CommentPopUp(char *title, char *str)\r
8719 {\r
8720   HWND hwnd = GetActiveWindow();\r
8721   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8722   SAY(str);\r
8723   SetActiveWindow(hwnd);\r
8724 }\r
8725 \r
8726 VOID\r
8727 CommentPopDown(void)\r
8728 {\r
8729   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8730   if (commentDialog) {\r
8731     ShowWindow(commentDialog, SW_HIDE);\r
8732   }\r
8733   commentUp = FALSE;\r
8734 }\r
8735 \r
8736 VOID\r
8737 EditCommentPopUp(int index, char *title, char *str)\r
8738 {\r
8739   EitherCommentPopUp(index, title, str, TRUE);\r
8740 }\r
8741 \r
8742 \r
8743 VOID\r
8744 RingBell()\r
8745 {\r
8746   MyPlaySound(&sounds[(int)SoundMove]);\r
8747 }\r
8748 \r
8749 VOID PlayIcsWinSound()\r
8750 {\r
8751   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8752 }\r
8753 \r
8754 VOID PlayIcsLossSound()\r
8755 {\r
8756   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8757 }\r
8758 \r
8759 VOID PlayIcsDrawSound()\r
8760 {\r
8761   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8762 }\r
8763 \r
8764 VOID PlayIcsUnfinishedSound()\r
8765 {\r
8766   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8767 }\r
8768 \r
8769 VOID\r
8770 PlayAlarmSound()\r
8771 {\r
8772   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8773 }\r
8774 \r
8775 VOID\r
8776 PlayTellSound()\r
8777 {\r
8778   MyPlaySound(&textAttribs[ColorTell].sound);\r
8779 }\r
8780 \r
8781 \r
8782 VOID\r
8783 EchoOn()\r
8784 {\r
8785   HWND hInput;\r
8786   consoleEcho = TRUE;\r
8787   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8788   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8789   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8790 }\r
8791 \r
8792 \r
8793 VOID\r
8794 EchoOff()\r
8795 {\r
8796   CHARFORMAT cf;\r
8797   HWND hInput;\r
8798   consoleEcho = FALSE;\r
8799   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8800   /* This works OK: set text and background both to the same color */\r
8801   cf = consoleCF;\r
8802   cf.crTextColor = COLOR_ECHOOFF;\r
8803   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8804   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8805 }\r
8806 \r
8807 /* No Raw()...? */\r
8808 \r
8809 void Colorize(ColorClass cc, int continuation)\r
8810 {\r
8811   currentColorClass = cc;\r
8812   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8813   consoleCF.crTextColor = textAttribs[cc].color;\r
8814   consoleCF.dwEffects = textAttribs[cc].effects;\r
8815   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8816 }\r
8817 \r
8818 char *\r
8819 UserName()\r
8820 {\r
8821   static char buf[MSG_SIZ];\r
8822   DWORD bufsiz = MSG_SIZ;\r
8823 \r
8824   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8825         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8826   }\r
8827   if (!GetUserName(buf, &bufsiz)) {\r
8828     /*DisplayError("Error getting user name", GetLastError());*/\r
8829     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8830   }\r
8831   return buf;\r
8832 }\r
8833 \r
8834 char *\r
8835 HostName()\r
8836 {\r
8837   static char buf[MSG_SIZ];\r
8838   DWORD bufsiz = MSG_SIZ;\r
8839 \r
8840   if (!GetComputerName(buf, &bufsiz)) {\r
8841     /*DisplayError("Error getting host name", GetLastError());*/\r
8842     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8843   }\r
8844   return buf;\r
8845 }\r
8846 \r
8847 \r
8848 int\r
8849 ClockTimerRunning()\r
8850 {\r
8851   return clockTimerEvent != 0;\r
8852 }\r
8853 \r
8854 int\r
8855 StopClockTimer()\r
8856 {\r
8857   if (clockTimerEvent == 0) return FALSE;\r
8858   KillTimer(hwndMain, clockTimerEvent);\r
8859   clockTimerEvent = 0;\r
8860   return TRUE;\r
8861 }\r
8862 \r
8863 void\r
8864 StartClockTimer(long millisec)\r
8865 {\r
8866   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8867                              (UINT) millisec, NULL);\r
8868 }\r
8869 \r
8870 void\r
8871 DisplayWhiteClock(long timeRemaining, int highlight)\r
8872 {\r
8873   HDC hdc;\r
8874   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8875 \r
8876   if(appData.noGUI) return;\r
8877   hdc = GetDC(hwndMain);\r
8878   if (!IsIconic(hwndMain)) {\r
8879     DisplayAClock(hdc, timeRemaining, highlight, \r
8880                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8881   }\r
8882   if (highlight && iconCurrent == iconBlack) {\r
8883     iconCurrent = iconWhite;\r
8884     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8885     if (IsIconic(hwndMain)) {\r
8886       DrawIcon(hdc, 2, 2, iconCurrent);\r
8887     }\r
8888   }\r
8889   (void) ReleaseDC(hwndMain, hdc);\r
8890   if (hwndConsole)\r
8891     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8892 }\r
8893 \r
8894 void\r
8895 DisplayBlackClock(long timeRemaining, int highlight)\r
8896 {\r
8897   HDC hdc;\r
8898   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8899 \r
8900   if(appData.noGUI) return;\r
8901   hdc = GetDC(hwndMain);\r
8902   if (!IsIconic(hwndMain)) {\r
8903     DisplayAClock(hdc, timeRemaining, highlight, \r
8904                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8905   }\r
8906   if (highlight && iconCurrent == iconWhite) {\r
8907     iconCurrent = iconBlack;\r
8908     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8909     if (IsIconic(hwndMain)) {\r
8910       DrawIcon(hdc, 2, 2, iconCurrent);\r
8911     }\r
8912   }\r
8913   (void) ReleaseDC(hwndMain, hdc);\r
8914   if (hwndConsole)\r
8915     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8916 }\r
8917 \r
8918 \r
8919 int\r
8920 LoadGameTimerRunning()\r
8921 {\r
8922   return loadGameTimerEvent != 0;\r
8923 }\r
8924 \r
8925 int\r
8926 StopLoadGameTimer()\r
8927 {\r
8928   if (loadGameTimerEvent == 0) return FALSE;\r
8929   KillTimer(hwndMain, loadGameTimerEvent);\r
8930   loadGameTimerEvent = 0;\r
8931   return TRUE;\r
8932 }\r
8933 \r
8934 void\r
8935 StartLoadGameTimer(long millisec)\r
8936 {\r
8937   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8938                                 (UINT) millisec, NULL);\r
8939 }\r
8940 \r
8941 void\r
8942 AutoSaveGame()\r
8943 {\r
8944   char *defName;\r
8945   FILE *f;\r
8946   char fileTitle[MSG_SIZ];\r
8947 \r
8948   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8949   f = OpenFileDialog(hwndMain, "a", defName,\r
8950                      appData.oldSaveStyle ? "gam" : "pgn",\r
8951                      GAME_FILT, \r
8952                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8953   if (f != NULL) {\r
8954     SaveGame(f, 0, "");\r
8955     fclose(f);\r
8956   }\r
8957 }\r
8958 \r
8959 \r
8960 void\r
8961 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8962 {\r
8963   if (delayedTimerEvent != 0) {\r
8964     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8965       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8966     }\r
8967     KillTimer(hwndMain, delayedTimerEvent);\r
8968     delayedTimerEvent = 0;\r
8969     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8970     delayedTimerCallback();\r
8971   }\r
8972   delayedTimerCallback = cb;\r
8973   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8974                                 (UINT) millisec, NULL);\r
8975 }\r
8976 \r
8977 DelayedEventCallback\r
8978 GetDelayedEvent()\r
8979 {\r
8980   if (delayedTimerEvent) {\r
8981     return delayedTimerCallback;\r
8982   } else {\r
8983     return NULL;\r
8984   }\r
8985 }\r
8986 \r
8987 void\r
8988 CancelDelayedEvent()\r
8989 {\r
8990   if (delayedTimerEvent) {\r
8991     KillTimer(hwndMain, delayedTimerEvent);\r
8992     delayedTimerEvent = 0;\r
8993   }\r
8994 }\r
8995 \r
8996 DWORD GetWin32Priority(int nice)\r
8997 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
8998 /*\r
8999 REALTIME_PRIORITY_CLASS     0x00000100\r
9000 HIGH_PRIORITY_CLASS         0x00000080\r
9001 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9002 NORMAL_PRIORITY_CLASS       0x00000020\r
9003 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9004 IDLE_PRIORITY_CLASS         0x00000040\r
9005 */\r
9006         if (nice < -15) return 0x00000080;\r
9007         if (nice < 0)   return 0x00008000;\r
9008         if (nice == 0)  return 0x00000020;\r
9009         if (nice < 15)  return 0x00004000;\r
9010         return 0x00000040;\r
9011 }\r
9012 \r
9013 void RunCommand(char *cmdLine)\r
9014 {\r
9015   /* Now create the child process. */\r
9016   STARTUPINFO siStartInfo;\r
9017   PROCESS_INFORMATION piProcInfo;\r
9018 \r
9019   siStartInfo.cb = sizeof(STARTUPINFO);\r
9020   siStartInfo.lpReserved = NULL;\r
9021   siStartInfo.lpDesktop = NULL;\r
9022   siStartInfo.lpTitle = NULL;\r
9023   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9024   siStartInfo.cbReserved2 = 0;\r
9025   siStartInfo.lpReserved2 = NULL;\r
9026   siStartInfo.hStdInput = NULL;\r
9027   siStartInfo.hStdOutput = NULL;\r
9028   siStartInfo.hStdError = NULL;\r
9029 \r
9030   CreateProcess(NULL,\r
9031                 cmdLine,           /* command line */\r
9032                 NULL,      /* process security attributes */\r
9033                 NULL,      /* primary thread security attrs */\r
9034                 TRUE,      /* handles are inherited */\r
9035                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9036                 NULL,      /* use parent's environment */\r
9037                 NULL,\r
9038                 &siStartInfo, /* STARTUPINFO pointer */\r
9039                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9040 \r
9041   CloseHandle(piProcInfo.hThread);\r
9042 }\r
9043 \r
9044 /* Start a child process running the given program.\r
9045    The process's standard output can be read from "from", and its\r
9046    standard input can be written to "to".\r
9047    Exit with fatal error if anything goes wrong.\r
9048    Returns an opaque pointer that can be used to destroy the process\r
9049    later.\r
9050 */\r
9051 int\r
9052 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9053 {\r
9054 #define BUFSIZE 4096\r
9055 \r
9056   HANDLE hChildStdinRd, hChildStdinWr,\r
9057     hChildStdoutRd, hChildStdoutWr;\r
9058   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9059   SECURITY_ATTRIBUTES saAttr;\r
9060   BOOL fSuccess;\r
9061   PROCESS_INFORMATION piProcInfo;\r
9062   STARTUPINFO siStartInfo;\r
9063   ChildProc *cp;\r
9064   char buf[MSG_SIZ];\r
9065   DWORD err;\r
9066 \r
9067   if (appData.debugMode) {\r
9068     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9069   }\r
9070 \r
9071   *pr = NoProc;\r
9072 \r
9073   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9074   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9075   saAttr.bInheritHandle = TRUE;\r
9076   saAttr.lpSecurityDescriptor = NULL;\r
9077 \r
9078   /*\r
9079    * The steps for redirecting child's STDOUT:\r
9080    *     1. Create anonymous pipe to be STDOUT for child.\r
9081    *     2. Create a noninheritable duplicate of read handle,\r
9082    *         and close the inheritable read handle.\r
9083    */\r
9084 \r
9085   /* Create a pipe for the child's STDOUT. */\r
9086   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9087     return GetLastError();\r
9088   }\r
9089 \r
9090   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9091   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9092                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9093                              FALSE,     /* not inherited */\r
9094                              DUPLICATE_SAME_ACCESS);\r
9095   if (! fSuccess) {\r
9096     return GetLastError();\r
9097   }\r
9098   CloseHandle(hChildStdoutRd);\r
9099 \r
9100   /*\r
9101    * The steps for redirecting child's STDIN:\r
9102    *     1. Create anonymous pipe to be STDIN for child.\r
9103    *     2. Create a noninheritable duplicate of write handle,\r
9104    *         and close the inheritable write handle.\r
9105    */\r
9106 \r
9107   /* Create a pipe for the child's STDIN. */\r
9108   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9109     return GetLastError();\r
9110   }\r
9111 \r
9112   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9113   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9114                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9115                              FALSE,     /* not inherited */\r
9116                              DUPLICATE_SAME_ACCESS);\r
9117   if (! fSuccess) {\r
9118     return GetLastError();\r
9119   }\r
9120   CloseHandle(hChildStdinWr);\r
9121 \r
9122   /* Arrange to (1) look in dir for the child .exe file, and\r
9123    * (2) have dir be the child's working directory.  Interpret\r
9124    * dir relative to the directory WinBoard loaded from. */\r
9125   GetCurrentDirectory(MSG_SIZ, buf);\r
9126   SetCurrentDirectory(installDir);\r
9127   SetCurrentDirectory(dir);\r
9128 \r
9129   /* Now create the child process. */\r
9130 \r
9131   siStartInfo.cb = sizeof(STARTUPINFO);\r
9132   siStartInfo.lpReserved = NULL;\r
9133   siStartInfo.lpDesktop = NULL;\r
9134   siStartInfo.lpTitle = NULL;\r
9135   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9136   siStartInfo.cbReserved2 = 0;\r
9137   siStartInfo.lpReserved2 = NULL;\r
9138   siStartInfo.hStdInput = hChildStdinRd;\r
9139   siStartInfo.hStdOutput = hChildStdoutWr;\r
9140   siStartInfo.hStdError = hChildStdoutWr;\r
9141 \r
9142   fSuccess = CreateProcess(NULL,\r
9143                            cmdLine,        /* command line */\r
9144                            NULL,           /* process security attributes */\r
9145                            NULL,           /* primary thread security attrs */\r
9146                            TRUE,           /* handles are inherited */\r
9147                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9148                            NULL,           /* use parent's environment */\r
9149                            NULL,\r
9150                            &siStartInfo, /* STARTUPINFO pointer */\r
9151                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9152 \r
9153   err = GetLastError();\r
9154   SetCurrentDirectory(buf); /* return to prev directory */\r
9155   if (! fSuccess) {\r
9156     return err;\r
9157   }\r
9158 \r
9159   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9160     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9161     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9162   }\r
9163 \r
9164   /* Close the handles we don't need in the parent */\r
9165   CloseHandle(piProcInfo.hThread);\r
9166   CloseHandle(hChildStdinRd);\r
9167   CloseHandle(hChildStdoutWr);\r
9168 \r
9169   /* Prepare return value */\r
9170   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9171   cp->kind = CPReal;\r
9172   cp->hProcess = piProcInfo.hProcess;\r
9173   cp->pid = piProcInfo.dwProcessId;\r
9174   cp->hFrom = hChildStdoutRdDup;\r
9175   cp->hTo = hChildStdinWrDup;\r
9176 \r
9177   *pr = (void *) cp;\r
9178 \r
9179   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9180      2000 where engines sometimes don't see the initial command(s)\r
9181      from WinBoard and hang.  I don't understand how that can happen,\r
9182      but the Sleep is harmless, so I've put it in.  Others have also\r
9183      reported what may be the same problem, so hopefully this will fix\r
9184      it for them too.  */\r
9185   Sleep(500);\r
9186 \r
9187   return NO_ERROR;\r
9188 }\r
9189 \r
9190 \r
9191 void\r
9192 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9193 {\r
9194   ChildProc *cp; int result;\r
9195 \r
9196   cp = (ChildProc *) pr;\r
9197   if (cp == NULL) return;\r
9198 \r
9199   switch (cp->kind) {\r
9200   case CPReal:\r
9201     /* TerminateProcess is considered harmful, so... */\r
9202     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9203     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9204     /* The following doesn't work because the chess program\r
9205        doesn't "have the same console" as WinBoard.  Maybe\r
9206        we could arrange for this even though neither WinBoard\r
9207        nor the chess program uses a console for stdio? */\r
9208     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9209 \r
9210     /* [AS] Special termination modes for misbehaving programs... */\r
9211     if( signal == 9 ) { \r
9212         result = TerminateProcess( cp->hProcess, 0 );\r
9213 \r
9214         if ( appData.debugMode) {\r
9215             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9216         }\r
9217     }\r
9218     else if( signal == 10 ) {\r
9219         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9220 \r
9221         if( dw != WAIT_OBJECT_0 ) {\r
9222             result = TerminateProcess( cp->hProcess, 0 );\r
9223 \r
9224             if ( appData.debugMode) {\r
9225                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9226             }\r
9227 \r
9228         }\r
9229     }\r
9230 \r
9231     CloseHandle(cp->hProcess);\r
9232     break;\r
9233 \r
9234   case CPComm:\r
9235     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9236     break;\r
9237 \r
9238   case CPSock:\r
9239     closesocket(cp->sock);\r
9240     WSACleanup();\r
9241     break;\r
9242 \r
9243   case CPRcmd:\r
9244     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9245     closesocket(cp->sock);\r
9246     closesocket(cp->sock2);\r
9247     WSACleanup();\r
9248     break;\r
9249   }\r
9250   free(cp);\r
9251 }\r
9252 \r
9253 void\r
9254 InterruptChildProcess(ProcRef pr)\r
9255 {\r
9256   ChildProc *cp;\r
9257 \r
9258   cp = (ChildProc *) pr;\r
9259   if (cp == NULL) return;\r
9260   switch (cp->kind) {\r
9261   case CPReal:\r
9262     /* The following doesn't work because the chess program\r
9263        doesn't "have the same console" as WinBoard.  Maybe\r
9264        we could arrange for this even though neither WinBoard\r
9265        nor the chess program uses a console for stdio */\r
9266     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9267     break;\r
9268 \r
9269   case CPComm:\r
9270   case CPSock:\r
9271     /* Can't interrupt */\r
9272     break;\r
9273 \r
9274   case CPRcmd:\r
9275     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9276     break;\r
9277   }\r
9278 }\r
9279 \r
9280 \r
9281 int\r
9282 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9283 {\r
9284   char cmdLine[MSG_SIZ];\r
9285 \r
9286   if (port[0] == NULLCHAR) {\r
9287     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9288   } else {\r
9289     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9290   }\r
9291   return StartChildProcess(cmdLine, "", pr);\r
9292 }\r
9293 \r
9294 \r
9295 /* Code to open TCP sockets */\r
9296 \r
9297 int\r
9298 OpenTCP(char *host, char *port, ProcRef *pr)\r
9299 {\r
9300   ChildProc *cp;\r
9301   int err;\r
9302   SOCKET s;\r
9303 \r
9304   struct sockaddr_in sa, mysa;\r
9305   struct hostent FAR *hp;\r
9306   unsigned short uport;\r
9307   WORD wVersionRequested;\r
9308   WSADATA wsaData;\r
9309 \r
9310   /* Initialize socket DLL */\r
9311   wVersionRequested = MAKEWORD(1, 1);\r
9312   err = WSAStartup(wVersionRequested, &wsaData);\r
9313   if (err != 0) return err;\r
9314 \r
9315   /* Make socket */\r
9316   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9317     err = WSAGetLastError();\r
9318     WSACleanup();\r
9319     return err;\r
9320   }\r
9321 \r
9322   /* Bind local address using (mostly) don't-care values.\r
9323    */\r
9324   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9325   mysa.sin_family = AF_INET;\r
9326   mysa.sin_addr.s_addr = INADDR_ANY;\r
9327   uport = (unsigned short) 0;\r
9328   mysa.sin_port = htons(uport);\r
9329   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9330       == SOCKET_ERROR) {\r
9331     err = WSAGetLastError();\r
9332     WSACleanup();\r
9333     return err;\r
9334   }\r
9335 \r
9336   /* Resolve remote host name */\r
9337   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9338   if (!(hp = gethostbyname(host))) {\r
9339     unsigned int b0, b1, b2, b3;\r
9340 \r
9341     err = WSAGetLastError();\r
9342 \r
9343     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9344       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9345       hp->h_addrtype = AF_INET;\r
9346       hp->h_length = 4;\r
9347       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9348       hp->h_addr_list[0] = (char *) malloc(4);\r
9349       hp->h_addr_list[0][0] = (char) b0;\r
9350       hp->h_addr_list[0][1] = (char) b1;\r
9351       hp->h_addr_list[0][2] = (char) b2;\r
9352       hp->h_addr_list[0][3] = (char) b3;\r
9353     } else {\r
9354       WSACleanup();\r
9355       return err;\r
9356     }\r
9357   }\r
9358   sa.sin_family = hp->h_addrtype;\r
9359   uport = (unsigned short) atoi(port);\r
9360   sa.sin_port = htons(uport);\r
9361   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9362 \r
9363   /* Make connection */\r
9364   if (connect(s, (struct sockaddr *) &sa,\r
9365               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9366     err = WSAGetLastError();\r
9367     WSACleanup();\r
9368     return err;\r
9369   }\r
9370 \r
9371   /* Prepare return value */\r
9372   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9373   cp->kind = CPSock;\r
9374   cp->sock = s;\r
9375   *pr = (ProcRef *) cp;\r
9376 \r
9377   return NO_ERROR;\r
9378 }\r
9379 \r
9380 int\r
9381 OpenCommPort(char *name, ProcRef *pr)\r
9382 {\r
9383   HANDLE h;\r
9384   COMMTIMEOUTS ct;\r
9385   ChildProc *cp;\r
9386   char fullname[MSG_SIZ];\r
9387 \r
9388   if (*name != '\\')\r
9389     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9390   else\r
9391     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9392 \r
9393   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9394                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9395   if (h == (HANDLE) -1) {\r
9396     return GetLastError();\r
9397   }\r
9398   hCommPort = h;\r
9399 \r
9400   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9401 \r
9402   /* Accumulate characters until a 100ms pause, then parse */\r
9403   ct.ReadIntervalTimeout = 100;\r
9404   ct.ReadTotalTimeoutMultiplier = 0;\r
9405   ct.ReadTotalTimeoutConstant = 0;\r
9406   ct.WriteTotalTimeoutMultiplier = 0;\r
9407   ct.WriteTotalTimeoutConstant = 0;\r
9408   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9409 \r
9410   /* Prepare return value */\r
9411   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9412   cp->kind = CPComm;\r
9413   cp->hFrom = h;\r
9414   cp->hTo = h;\r
9415   *pr = (ProcRef *) cp;\r
9416 \r
9417   return NO_ERROR;\r
9418 }\r
9419 \r
9420 int\r
9421 OpenLoopback(ProcRef *pr)\r
9422 {\r
9423   DisplayFatalError(_("Not implemented"), 0, 1);\r
9424   return NO_ERROR;\r
9425 }\r
9426 \r
9427 \r
9428 int\r
9429 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9430 {\r
9431   ChildProc *cp;\r
9432   int err;\r
9433   SOCKET s, s2, s3;\r
9434   struct sockaddr_in sa, mysa;\r
9435   struct hostent FAR *hp;\r
9436   unsigned short uport;\r
9437   WORD wVersionRequested;\r
9438   WSADATA wsaData;\r
9439   int fromPort;\r
9440   char stderrPortStr[MSG_SIZ];\r
9441 \r
9442   /* Initialize socket DLL */\r
9443   wVersionRequested = MAKEWORD(1, 1);\r
9444   err = WSAStartup(wVersionRequested, &wsaData);\r
9445   if (err != 0) return err;\r
9446 \r
9447   /* Resolve remote host name */\r
9448   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9449   if (!(hp = gethostbyname(host))) {\r
9450     unsigned int b0, b1, b2, b3;\r
9451 \r
9452     err = WSAGetLastError();\r
9453 \r
9454     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9455       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9456       hp->h_addrtype = AF_INET;\r
9457       hp->h_length = 4;\r
9458       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9459       hp->h_addr_list[0] = (char *) malloc(4);\r
9460       hp->h_addr_list[0][0] = (char) b0;\r
9461       hp->h_addr_list[0][1] = (char) b1;\r
9462       hp->h_addr_list[0][2] = (char) b2;\r
9463       hp->h_addr_list[0][3] = (char) b3;\r
9464     } else {\r
9465       WSACleanup();\r
9466       return err;\r
9467     }\r
9468   }\r
9469   sa.sin_family = hp->h_addrtype;\r
9470   uport = (unsigned short) 514;\r
9471   sa.sin_port = htons(uport);\r
9472   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9473 \r
9474   /* Bind local socket to unused "privileged" port address\r
9475    */\r
9476   s = INVALID_SOCKET;\r
9477   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9478   mysa.sin_family = AF_INET;\r
9479   mysa.sin_addr.s_addr = INADDR_ANY;\r
9480   for (fromPort = 1023;; fromPort--) {\r
9481     if (fromPort < 0) {\r
9482       WSACleanup();\r
9483       return WSAEADDRINUSE;\r
9484     }\r
9485     if (s == INVALID_SOCKET) {\r
9486       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9487         err = WSAGetLastError();\r
9488         WSACleanup();\r
9489         return err;\r
9490       }\r
9491     }\r
9492     uport = (unsigned short) fromPort;\r
9493     mysa.sin_port = htons(uport);\r
9494     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9495         == SOCKET_ERROR) {\r
9496       err = WSAGetLastError();\r
9497       if (err == WSAEADDRINUSE) continue;\r
9498       WSACleanup();\r
9499       return err;\r
9500     }\r
9501     if (connect(s, (struct sockaddr *) &sa,\r
9502       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9503       err = WSAGetLastError();\r
9504       if (err == WSAEADDRINUSE) {\r
9505         closesocket(s);\r
9506         s = -1;\r
9507         continue;\r
9508       }\r
9509       WSACleanup();\r
9510       return err;\r
9511     }\r
9512     break;\r
9513   }\r
9514 \r
9515   /* Bind stderr local socket to unused "privileged" port address\r
9516    */\r
9517   s2 = INVALID_SOCKET;\r
9518   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9519   mysa.sin_family = AF_INET;\r
9520   mysa.sin_addr.s_addr = INADDR_ANY;\r
9521   for (fromPort = 1023;; fromPort--) {\r
9522     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9523     if (fromPort < 0) {\r
9524       (void) closesocket(s);\r
9525       WSACleanup();\r
9526       return WSAEADDRINUSE;\r
9527     }\r
9528     if (s2 == INVALID_SOCKET) {\r
9529       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9530         err = WSAGetLastError();\r
9531         closesocket(s);\r
9532         WSACleanup();\r
9533         return err;\r
9534       }\r
9535     }\r
9536     uport = (unsigned short) fromPort;\r
9537     mysa.sin_port = htons(uport);\r
9538     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9539         == SOCKET_ERROR) {\r
9540       err = WSAGetLastError();\r
9541       if (err == WSAEADDRINUSE) continue;\r
9542       (void) closesocket(s);\r
9543       WSACleanup();\r
9544       return err;\r
9545     }\r
9546     if (listen(s2, 1) == SOCKET_ERROR) {\r
9547       err = WSAGetLastError();\r
9548       if (err == WSAEADDRINUSE) {\r
9549         closesocket(s2);\r
9550         s2 = INVALID_SOCKET;\r
9551         continue;\r
9552       }\r
9553       (void) closesocket(s);\r
9554       (void) closesocket(s2);\r
9555       WSACleanup();\r
9556       return err;\r
9557     }\r
9558     break;\r
9559   }\r
9560   prevStderrPort = fromPort; // remember port used\r
9561   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9562 \r
9563   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9564     err = WSAGetLastError();\r
9565     (void) closesocket(s);\r
9566     (void) closesocket(s2);\r
9567     WSACleanup();\r
9568     return err;\r
9569   }\r
9570 \r
9571   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9572     err = WSAGetLastError();\r
9573     (void) closesocket(s);\r
9574     (void) closesocket(s2);\r
9575     WSACleanup();\r
9576     return err;\r
9577   }\r
9578   if (*user == NULLCHAR) user = UserName();\r
9579   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9580     err = WSAGetLastError();\r
9581     (void) closesocket(s);\r
9582     (void) closesocket(s2);\r
9583     WSACleanup();\r
9584     return err;\r
9585   }\r
9586   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9587     err = WSAGetLastError();\r
9588     (void) closesocket(s);\r
9589     (void) closesocket(s2);\r
9590     WSACleanup();\r
9591     return err;\r
9592   }\r
9593 \r
9594   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9595     err = WSAGetLastError();\r
9596     (void) closesocket(s);\r
9597     (void) closesocket(s2);\r
9598     WSACleanup();\r
9599     return err;\r
9600   }\r
9601   (void) closesocket(s2);  /* Stop listening */\r
9602 \r
9603   /* Prepare return value */\r
9604   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9605   cp->kind = CPRcmd;\r
9606   cp->sock = s;\r
9607   cp->sock2 = s3;\r
9608   *pr = (ProcRef *) cp;\r
9609 \r
9610   return NO_ERROR;\r
9611 }\r
9612 \r
9613 \r
9614 InputSourceRef\r
9615 AddInputSource(ProcRef pr, int lineByLine,\r
9616                InputCallback func, VOIDSTAR closure)\r
9617 {\r
9618   InputSource *is, *is2 = NULL;\r
9619   ChildProc *cp = (ChildProc *) pr;\r
9620 \r
9621   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9622   is->lineByLine = lineByLine;\r
9623   is->func = func;\r
9624   is->closure = closure;\r
9625   is->second = NULL;\r
9626   is->next = is->buf;\r
9627   if (pr == NoProc) {\r
9628     is->kind = CPReal;\r
9629     consoleInputSource = is;\r
9630   } else {\r
9631     is->kind = cp->kind;\r
9632     /* \r
9633         [AS] Try to avoid a race condition if the thread is given control too early:\r
9634         we create all threads suspended so that the is->hThread variable can be\r
9635         safely assigned, then let the threads start with ResumeThread.\r
9636     */\r
9637     switch (cp->kind) {\r
9638     case CPReal:\r
9639       is->hFile = cp->hFrom;\r
9640       cp->hFrom = NULL; /* now owned by InputThread */\r
9641       is->hThread =\r
9642         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9643                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9644       break;\r
9645 \r
9646     case CPComm:\r
9647       is->hFile = cp->hFrom;\r
9648       cp->hFrom = NULL; /* now owned by InputThread */\r
9649       is->hThread =\r
9650         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9651                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9652       break;\r
9653 \r
9654     case CPSock:\r
9655       is->sock = cp->sock;\r
9656       is->hThread =\r
9657         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9658                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9659       break;\r
9660 \r
9661     case CPRcmd:\r
9662       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9663       *is2 = *is;\r
9664       is->sock = cp->sock;\r
9665       is->second = is2;\r
9666       is2->sock = cp->sock2;\r
9667       is2->second = is2;\r
9668       is->hThread =\r
9669         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9670                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9671       is2->hThread =\r
9672         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9673                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9674       break;\r
9675     }\r
9676 \r
9677     if( is->hThread != NULL ) {\r
9678         ResumeThread( is->hThread );\r
9679     }\r
9680 \r
9681     if( is2 != NULL && is2->hThread != NULL ) {\r
9682         ResumeThread( is2->hThread );\r
9683     }\r
9684   }\r
9685 \r
9686   return (InputSourceRef) is;\r
9687 }\r
9688 \r
9689 void\r
9690 RemoveInputSource(InputSourceRef isr)\r
9691 {\r
9692   InputSource *is;\r
9693 \r
9694   is = (InputSource *) isr;\r
9695   is->hThread = NULL;  /* tell thread to stop */\r
9696   CloseHandle(is->hThread);\r
9697   if (is->second != NULL) {\r
9698     is->second->hThread = NULL;\r
9699     CloseHandle(is->second->hThread);\r
9700   }\r
9701 }\r
9702 \r
9703 int no_wrap(char *message, int count)\r
9704 {\r
9705     ConsoleOutput(message, count, FALSE);\r
9706     return count;\r
9707 }\r
9708 \r
9709 int\r
9710 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9711 {\r
9712   DWORD dOutCount;\r
9713   int outCount = SOCKET_ERROR;\r
9714   ChildProc *cp = (ChildProc *) pr;\r
9715   static OVERLAPPED ovl;\r
9716   static int line = 0;\r
9717 \r
9718   if (pr == NoProc)\r
9719   {\r
9720     if (appData.noJoin || !appData.useInternalWrap)\r
9721       return no_wrap(message, count);\r
9722     else\r
9723     {\r
9724       int width = get_term_width();\r
9725       int len = wrap(NULL, message, count, width, &line);\r
9726       char *msg = malloc(len);\r
9727       int dbgchk;\r
9728 \r
9729       if (!msg)\r
9730         return no_wrap(message, count);\r
9731       else\r
9732       {\r
9733         dbgchk = wrap(msg, message, count, width, &line);\r
9734         if (dbgchk != len && appData.debugMode)\r
9735             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9736         ConsoleOutput(msg, len, FALSE);\r
9737         free(msg);\r
9738         return len;\r
9739       }\r
9740     }\r
9741   }\r
9742 \r
9743   if (ovl.hEvent == NULL) {\r
9744     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9745   }\r
9746   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9747 \r
9748   switch (cp->kind) {\r
9749   case CPSock:\r
9750   case CPRcmd:\r
9751     outCount = send(cp->sock, message, count, 0);\r
9752     if (outCount == SOCKET_ERROR) {\r
9753       *outError = WSAGetLastError();\r
9754     } else {\r
9755       *outError = NO_ERROR;\r
9756     }\r
9757     break;\r
9758 \r
9759   case CPReal:\r
9760     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9761                   &dOutCount, NULL)) {\r
9762       *outError = NO_ERROR;\r
9763       outCount = (int) dOutCount;\r
9764     } else {\r
9765       *outError = GetLastError();\r
9766     }\r
9767     break;\r
9768 \r
9769   case CPComm:\r
9770     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9771                             &dOutCount, &ovl);\r
9772     if (*outError == NO_ERROR) {\r
9773       outCount = (int) dOutCount;\r
9774     }\r
9775     break;\r
9776   }\r
9777   return outCount;\r
9778 }\r
9779 \r
9780 void\r
9781 DoSleep(int n)\r
9782 {\r
9783     if(n != 0) Sleep(n);\r
9784 }\r
9785 \r
9786 int\r
9787 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9788                        long msdelay)\r
9789 {\r
9790   /* Ignore delay, not implemented for WinBoard */\r
9791   return OutputToProcess(pr, message, count, outError);\r
9792 }\r
9793 \r
9794 \r
9795 void\r
9796 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9797                         char *buf, int count, int error)\r
9798 {\r
9799   DisplayFatalError(_("Not implemented"), 0, 1);\r
9800 }\r
9801 \r
9802 /* see wgamelist.c for Game List functions */\r
9803 /* see wedittags.c for Edit Tags functions */\r
9804 \r
9805 \r
9806 int\r
9807 ICSInitScript()\r
9808 {\r
9809   FILE *f;\r
9810   char buf[MSG_SIZ];\r
9811   char *dummy;\r
9812 \r
9813   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9814     f = fopen(buf, "r");\r
9815     if (f != NULL) {\r
9816       ProcessICSInitScript(f);\r
9817       fclose(f);\r
9818       return TRUE;\r
9819     }\r
9820   }\r
9821   return FALSE;\r
9822 }\r
9823 \r
9824 \r
9825 VOID\r
9826 StartAnalysisClock()\r
9827 {\r
9828   if (analysisTimerEvent) return;\r
9829   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9830                                         (UINT) 2000, NULL);\r
9831 }\r
9832 \r
9833 VOID\r
9834 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9835 {\r
9836   highlightInfo.sq[0].x = fromX;\r
9837   highlightInfo.sq[0].y = fromY;\r
9838   highlightInfo.sq[1].x = toX;\r
9839   highlightInfo.sq[1].y = toY;\r
9840 }\r
9841 \r
9842 VOID\r
9843 ClearHighlights()\r
9844 {\r
9845   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9846     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9847 }\r
9848 \r
9849 VOID\r
9850 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9851 {\r
9852   premoveHighlightInfo.sq[0].x = fromX;\r
9853   premoveHighlightInfo.sq[0].y = fromY;\r
9854   premoveHighlightInfo.sq[1].x = toX;\r
9855   premoveHighlightInfo.sq[1].y = toY;\r
9856 }\r
9857 \r
9858 VOID\r
9859 ClearPremoveHighlights()\r
9860 {\r
9861   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9862     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9863 }\r
9864 \r
9865 VOID\r
9866 ShutDownFrontEnd()\r
9867 {\r
9868   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9869   DeleteClipboardTempFiles();\r
9870 }\r
9871 \r
9872 void\r
9873 BoardToTop()\r
9874 {\r
9875     if (IsIconic(hwndMain))\r
9876       ShowWindow(hwndMain, SW_RESTORE);\r
9877 \r
9878     SetActiveWindow(hwndMain);\r
9879 }\r
9880 \r
9881 /*\r
9882  * Prototypes for animation support routines\r
9883  */\r
9884 static void ScreenSquare(int column, int row, POINT * pt);\r
9885 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9886      POINT frames[], int * nFrames);\r
9887 \r
9888 \r
9889 #define kFactor 4\r
9890 \r
9891 void\r
9892 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9893 {       // [HGM] atomic: animate blast wave\r
9894         int i;\r
9895 \r
9896         explodeInfo.fromX = fromX;\r
9897         explodeInfo.fromY = fromY;\r
9898         explodeInfo.toX = toX;\r
9899         explodeInfo.toY = toY;\r
9900         for(i=1; i<4*kFactor; i++) {\r
9901             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9902             DrawPosition(FALSE, board);\r
9903             Sleep(appData.animSpeed);\r
9904         }\r
9905         explodeInfo.radius = 0;\r
9906         DrawPosition(TRUE, board);\r
9907 }\r
9908 \r
9909 void\r
9910 AnimateMove(board, fromX, fromY, toX, toY)\r
9911      Board board;\r
9912      int fromX;\r
9913      int fromY;\r
9914      int toX;\r
9915      int toY;\r
9916 {\r
9917   ChessSquare piece;\r
9918   POINT start, finish, mid;\r
9919   POINT frames[kFactor * 2 + 1];\r
9920   int nFrames, n;\r
9921 \r
9922   if (!appData.animate) return;\r
9923   if (doingSizing) return;\r
9924   if (fromY < 0 || fromX < 0) return;\r
9925   piece = board[fromY][fromX];\r
9926   if (piece >= EmptySquare) return;\r
9927 \r
9928   ScreenSquare(fromX, fromY, &start);\r
9929   ScreenSquare(toX, toY, &finish);\r
9930 \r
9931   /* All moves except knight jumps move in straight line */\r
9932   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9933     mid.x = start.x + (finish.x - start.x) / 2;\r
9934     mid.y = start.y + (finish.y - start.y) / 2;\r
9935   } else {\r
9936     /* Knight: make straight movement then diagonal */\r
9937     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9938        mid.x = start.x + (finish.x - start.x) / 2;\r
9939        mid.y = start.y;\r
9940      } else {\r
9941        mid.x = start.x;\r
9942        mid.y = start.y + (finish.y - start.y) / 2;\r
9943      }\r
9944   }\r
9945   \r
9946   /* Don't use as many frames for very short moves */\r
9947   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9948     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9949   else\r
9950     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9951 \r
9952   animInfo.from.x = fromX;\r
9953   animInfo.from.y = fromY;\r
9954   animInfo.to.x = toX;\r
9955   animInfo.to.y = toY;\r
9956   animInfo.lastpos = start;\r
9957   animInfo.piece = piece;\r
9958   for (n = 0; n < nFrames; n++) {\r
9959     animInfo.pos = frames[n];\r
9960     DrawPosition(FALSE, NULL);\r
9961     animInfo.lastpos = animInfo.pos;\r
9962     Sleep(appData.animSpeed);\r
9963   }\r
9964   animInfo.pos = finish;\r
9965   DrawPosition(FALSE, NULL);\r
9966   animInfo.piece = EmptySquare;\r
9967   Explode(board, fromX, fromY, toX, toY);\r
9968 }\r
9969 \r
9970 /*      Convert board position to corner of screen rect and color       */\r
9971 \r
9972 static void\r
9973 ScreenSquare(column, row, pt)\r
9974      int column; int row; POINT * pt;\r
9975 {\r
9976   if (flipView) {\r
9977     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
9978     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
9979   } else {\r
9980     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
9981     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
9982   }\r
9983 }\r
9984 \r
9985 /*      Generate a series of frame coords from start->mid->finish.\r
9986         The movement rate doubles until the half way point is\r
9987         reached, then halves back down to the final destination,\r
9988         which gives a nice slow in/out effect. The algorithmn\r
9989         may seem to generate too many intermediates for short\r
9990         moves, but remember that the purpose is to attract the\r
9991         viewers attention to the piece about to be moved and\r
9992         then to where it ends up. Too few frames would be less\r
9993         noticeable.                                             */\r
9994 \r
9995 static void\r
9996 Tween(start, mid, finish, factor, frames, nFrames)\r
9997      POINT * start; POINT * mid;\r
9998      POINT * finish; int factor;\r
9999      POINT frames[]; int * nFrames;\r
10000 {\r
10001   int n, fraction = 1, count = 0;\r
10002 \r
10003   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10004   for (n = 0; n < factor; n++)\r
10005     fraction *= 2;\r
10006   for (n = 0; n < factor; n++) {\r
10007     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10008     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10009     count ++;\r
10010     fraction = fraction / 2;\r
10011   }\r
10012   \r
10013   /* Midpoint */\r
10014   frames[count] = *mid;\r
10015   count ++;\r
10016   \r
10017   /* Slow out, stepping 1/2, then 1/4, ... */\r
10018   fraction = 2;\r
10019   for (n = 0; n < factor; n++) {\r
10020     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10021     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10022     count ++;\r
10023     fraction = fraction * 2;\r
10024   }\r
10025   *nFrames = count;\r
10026 }\r
10027 \r
10028 void\r
10029 SettingsPopUp(ChessProgramState *cps)\r
10030 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10031       EngineOptionsPopup(savedHwnd, cps);\r
10032 }\r
10033 \r
10034 int flock(int fid, int code)\r
10035 {\r
10036     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10037     OVERLAPPED ov;\r
10038     ov.hEvent = NULL;\r
10039     ov.Offset = 0;\r
10040     ov.OffsetHigh = 0;\r
10041     switch(code) {\r
10042       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10043       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10044       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10045       default: return -1;\r
10046     }\r
10047     return 0;\r
10048 }\r
10049 \r
10050 char *\r
10051 Col2Text (int n)\r
10052 {\r
10053     static int i=0;\r
10054     static char col[8][20];\r
10055     COLORREF color = *(COLORREF *) colorVariable[n];\r
10056     i = i+1 & 7;\r
10057     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10058     return col[i];\r
10059 }\r
10060 \r
10061 void\r
10062 ActivateTheme (int new)\r
10063 {   // Redo initialization of features depending on options that can occur in themes\r
10064    InitTextures();\r
10065    if(new) InitDrawingColors();\r
10066    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10067    InitDrawingSizes(boardSize, 0);\r
10068    InvalidateRect(hwndMain, NULL, TRUE);\r
10069 }\r