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