Add final piece count to search criteria
[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, 2014 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,\r
264   OPT_Ranget, IDOK, IDCANCEL }, \r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
266   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
269   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
272   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
273 { ABOUTBOX2, IDC_ChessBoard }, \r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
275   OPT_GameListClose, IDC_GameListDoFilter }, \r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
277 { DLG_Error, IDOK }, \r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
279   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
282   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
283   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
284 { DLG_IndexNumber, IDC_Index }, \r
285 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
286 { DLG_TypeInName, IDOK, IDCANCEL }, \r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
288   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
290   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
291   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
292   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
293   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
294   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
295   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
297   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
298   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
299   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
300   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
301   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
302   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
303   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
304   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
306   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
307   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
308   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
309   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
310   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
311   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
312   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
314   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
315   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
316   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
317   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
318   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
319   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
320   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
321   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
323   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
324   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
325   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
326   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
329   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
330 { DLG_MoveHistory }, \r
331 { DLG_EvalGraph }, \r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
335   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
336   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
337   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
339   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
340   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
341 { 0 }\r
342 };\r
343 \r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
345 static int lastChecked;\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
347 extern int tinyLayout;\r
348 extern char * menuBarText[][10];\r
349 \r
350 void\r
351 LoadLanguageFile(char *name)\r
352 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
353     FILE *f;\r
354     int i=0, j=0, n=0, k;\r
355     char buf[MSG_SIZ];\r
356 \r
357     if(!name || name[0] == NULLCHAR) return;\r
358       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
359     appData.language = oldLanguage;\r
360     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
361     if((f = fopen(buf, "r")) == NULL) return;\r
362     while((k = fgetc(f)) != EOF) {\r
363         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
364         languageBuf[i] = k;\r
365         if(k == '\n') {\r
366             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
367                 char *p;\r
368                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
369                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
370                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
371                         english[j] = languageBuf + n + 1; *p = 0;\r
372                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
374                     }\r
375                 }\r
376             }\r
377             n = i + 1;\r
378         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
379             switch(k) {\r
380               case 'n': k = '\n'; break;\r
381               case 'r': k = '\r'; break;\r
382               case 't': k = '\t'; break;\r
383             }\r
384             languageBuf[--i] = k;\r
385         }\r
386         i++;\r
387     }\r
388     fclose(f);\r
389     barbaric = (j != 0);\r
390     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
391 }\r
392 \r
393 char *\r
394 T_(char *s)\r
395 {   // return the translation of the given string\r
396     // efficiency can be improved a lot...\r
397     int i=0;\r
398     static char buf[MSG_SIZ];\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
400     if(!barbaric) return s;\r
401     if(!s) return ""; // sanity\r
402     while(english[i]) {\r
403         if(!strcmp(s, english[i])) return foreign[i];\r
404         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
405             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
406             return buf;\r
407         }\r
408         i++;\r
409     }\r
410     return s;\r
411 }\r
412 \r
413 void\r
414 Translate(HWND hDlg, int dialogID)\r
415 {   // translate all text items in the given dialog\r
416     int i=0, j, k;\r
417     char buf[MSG_SIZ], *s;\r
418     if(!barbaric) return;\r
419     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
420     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
421     GetWindowText( hDlg, buf, MSG_SIZ );\r
422     s = T_(buf);\r
423     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
424     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
425         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
426         if(strlen(buf) == 0) continue;\r
427         s = T_(buf);\r
428         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
429     }\r
430 }\r
431 \r
432 HMENU\r
433 TranslateOneMenu(int i, HMENU subMenu)\r
434 {\r
435     int j;\r
436     static MENUITEMINFO info;\r
437 \r
438     info.cbSize = sizeof(MENUITEMINFO);\r
439     info.fMask = MIIM_STATE | MIIM_TYPE;\r
440           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
441             char buf[MSG_SIZ];\r
442             info.dwTypeData = buf;\r
443             info.cch = sizeof(buf);\r
444             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
445             if(i < 10) {\r
446                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
447                 else menuText[i][j] = strdup(buf); // remember original on first change\r
448             }\r
449             if(buf[0] == NULLCHAR) continue;\r
450             info.dwTypeData = T_(buf);\r
451             info.cch = strlen(buf)+1;\r
452             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
453           }\r
454     return subMenu;\r
455 }\r
456 \r
457 void\r
458 TranslateMenus(int addLanguage)\r
459 {\r
460     int i;\r
461     WIN32_FIND_DATA fileData;\r
462     HANDLE hFind;\r
463 #define IDM_English 1970\r
464     if(1) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
467           HMENU subMenu = GetSubMenu(mainMenu, i);\r
468           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
469                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
470           TranslateOneMenu(i, subMenu);\r
471         }\r
472         DrawMenuBar(hwndMain);\r
473     }\r
474 \r
475     if(!addLanguage) return;\r
476     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
477         HMENU mainMenu = GetMenu(hwndMain);\r
478         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
479         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
480         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
481         i = 0; lastChecked = IDM_English;\r
482         do {\r
483             char *p, *q = fileData.cFileName;\r
484             int checkFlag = MF_UNCHECKED;\r
485             languageFile[i] = strdup(q);\r
486             if(barbaric && !strcmp(oldLanguage, q)) {\r
487                 checkFlag = MF_CHECKED;\r
488                 lastChecked = IDM_English + i + 1;\r
489                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
490             }\r
491             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
492             p = strstr(fileData.cFileName, ".lng");\r
493             if(p) *p = 0;\r
494             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
495         } while(FindNextFile(hFind, &fileData));\r
496         FindClose(hFind);\r
497     }\r
498 }\r
499 \r
500 #endif\r
501 \r
502 #define IDM_RecentEngines 3000\r
503 \r
504 void\r
505 RecentEngineMenu (char *s)\r
506 {\r
507     if(appData.icsActive) return;\r
508     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
509         HMENU mainMenu = GetMenu(hwndMain);\r
510         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
511         int i=IDM_RecentEngines;\r
512         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
513         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
514         while(*s) {\r
515           char *p = strchr(s, '\n');\r
516           if(p == NULL) return; // malformed!\r
517           *p = NULLCHAR;\r
518           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
519           *p = '\n';\r
520           s = p+1;\r
521         }\r
522     }\r
523 }\r
524 \r
525 \r
526 typedef struct {\r
527   char *name;\r
528   int squareSize;\r
529   int lineGap;\r
530   int smallLayout;\r
531   int tinyLayout;\r
532   int cliWidth, cliHeight;\r
533 } SizeInfo;\r
534 \r
535 SizeInfo sizeInfo[] = \r
536 {\r
537   { "tiny",     21, 0, 1, 1, 0, 0 },\r
538   { "teeny",    25, 1, 1, 1, 0, 0 },\r
539   { "dinky",    29, 1, 1, 1, 0, 0 },\r
540   { "petite",   33, 1, 1, 1, 0, 0 },\r
541   { "slim",     37, 2, 1, 0, 0, 0 },\r
542   { "small",    40, 2, 1, 0, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576   { 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
577   { 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
578   { 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
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
607 };\r
608 \r
609 \r
610 MySound sounds[(int)NSoundClasses];\r
611 MyTextAttribs textAttribs[(int)NColorClasses];\r
612 \r
613 MyColorizeAttribs colorizeAttribs[] = {\r
614   { (COLORREF)0, 0, N_("Shout Text") },\r
615   { (COLORREF)0, 0, N_("SShout/CShout") },\r
616   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
617   { (COLORREF)0, 0, N_("Channel Text") },\r
618   { (COLORREF)0, 0, N_("Kibitz Text") },\r
619   { (COLORREF)0, 0, N_("Tell Text") },\r
620   { (COLORREF)0, 0, N_("Challenge Text") },\r
621   { (COLORREF)0, 0, N_("Request Text") },\r
622   { (COLORREF)0, 0, N_("Seek Text") },\r
623   { (COLORREF)0, 0, N_("Normal Text") },\r
624   { (COLORREF)0, 0, N_("None") }\r
625 };\r
626 \r
627 \r
628 \r
629 static char *commentTitle;\r
630 static char *commentText;\r
631 static int commentIndex;\r
632 static Boolean editComment = FALSE;\r
633 \r
634 \r
635 char errorTitle[MSG_SIZ];\r
636 char errorMessage[2*MSG_SIZ];\r
637 HWND errorDialog = NULL;\r
638 BOOLEAN moveErrorMessageUp = FALSE;\r
639 BOOLEAN consoleEcho = TRUE;\r
640 CHARFORMAT consoleCF;\r
641 COLORREF consoleBackgroundColor;\r
642 \r
643 char *programVersion;\r
644 \r
645 #define CPReal 1\r
646 #define CPComm 2\r
647 #define CPSock 3\r
648 #define CPRcmd 4\r
649 typedef int CPKind;\r
650 \r
651 typedef struct {\r
652   CPKind kind;\r
653   HANDLE hProcess;\r
654   DWORD pid;\r
655   HANDLE hTo;\r
656   HANDLE hFrom;\r
657   SOCKET sock;\r
658   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
659 } ChildProc;\r
660 \r
661 #define INPUT_SOURCE_BUF_SIZE 4096\r
662 \r
663 typedef struct _InputSource {\r
664   CPKind kind;\r
665   HANDLE hFile;\r
666   SOCKET sock;\r
667   int lineByLine;\r
668   HANDLE hThread;\r
669   DWORD id;\r
670   char buf[INPUT_SOURCE_BUF_SIZE];\r
671   char *next;\r
672   DWORD count;\r
673   int error;\r
674   InputCallback func;\r
675   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
676   VOIDSTAR closure;\r
677 } InputSource;\r
678 \r
679 InputSource *consoleInputSource;\r
680 \r
681 DCB dcb;\r
682 \r
683 /* forward */\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
685 VOID ConsoleCreate();\r
686 LRESULT CALLBACK\r
687   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);\r
691 LRESULT CALLBACK\r
692   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
694 void ParseIcsTextMenu(char *icsTextMenuString);\r
695 VOID PopUpNameDialog(char firstchar);\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
697 \r
698 /* [AS] */\r
699 int NewGameFRC();\r
700 int GameListOptions();\r
701 \r
702 int dummy; // [HGM] for obsolete args\r
703 \r
704 HWND hwndMain = NULL;        /* root window*/\r
705 HWND hwndConsole = NULL;\r
706 HWND commentDialog = NULL;\r
707 HWND moveHistoryDialog = NULL;\r
708 HWND evalGraphDialog = NULL;\r
709 HWND engineOutputDialog = NULL;\r
710 HWND gameListDialog = NULL;\r
711 HWND editTagsDialog = NULL;\r
712 \r
713 int commentUp = FALSE;\r
714 \r
715 WindowPlacement wpMain;\r
716 WindowPlacement wpConsole;\r
717 WindowPlacement wpComment;\r
718 WindowPlacement wpMoveHistory;\r
719 WindowPlacement wpEvalGraph;\r
720 WindowPlacement wpEngineOutput;\r
721 WindowPlacement wpGameList;\r
722 WindowPlacement wpTags;\r
723 \r
724 VOID EngineOptionsPopup(); // [HGM] settings\r
725 \r
726 VOID GothicPopUp(char *title, VariantClass variant);\r
727 /*\r
728  * Setting "frozen" should disable all user input other than deleting\r
729  * the window.  We do this while engines are initializing themselves.\r
730  */\r
731 static int frozen = 0;\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];\r
733 void FreezeUI()\r
734 {\r
735   HMENU hmenu;\r
736   int i;\r
737 \r
738   if (frozen) return;\r
739   frozen = 1;\r
740   hmenu = GetMenu(hwndMain);\r
741   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
742     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
743   }\r
744   DrawMenuBar(hwndMain);\r
745 }\r
746 \r
747 /* Undo a FreezeUI */\r
748 void ThawUI()\r
749 {\r
750   HMENU hmenu;\r
751   int i;\r
752 \r
753   if (!frozen) return;\r
754   frozen = 0;\r
755   hmenu = GetMenu(hwndMain);\r
756   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
757     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
758   }\r
759   DrawMenuBar(hwndMain);\r
760 }\r
761 \r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
763 \r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
765 #ifdef JAWS\r
766 #include "jaws.c"\r
767 #else\r
768 #define JAWS_INIT\r
769 #define JAWS_ARGS\r
770 #define JAWS_ALT_INTERCEPT\r
771 #define JAWS_KBUP_NAVIGATION\r
772 #define JAWS_KBDOWN_NAVIGATION\r
773 #define JAWS_MENU_ITEMS\r
774 #define JAWS_SILENCE\r
775 #define JAWS_REPLAY\r
776 #define JAWS_ACCEL\r
777 #define JAWS_COPYRIGHT\r
778 #define JAWS_DELETE(X) X\r
779 #define SAYMACHINEMOVE()\r
780 #define SAY(X)\r
781 #endif\r
782 \r
783 /*---------------------------------------------------------------------------*\\r
784  *\r
785  * WinMain\r
786  *\r
787 \*---------------------------------------------------------------------------*/\r
788 \r
789 int APIENTRY\r
790 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
791         LPSTR lpCmdLine, int nCmdShow)\r
792 {\r
793   MSG msg;\r
794   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
795 //  INITCOMMONCONTROLSEX ex;\r
796 \r
797   debugFP = stderr;\r
798 \r
799   LoadLibrary("RICHED32.DLL");\r
800   consoleCF.cbSize = sizeof(CHARFORMAT);\r
801 \r
802   if (!InitApplication(hInstance)) {\r
803     return (FALSE);\r
804   }\r
805   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
806     return (FALSE);\r
807   }\r
808 \r
809   JAWS_INIT\r
810   TranslateMenus(1);\r
811 \r
812 //  InitCommonControlsEx(&ex);\r
813   InitCommonControls();\r
814 \r
815   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
816   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
817   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
818 \r
819   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
820 \r
821   while (GetMessage(&msg, /* message structure */\r
822                     NULL, /* handle of window receiving the message */\r
823                     0,    /* lowest message to examine */\r
824                     0))   /* highest message to examine */\r
825     {\r
826 \r
827       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
828         // [HGM] navigate: switch between all windows with tab\r
829         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
830         int i, currentElement = 0;\r
831 \r
832         // first determine what element of the chain we come from (if any)\r
833         if(appData.icsActive) {\r
834             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
835             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
836         }\r
837         if(engineOutputDialog && EngineOutputIsUp()) {\r
838             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
839             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
840         }\r
841         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
842             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
843         }\r
844         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
845         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
846         if(msg.hwnd == e1)                 currentElement = 2; else\r
847         if(msg.hwnd == e2)                 currentElement = 3; else\r
848         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
849         if(msg.hwnd == mh)                currentElement = 4; else\r
850         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
851         if(msg.hwnd == hText)  currentElement = 5; else\r
852         if(msg.hwnd == hInput) currentElement = 6; else\r
853         for (i = 0; i < N_BUTTONS; i++) {\r
854             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
855         }\r
856 \r
857         // determine where to go to\r
858         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
859           do {\r
860             currentElement = (currentElement + direction) % 7;\r
861             switch(currentElement) {\r
862                 case 0:\r
863                   h = hwndMain; break; // passing this case always makes the loop exit\r
864                 case 1:\r
865                   h = buttonDesc[0].hwnd; break; // could be NULL\r
866                 case 2:\r
867                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
868                   h = e1; break;\r
869                 case 3:\r
870                   if(!EngineOutputIsUp()) continue;\r
871                   h = e2; break;\r
872                 case 4:\r
873                   if(!MoveHistoryIsUp()) continue;\r
874                   h = mh; break;\r
875 //              case 6: // input to eval graph does not seem to get here!\r
876 //                if(!EvalGraphIsUp()) continue;\r
877 //                h = evalGraphDialog; break;\r
878                 case 5:\r
879                   if(!appData.icsActive) continue;\r
880                   SAY("display");\r
881                   h = hText; break;\r
882                 case 6:\r
883                   if(!appData.icsActive) continue;\r
884                   SAY("input");\r
885                   h = hInput; break;\r
886             }\r
887           } while(h == 0);\r
888 \r
889           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
890           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
891           SetFocus(h);\r
892 \r
893           continue; // this message now has been processed\r
894         }\r
895       }\r
896 \r
897       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
898           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
899           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
900           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
901           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
902           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
903           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
904           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
905           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
906           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
907         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
908         for(i=0; i<MAX_CHAT; i++) \r
909             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
910                 done = 1; break;\r
911         }\r
912         if(done) continue; // [HGM] chat: end patch\r
913         TranslateMessage(&msg); /* Translates virtual key codes */\r
914         DispatchMessage(&msg);  /* Dispatches message to window */\r
915       }\r
916     }\r
917 \r
918 \r
919   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
920 }\r
921 \r
922 /*---------------------------------------------------------------------------*\\r
923  *\r
924  * Initialization functions\r
925  *\r
926 \*---------------------------------------------------------------------------*/\r
927 \r
928 void\r
929 SetUserLogo()\r
930 {   // update user logo if necessary\r
931     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
932 \r
933     if(appData.autoLogo) {\r
934           curName = UserName();\r
935           if(strcmp(curName, oldUserName)) {\r
936                 GetCurrentDirectory(MSG_SIZ, dir);\r
937                 SetCurrentDirectory(installDir);\r
938                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
939                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
940                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
941                 if(userLogo == NULL)\r
942                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
943                 SetCurrentDirectory(dir); /* return to prev directory */\r
944           }\r
945     }\r
946 }\r
947 \r
948 BOOL\r
949 InitApplication(HINSTANCE hInstance)\r
950 {\r
951   WNDCLASS wc;\r
952 \r
953   /* Fill in window class structure with parameters that describe the */\r
954   /* main window. */\r
955 \r
956   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
957   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
958   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
959   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
960   wc.hInstance     = hInstance;         /* Owner of this class */\r
961   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
962   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
963   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
964   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
965   wc.lpszClassName = szAppName;                 /* Name to register as */\r
966 \r
967   /* Register the window class and return success/failure code. */\r
968   if (!RegisterClass(&wc)) return FALSE;\r
969 \r
970   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
971   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
972   wc.cbClsExtra    = 0;\r
973   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
974   wc.hInstance     = hInstance;\r
975   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
976   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
977   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
978   wc.lpszMenuName  = NULL;\r
979   wc.lpszClassName = szConsoleName;\r
980 \r
981   if (!RegisterClass(&wc)) return FALSE;\r
982   return TRUE;\r
983 }\r
984 \r
985 \r
986 /* Set by InitInstance, used by EnsureOnScreen */\r
987 int screenHeight, screenWidth;\r
988 RECT screenGeometry;\r
989 \r
990 void\r
991 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
992 {\r
993 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
994   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
995   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
996   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
997   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
998   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
999 }\r
1000 \r
1001 VOID\r
1002 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1003 {\r
1004   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1005   GetCurrentDirectory(MSG_SIZ, dir);\r
1006   SetCurrentDirectory(installDir);\r
1007   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1008       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1009 \r
1010       if (cps->programLogo == NULL && appData.debugMode) {\r
1011           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1012       }\r
1013   } else if(appData.autoLogo) {\r
1014       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1015         char *opponent = "";\r
1016         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1017         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1018         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1019         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1020             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1021             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1022         }\r
1023       } else\r
1024       if(appData.directory[n] && appData.directory[n][0]) {\r
1025         SetCurrentDirectory(appData.directory[n]);\r
1026         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1027       }\r
1028   }\r
1029   SetCurrentDirectory(dir); /* return to prev directory */\r
1030 }\r
1031 \r
1032 VOID\r
1033 InitTextures()\r
1034 {\r
1035   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1036   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1037   \r
1038   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1039       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1040       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1041       liteBackTextureMode = appData.liteBackTextureMode;\r
1042 \r
1043       if (liteBackTexture == NULL && appData.debugMode) {\r
1044           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1045       }\r
1046   }\r
1047   \r
1048   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1049       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1050       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1051       darkBackTextureMode = appData.darkBackTextureMode;\r
1052 \r
1053       if (darkBackTexture == NULL && appData.debugMode) {\r
1054           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1055       }\r
1056   }\r
1057 }\r
1058 \r
1059 #ifndef SM_CXVIRTUALSCREEN\r
1060 #define SM_CXVIRTUALSCREEN 78\r
1061 #endif\r
1062 #ifndef SM_CYVIRTUALSCREEN\r
1063 #define SM_CYVIRTUALSCREEN 79\r
1064 #endif\r
1065 #ifndef SM_XVIRTUALSCREEN \r
1066 #define SM_XVIRTUALSCREEN 76\r
1067 #endif\r
1068 #ifndef SM_YVIRTUALSCREEN \r
1069 #define SM_YVIRTUALSCREEN 77\r
1070 #endif\r
1071 \r
1072 VOID\r
1073 InitGeometry()\r
1074 {\r
1075   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1076   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1077   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1078   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1079   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1080   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1081   screenGeometry.right = screenGeometry.left + screenWidth;\r
1082   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1083 }\r
1084 \r
1085 BOOL\r
1086 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1087 {\r
1088   HWND hwnd; /* Main window handle. */\r
1089   int ibs;\r
1090   WINDOWPLACEMENT wp;\r
1091   char *filepart;\r
1092 \r
1093   hInst = hInstance;    /* Store instance handle in our global variable */\r
1094   programName = szAppName;\r
1095 \r
1096   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1097     *filepart = NULLCHAR;\r
1098     SetCurrentDirectory(installDir);\r
1099   } else {\r
1100     GetCurrentDirectory(MSG_SIZ, installDir);\r
1101   }\r
1102   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1103   InitGeometry();\r
1104   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1105   /* xboard, and older WinBoards, controlled the move sound with the\r
1106      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1107      always turn the option on (so that the backend will call us),\r
1108      then let the user turn the sound off by setting it to silence if\r
1109      desired.  To accommodate old winboard.ini files saved by old\r
1110      versions of WinBoard, we also turn off the sound if the option\r
1111      was initially set to false. [HGM] taken out of InitAppData */\r
1112   if (!appData.ringBellAfterMoves) {\r
1113     sounds[(int)SoundMove].name = strdup("");\r
1114     appData.ringBellAfterMoves = TRUE;\r
1115   }\r
1116   if (appData.debugMode) {\r
1117     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1118     setbuf(debugFP, NULL);\r
1119   }\r
1120 \r
1121   LoadLanguageFile(appData.language);\r
1122 \r
1123   InitBackEnd1();\r
1124 \r
1125 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1126 //  InitEngineUCI( installDir, &second );\r
1127 \r
1128   /* Create a main window for this application instance. */\r
1129   hwnd = CreateWindow(szAppName, szTitle,\r
1130                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1131                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1132                       NULL, NULL, hInstance, NULL);\r
1133   hwndMain = hwnd;\r
1134 \r
1135   /* If window could not be created, return "failure" */\r
1136   if (!hwnd) {\r
1137     return (FALSE);\r
1138   }\r
1139 \r
1140   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1141   LoadLogo(&first, 0, FALSE);\r
1142   LoadLogo(&second, 1, appData.icsActive);\r
1143 \r
1144   SetUserLogo();\r
1145 \r
1146   iconWhite = LoadIcon(hInstance, "icon_white");\r
1147   iconBlack = LoadIcon(hInstance, "icon_black");\r
1148   iconCurrent = iconWhite;\r
1149   InitDrawingColors();\r
1150 \r
1151   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1152   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1153     /* Compute window size for each board size, and use the largest\r
1154        size that fits on this screen as the default. */\r
1155     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1156     if (boardSize == (BoardSize)-1 &&\r
1157         winH <= screenHeight\r
1158            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1159         && winW <= screenWidth) {\r
1160       boardSize = (BoardSize)ibs;\r
1161     }\r
1162   }\r
1163 \r
1164   InitDrawingSizes(boardSize, 0);\r
1165   RecentEngineMenu(appData.recentEngineList);\r
1166   InitMenuChecks();\r
1167   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1168 \r
1169   /* [AS] Load textures if specified */\r
1170   InitTextures();\r
1171 \r
1172   mysrandom( (unsigned) time(NULL) );\r
1173 \r
1174   /* [AS] Restore layout */\r
1175   if( wpMoveHistory.visible ) {\r
1176       MoveHistoryPopUp();\r
1177   }\r
1178 \r
1179   if( wpEvalGraph.visible ) {\r
1180       EvalGraphPopUp();\r
1181   }\r
1182 \r
1183   if( wpEngineOutput.visible ) {\r
1184       EngineOutputPopUp();\r
1185   }\r
1186 \r
1187   /* Make the window visible; update its client area; and return "success" */\r
1188   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1189   wp.length = sizeof(WINDOWPLACEMENT);\r
1190   wp.flags = 0;\r
1191   wp.showCmd = nCmdShow;\r
1192   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1193   wp.rcNormalPosition.left = wpMain.x;\r
1194   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1195   wp.rcNormalPosition.top = wpMain.y;\r
1196   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1197   SetWindowPlacement(hwndMain, &wp);\r
1198 \r
1199   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1200 \r
1201   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1202                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1203 \r
1204   if (hwndConsole) {\r
1205 #if AOT_CONSOLE\r
1206     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1207                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1208 #endif\r
1209     ShowWindow(hwndConsole, nCmdShow);\r
1210     SetActiveWindow(hwndConsole);\r
1211   }\r
1212   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1213   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1214 \r
1215   return TRUE;\r
1216 \r
1217 }\r
1218 \r
1219 VOID\r
1220 InitMenuChecks()\r
1221 {\r
1222   HMENU hmenu = GetMenu(hwndMain);\r
1223 \r
1224   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1225                         MF_BYCOMMAND|((appData.icsActive &&\r
1226                                        *appData.icsCommPort != NULLCHAR) ?\r
1227                                       MF_ENABLED : MF_GRAYED));\r
1228   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1229                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1230                                      MF_CHECKED : MF_UNCHECKED));\r
1231 }\r
1232 \r
1233 //---------------------------------------------------------------------------------------------------------\r
1234 \r
1235 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1236 #define XBOARD FALSE\r
1237 \r
1238 #define OPTCHAR "/"\r
1239 #define SEPCHAR "="\r
1240 #define TOPLEVEL 0\r
1241 \r
1242 #include "args.h"\r
1243 \r
1244 // front-end part of option handling\r
1245 \r
1246 VOID\r
1247 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1248 {\r
1249   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1250   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1251   DeleteDC(hdc);\r
1252   lf->lfWidth = 0;\r
1253   lf->lfEscapement = 0;\r
1254   lf->lfOrientation = 0;\r
1255   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1256   lf->lfItalic = mfp->italic;\r
1257   lf->lfUnderline = mfp->underline;\r
1258   lf->lfStrikeOut = mfp->strikeout;\r
1259   lf->lfCharSet = mfp->charset;\r
1260   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1261   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1262   lf->lfQuality = DEFAULT_QUALITY;\r
1263   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1264     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1265 }\r
1266 \r
1267 void\r
1268 CreateFontInMF(MyFont *mf)\r
1269\r
1270   LFfromMFP(&mf->lf, &mf->mfp);\r
1271   if (mf->hf) DeleteObject(mf->hf);\r
1272   mf->hf = CreateFontIndirect(&mf->lf);\r
1273 }\r
1274 \r
1275 // [HGM] This platform-dependent table provides the location for storing the color info\r
1276 void *\r
1277 colorVariable[] = {\r
1278   &whitePieceColor, \r
1279   &blackPieceColor, \r
1280   &lightSquareColor,\r
1281   &darkSquareColor, \r
1282   &highlightSquareColor,\r
1283   &premoveHighlightColor,\r
1284   NULL,\r
1285   &consoleBackgroundColor,\r
1286   &appData.fontForeColorWhite,\r
1287   &appData.fontBackColorWhite,\r
1288   &appData.fontForeColorBlack,\r
1289   &appData.fontBackColorBlack,\r
1290   &appData.evalHistColorWhite,\r
1291   &appData.evalHistColorBlack,\r
1292   &appData.highlightArrowColor,\r
1293 };\r
1294 \r
1295 /* Command line font name parser.  NULL name means do nothing.\r
1296    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1297    For backward compatibility, syntax without the colon is also\r
1298    accepted, but font names with digits in them won't work in that case.\r
1299 */\r
1300 VOID\r
1301 ParseFontName(char *name, MyFontParams *mfp)\r
1302 {\r
1303   char *p, *q;\r
1304   if (name == NULL) return;\r
1305   p = name;\r
1306   q = strchr(p, ':');\r
1307   if (q) {\r
1308     if (q - p >= sizeof(mfp->faceName))\r
1309       ExitArgError(_("Font name too long:"), name, TRUE);\r
1310     memcpy(mfp->faceName, p, q - p);\r
1311     mfp->faceName[q - p] = NULLCHAR;\r
1312     p = q + 1;\r
1313   } else {\r
1314     q = mfp->faceName;\r
1315 \r
1316     while (*p && !isdigit(*p)) {\r
1317       *q++ = *p++;\r
1318       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1319         ExitArgError(_("Font name too long:"), name, TRUE);\r
1320     }\r
1321     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1322     *q = NULLCHAR;\r
1323   }\r
1324   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1325   mfp->pointSize = (float) atof(p);\r
1326   mfp->bold = (strchr(p, 'b') != NULL);\r
1327   mfp->italic = (strchr(p, 'i') != NULL);\r
1328   mfp->underline = (strchr(p, 'u') != NULL);\r
1329   mfp->strikeout = (strchr(p, 's') != NULL);\r
1330   mfp->charset = DEFAULT_CHARSET;\r
1331   q = strchr(p, 'c');\r
1332   if (q)\r
1333     mfp->charset = (BYTE) atoi(q+1);\r
1334 }\r
1335 \r
1336 void\r
1337 ParseFont(char *name, int number)\r
1338 { // wrapper to shield back-end from 'font'\r
1339   ParseFontName(name, &font[boardSize][number]->mfp);\r
1340 }\r
1341 \r
1342 void\r
1343 SetFontDefaults()\r
1344 { // in WB  we have a 2D array of fonts; this initializes their description\r
1345   int i, j;\r
1346   /* Point font array elements to structures and\r
1347      parse default font names */\r
1348   for (i=0; i<NUM_FONTS; i++) {\r
1349     for (j=0; j<NUM_SIZES; j++) {\r
1350       font[j][i] = &fontRec[j][i];\r
1351       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1352     }\r
1353   }\r
1354 }\r
1355 \r
1356 void\r
1357 CreateFonts()\r
1358 { // here we create the actual fonts from the selected descriptions\r
1359   int i, j;\r
1360   for (i=0; i<NUM_FONTS; i++) {\r
1361     for (j=0; j<NUM_SIZES; j++) {\r
1362       CreateFontInMF(font[j][i]);\r
1363     }\r
1364   }\r
1365 }\r
1366 /* Color name parser.\r
1367    X version accepts X color names, but this one\r
1368    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1369 COLORREF\r
1370 ParseColorName(char *name)\r
1371 {\r
1372   int red, green, blue, count;\r
1373   char buf[MSG_SIZ];\r
1374 \r
1375   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1376   if (count != 3) {\r
1377     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1378       &red, &green, &blue);\r
1379   }\r
1380   if (count != 3) {\r
1381     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1382     DisplayError(buf, 0);\r
1383     return RGB(0, 0, 0);\r
1384   }\r
1385   return PALETTERGB(red, green, blue);\r
1386 }\r
1387 \r
1388 void\r
1389 ParseColor(int n, char *name)\r
1390 { // for WinBoard the color is an int, which needs to be derived from the string\r
1391   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1392 }\r
1393 \r
1394 void\r
1395 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1396 {\r
1397   char *e = argValue;\r
1398   int eff = 0;\r
1399 \r
1400   while (*e) {\r
1401     if (*e == 'b')      eff |= CFE_BOLD;\r
1402     else if (*e == 'i') eff |= CFE_ITALIC;\r
1403     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1404     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1405     else if (*e == '#' || isdigit(*e)) break;\r
1406     e++;\r
1407   }\r
1408   *effects = eff;\r
1409   *color   = ParseColorName(e);\r
1410 }\r
1411 \r
1412 void\r
1413 ParseTextAttribs(ColorClass cc, char *s)\r
1414 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1415     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1416     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1417 }\r
1418 \r
1419 void\r
1420 ParseBoardSize(void *addr, char *name)\r
1421 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1422   BoardSize bs = SizeTiny;\r
1423   while (sizeInfo[bs].name != NULL) {\r
1424     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1425         *(BoardSize *)addr = bs;\r
1426         return;\r
1427     }\r
1428     bs++;\r
1429   }\r
1430   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1431 }\r
1432 \r
1433 void\r
1434 LoadAllSounds()\r
1435 { // [HGM] import name from appData first\r
1436   ColorClass cc;\r
1437   SoundClass sc;\r
1438   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1439     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1440     textAttribs[cc].sound.data = NULL;\r
1441     MyLoadSound(&textAttribs[cc].sound);\r
1442   }\r
1443   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1444     textAttribs[cc].sound.name = strdup("");\r
1445     textAttribs[cc].sound.data = NULL;\r
1446   }\r
1447   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1448     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1449     sounds[sc].data = NULL;\r
1450     MyLoadSound(&sounds[sc]);\r
1451   }\r
1452 }\r
1453 \r
1454 void\r
1455 SetCommPortDefaults()\r
1456 {\r
1457    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1458   dcb.DCBlength = sizeof(DCB);\r
1459   dcb.BaudRate = 9600;\r
1460   dcb.fBinary = TRUE;\r
1461   dcb.fParity = FALSE;\r
1462   dcb.fOutxCtsFlow = FALSE;\r
1463   dcb.fOutxDsrFlow = FALSE;\r
1464   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1465   dcb.fDsrSensitivity = FALSE;\r
1466   dcb.fTXContinueOnXoff = TRUE;\r
1467   dcb.fOutX = FALSE;\r
1468   dcb.fInX = FALSE;\r
1469   dcb.fNull = FALSE;\r
1470   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1471   dcb.fAbortOnError = FALSE;\r
1472   dcb.ByteSize = 7;\r
1473   dcb.Parity = SPACEPARITY;\r
1474   dcb.StopBits = ONESTOPBIT;\r
1475 }\r
1476 \r
1477 // [HGM] args: these three cases taken out to stay in front-end\r
1478 void\r
1479 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1480 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1481         // while the curent board size determines the element. This system should be ported to XBoard.\r
1482         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1483         int bs;\r
1484         for (bs=0; bs<NUM_SIZES; bs++) {\r
1485           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1486           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1487           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1488             ad->argName, mfp->faceName, mfp->pointSize,\r
1489             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1490             mfp->bold ? "b" : "",\r
1491             mfp->italic ? "i" : "",\r
1492             mfp->underline ? "u" : "",\r
1493             mfp->strikeout ? "s" : "",\r
1494             (int)mfp->charset);\r
1495         }\r
1496       }\r
1497 \r
1498 void\r
1499 ExportSounds()\r
1500 { // [HGM] copy the names from the internal WB variables to appData\r
1501   ColorClass cc;\r
1502   SoundClass sc;\r
1503   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1504     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1505   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1506     (&appData.soundMove)[sc] = sounds[sc].name;\r
1507 }\r
1508 \r
1509 void\r
1510 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1511 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1512         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1513         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1514           (ta->effects & CFE_BOLD) ? "b" : "",\r
1515           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1516           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1517           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1518           (ta->effects) ? " " : "",\r
1519           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1520       }\r
1521 \r
1522 void\r
1523 SaveColor(FILE *f, ArgDescriptor *ad)\r
1524 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1525         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1526         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1527           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1528 }\r
1529 \r
1530 void\r
1531 SaveBoardSize(FILE *f, char *name, void *addr)\r
1532 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1533   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1534 }\r
1535 \r
1536 void\r
1537 ParseCommPortSettings(char *s)\r
1538 { // wrapper to keep dcb from back-end\r
1539   ParseCommSettings(s, &dcb);\r
1540 }\r
1541 \r
1542 void\r
1543 GetWindowCoords()\r
1544 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1545   GetActualPlacement(hwndMain, &wpMain);\r
1546   GetActualPlacement(hwndConsole, &wpConsole);\r
1547   GetActualPlacement(commentDialog, &wpComment);\r
1548   GetActualPlacement(editTagsDialog, &wpTags);\r
1549   GetActualPlacement(gameListDialog, &wpGameList);\r
1550   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1551   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1552   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1553 }\r
1554 \r
1555 void\r
1556 PrintCommPortSettings(FILE *f, char *name)\r
1557 { // wrapper to shield back-end from DCB\r
1558       PrintCommSettings(f, name, &dcb);\r
1559 }\r
1560 \r
1561 int\r
1562 MySearchPath(char *installDir, char *name, char *fullname)\r
1563 {\r
1564   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1565   if(name[0]== '%') {\r
1566     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1567     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1568       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1569       *strchr(buf, '%') = 0;\r
1570       strcat(fullname, getenv(buf));\r
1571       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1572     }\r
1573     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1574     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1575     return (int) strlen(fullname);\r
1576   }\r
1577   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1578 }\r
1579 \r
1580 int\r
1581 MyGetFullPathName(char *name, char *fullname)\r
1582 {\r
1583   char *dummy;\r
1584   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1585 }\r
1586 \r
1587 int\r
1588 MainWindowUp()\r
1589 { // [HGM] args: allows testing if main window is realized from back-end\r
1590   return hwndMain != NULL;\r
1591 }\r
1592 \r
1593 void\r
1594 PopUpStartupDialog()\r
1595 {\r
1596     FARPROC lpProc;\r
1597     \r
1598     LoadLanguageFile(appData.language);\r
1599     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1600     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1601     FreeProcInstance(lpProc);\r
1602 }\r
1603 \r
1604 /*---------------------------------------------------------------------------*\\r
1605  *\r
1606  * GDI board drawing routines\r
1607  *\r
1608 \*---------------------------------------------------------------------------*/\r
1609 \r
1610 /* [AS] Draw square using background texture */\r
1611 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1612 {\r
1613     XFORM   x;\r
1614 \r
1615     if( mode == 0 ) {\r
1616         return; /* Should never happen! */\r
1617     }\r
1618 \r
1619     SetGraphicsMode( dst, GM_ADVANCED );\r
1620 \r
1621     switch( mode ) {\r
1622     case 1:\r
1623         /* Identity */\r
1624         break;\r
1625     case 2:\r
1626         /* X reflection */\r
1627         x.eM11 = -1.0;\r
1628         x.eM12 = 0;\r
1629         x.eM21 = 0;\r
1630         x.eM22 = 1.0;\r
1631         x.eDx = (FLOAT) dw + dx - 1;\r
1632         x.eDy = 0;\r
1633         dx = 0;\r
1634         SetWorldTransform( dst, &x );\r
1635         break;\r
1636     case 3:\r
1637         /* Y reflection */\r
1638         x.eM11 = 1.0;\r
1639         x.eM12 = 0;\r
1640         x.eM21 = 0;\r
1641         x.eM22 = -1.0;\r
1642         x.eDx = 0;\r
1643         x.eDy = (FLOAT) dh + dy - 1;\r
1644         dy = 0;\r
1645         SetWorldTransform( dst, &x );\r
1646         break;\r
1647     case 4:\r
1648         /* X/Y flip */\r
1649         x.eM11 = 0;\r
1650         x.eM12 = 1.0;\r
1651         x.eM21 = 1.0;\r
1652         x.eM22 = 0;\r
1653         x.eDx = (FLOAT) dx;\r
1654         x.eDy = (FLOAT) dy;\r
1655         dx = 0;\r
1656         dy = 0;\r
1657         SetWorldTransform( dst, &x );\r
1658         break;\r
1659     }\r
1660 \r
1661     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1662 \r
1663     x.eM11 = 1.0;\r
1664     x.eM12 = 0;\r
1665     x.eM21 = 0;\r
1666     x.eM22 = 1.0;\r
1667     x.eDx = 0;\r
1668     x.eDy = 0;\r
1669     SetWorldTransform( dst, &x );\r
1670 \r
1671     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1672 }\r
1673 \r
1674 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1675 enum {\r
1676     PM_WP = (int) WhitePawn, \r
1677     PM_WN = (int) WhiteKnight, \r
1678     PM_WB = (int) WhiteBishop, \r
1679     PM_WR = (int) WhiteRook, \r
1680     PM_WQ = (int) WhiteQueen, \r
1681     PM_WF = (int) WhiteFerz, \r
1682     PM_WW = (int) WhiteWazir, \r
1683     PM_WE = (int) WhiteAlfil, \r
1684     PM_WM = (int) WhiteMan, \r
1685     PM_WO = (int) WhiteCannon, \r
1686     PM_WU = (int) WhiteUnicorn, \r
1687     PM_WH = (int) WhiteNightrider, \r
1688     PM_WA = (int) WhiteAngel, \r
1689     PM_WC = (int) WhiteMarshall, \r
1690     PM_WAB = (int) WhiteCardinal, \r
1691     PM_WD = (int) WhiteDragon, \r
1692     PM_WL = (int) WhiteLance, \r
1693     PM_WS = (int) WhiteCobra, \r
1694     PM_WV = (int) WhiteFalcon, \r
1695     PM_WSG = (int) WhiteSilver, \r
1696     PM_WG = (int) WhiteGrasshopper, \r
1697     PM_WK = (int) WhiteKing,\r
1698     PM_BP = (int) BlackPawn, \r
1699     PM_BN = (int) BlackKnight, \r
1700     PM_BB = (int) BlackBishop, \r
1701     PM_BR = (int) BlackRook, \r
1702     PM_BQ = (int) BlackQueen, \r
1703     PM_BF = (int) BlackFerz, \r
1704     PM_BW = (int) BlackWazir, \r
1705     PM_BE = (int) BlackAlfil, \r
1706     PM_BM = (int) BlackMan,\r
1707     PM_BO = (int) BlackCannon, \r
1708     PM_BU = (int) BlackUnicorn, \r
1709     PM_BH = (int) BlackNightrider, \r
1710     PM_BA = (int) BlackAngel, \r
1711     PM_BC = (int) BlackMarshall, \r
1712     PM_BG = (int) BlackGrasshopper, \r
1713     PM_BAB = (int) BlackCardinal,\r
1714     PM_BD = (int) BlackDragon,\r
1715     PM_BL = (int) BlackLance,\r
1716     PM_BS = (int) BlackCobra,\r
1717     PM_BV = (int) BlackFalcon,\r
1718     PM_BSG = (int) BlackSilver,\r
1719     PM_BK = (int) BlackKing\r
1720 };\r
1721 \r
1722 static HFONT hPieceFont = NULL;\r
1723 static HBITMAP hPieceMask[(int) EmptySquare];\r
1724 static HBITMAP hPieceFace[(int) EmptySquare];\r
1725 static int fontBitmapSquareSize = 0;\r
1726 static char pieceToFontChar[(int) EmptySquare] =\r
1727                               { 'p', 'n', 'b', 'r', 'q', \r
1728                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1729                       'k', 'o', 'm', 'v', 't', 'w', \r
1730                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1731                                                               'l' };\r
1732 \r
1733 extern BOOL SetCharTable( char *table, const char * map );\r
1734 /* [HGM] moved to backend.c */\r
1735 \r
1736 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1737 {\r
1738     HBRUSH hbrush;\r
1739     BYTE r1 = GetRValue( color );\r
1740     BYTE g1 = GetGValue( color );\r
1741     BYTE b1 = GetBValue( color );\r
1742     BYTE r2 = r1 / 2;\r
1743     BYTE g2 = g1 / 2;\r
1744     BYTE b2 = b1 / 2;\r
1745     RECT rc;\r
1746 \r
1747     /* Create a uniform background first */\r
1748     hbrush = CreateSolidBrush( color );\r
1749     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1750     FillRect( hdc, &rc, hbrush );\r
1751     DeleteObject( hbrush );\r
1752     \r
1753     if( mode == 1 ) {\r
1754         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1755         int steps = squareSize / 2;\r
1756         int i;\r
1757 \r
1758         for( i=0; i<steps; i++ ) {\r
1759             BYTE r = r1 - (r1-r2) * i / steps;\r
1760             BYTE g = g1 - (g1-g2) * i / steps;\r
1761             BYTE b = b1 - (b1-b2) * i / steps;\r
1762 \r
1763             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1764             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1765             FillRect( hdc, &rc, hbrush );\r
1766             DeleteObject(hbrush);\r
1767         }\r
1768     }\r
1769     else if( mode == 2 ) {\r
1770         /* Diagonal gradient, good more or less for every piece */\r
1771         POINT triangle[3];\r
1772         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1773         HBRUSH hbrush_old;\r
1774         int steps = squareSize;\r
1775         int i;\r
1776 \r
1777         triangle[0].x = squareSize - steps;\r
1778         triangle[0].y = squareSize;\r
1779         triangle[1].x = squareSize;\r
1780         triangle[1].y = squareSize;\r
1781         triangle[2].x = squareSize;\r
1782         triangle[2].y = squareSize - steps;\r
1783 \r
1784         for( i=0; i<steps; i++ ) {\r
1785             BYTE r = r1 - (r1-r2) * i / steps;\r
1786             BYTE g = g1 - (g1-g2) * i / steps;\r
1787             BYTE b = b1 - (b1-b2) * i / steps;\r
1788 \r
1789             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1790             hbrush_old = SelectObject( hdc, hbrush );\r
1791             Polygon( hdc, triangle, 3 );\r
1792             SelectObject( hdc, hbrush_old );\r
1793             DeleteObject(hbrush);\r
1794             triangle[0].x++;\r
1795             triangle[2].y++;\r
1796         }\r
1797 \r
1798         SelectObject( hdc, hpen );\r
1799     }\r
1800 }\r
1801 \r
1802 /*\r
1803     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1804     seems to work ok. The main problem here is to find the "inside" of a chess\r
1805     piece: follow the steps as explained below.\r
1806 */\r
1807 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1808 {\r
1809     HBITMAP hbm;\r
1810     HBITMAP hbm_old;\r
1811     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1812     RECT rc;\r
1813     SIZE sz;\r
1814 \r
1815 \r
1816     POINT pt;\r
1817     int backColor = whitePieceColor; \r
1818     int foreColor = blackPieceColor;\r
1819     \r
1820     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1821         backColor = appData.fontBackColorWhite;\r
1822         foreColor = appData.fontForeColorWhite;\r
1823     }\r
1824     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1825         backColor = appData.fontBackColorBlack;\r
1826         foreColor = appData.fontForeColorBlack;\r
1827     }\r
1828 \r
1829     /* Mask */\r
1830     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1831 \r
1832     hbm_old = SelectObject( hdc, hbm );\r
1833 \r
1834     rc.left = 0;\r
1835     rc.top = 0;\r
1836     rc.right = squareSize;\r
1837     rc.bottom = squareSize;\r
1838 \r
1839     /* Step 1: background is now black */\r
1840     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1841 \r
1842     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1843 \r
1844     pt.x = (squareSize - sz.cx) / 2;\r
1845     pt.y = (squareSize - sz.cy) / 2;\r
1846 \r
1847     SetBkMode( hdc, TRANSPARENT );\r
1848     SetTextColor( hdc, chroma );\r
1849     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1850     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1851 \r
1852     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1853     /* Step 3: the area outside the piece is filled with white */\r
1854 //    FloodFill( hdc, 0, 0, chroma );\r
1855     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1856     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1857     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1858     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1859     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1860     /* \r
1861         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1862         but if the start point is not inside the piece we're lost!\r
1863         There should be a better way to do this... if we could create a region or path\r
1864         from the fill operation we would be fine for example.\r
1865     */\r
1866 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1867     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1868 \r
1869     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1870         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1871         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1872 \r
1873         SelectObject( dc2, bm2 );\r
1874         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1875         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1876         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1877         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1878         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1879 \r
1880         DeleteDC( dc2 );\r
1881         DeleteObject( bm2 );\r
1882     }\r
1883 \r
1884     SetTextColor( hdc, 0 );\r
1885     /* \r
1886         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1887         draw the piece again in black for safety.\r
1888     */\r
1889     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1890 \r
1891     SelectObject( hdc, hbm_old );\r
1892 \r
1893     if( hPieceMask[index] != NULL ) {\r
1894         DeleteObject( hPieceMask[index] );\r
1895     }\r
1896 \r
1897     hPieceMask[index] = hbm;\r
1898 \r
1899     /* Face */\r
1900     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1901 \r
1902     SelectObject( hdc, hbm );\r
1903 \r
1904     {\r
1905         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1906         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1907         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1908 \r
1909         SelectObject( dc1, hPieceMask[index] );\r
1910         SelectObject( dc2, bm2 );\r
1911         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1912         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1913         \r
1914         /* \r
1915             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1916             the piece background and deletes (makes transparent) the rest.\r
1917             Thanks to that mask, we are free to paint the background with the greates\r
1918             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1919             We use this, to make gradients and give the pieces a "roundish" look.\r
1920         */\r
1921         SetPieceBackground( hdc, backColor, 2 );\r
1922         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1923 \r
1924         DeleteDC( dc2 );\r
1925         DeleteDC( dc1 );\r
1926         DeleteObject( bm2 );\r
1927     }\r
1928 \r
1929     SetTextColor( hdc, foreColor );\r
1930     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1931 \r
1932     SelectObject( hdc, hbm_old );\r
1933 \r
1934     if( hPieceFace[index] != NULL ) {\r
1935         DeleteObject( hPieceFace[index] );\r
1936     }\r
1937 \r
1938     hPieceFace[index] = hbm;\r
1939 }\r
1940 \r
1941 static int TranslatePieceToFontPiece( int piece )\r
1942 {\r
1943     switch( piece ) {\r
1944     case BlackPawn:\r
1945         return PM_BP;\r
1946     case BlackKnight:\r
1947         return PM_BN;\r
1948     case BlackBishop:\r
1949         return PM_BB;\r
1950     case BlackRook:\r
1951         return PM_BR;\r
1952     case BlackQueen:\r
1953         return PM_BQ;\r
1954     case BlackKing:\r
1955         return PM_BK;\r
1956     case WhitePawn:\r
1957         return PM_WP;\r
1958     case WhiteKnight:\r
1959         return PM_WN;\r
1960     case WhiteBishop:\r
1961         return PM_WB;\r
1962     case WhiteRook:\r
1963         return PM_WR;\r
1964     case WhiteQueen:\r
1965         return PM_WQ;\r
1966     case WhiteKing:\r
1967         return PM_WK;\r
1968 \r
1969     case BlackAngel:\r
1970         return PM_BA;\r
1971     case BlackMarshall:\r
1972         return PM_BC;\r
1973     case BlackFerz:\r
1974         return PM_BF;\r
1975     case BlackNightrider:\r
1976         return PM_BH;\r
1977     case BlackAlfil:\r
1978         return PM_BE;\r
1979     case BlackWazir:\r
1980         return PM_BW;\r
1981     case BlackUnicorn:\r
1982         return PM_BU;\r
1983     case BlackCannon:\r
1984         return PM_BO;\r
1985     case BlackGrasshopper:\r
1986         return PM_BG;\r
1987     case BlackMan:\r
1988         return PM_BM;\r
1989     case BlackSilver:\r
1990         return PM_BSG;\r
1991     case BlackLance:\r
1992         return PM_BL;\r
1993     case BlackFalcon:\r
1994         return PM_BV;\r
1995     case BlackCobra:\r
1996         return PM_BS;\r
1997     case BlackCardinal:\r
1998         return PM_BAB;\r
1999     case BlackDragon:\r
2000         return PM_BD;\r
2001 \r
2002     case WhiteAngel:\r
2003         return PM_WA;\r
2004     case WhiteMarshall:\r
2005         return PM_WC;\r
2006     case WhiteFerz:\r
2007         return PM_WF;\r
2008     case WhiteNightrider:\r
2009         return PM_WH;\r
2010     case WhiteAlfil:\r
2011         return PM_WE;\r
2012     case WhiteWazir:\r
2013         return PM_WW;\r
2014     case WhiteUnicorn:\r
2015         return PM_WU;\r
2016     case WhiteCannon:\r
2017         return PM_WO;\r
2018     case WhiteGrasshopper:\r
2019         return PM_WG;\r
2020     case WhiteMan:\r
2021         return PM_WM;\r
2022     case WhiteSilver:\r
2023         return PM_WSG;\r
2024     case WhiteLance:\r
2025         return PM_WL;\r
2026     case WhiteFalcon:\r
2027         return PM_WV;\r
2028     case WhiteCobra:\r
2029         return PM_WS;\r
2030     case WhiteCardinal:\r
2031         return PM_WAB;\r
2032     case WhiteDragon:\r
2033         return PM_WD;\r
2034     }\r
2035 \r
2036     return 0;\r
2037 }\r
2038 \r
2039 void CreatePiecesFromFont()\r
2040 {\r
2041     LOGFONT lf;\r
2042     HDC hdc_window = NULL;\r
2043     HDC hdc = NULL;\r
2044     HFONT hfont_old;\r
2045     int fontHeight;\r
2046     int i;\r
2047 \r
2048     if( fontBitmapSquareSize < 0 ) {\r
2049         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2050         return;\r
2051     }\r
2052 \r
2053     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2054             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2055         fontBitmapSquareSize = -1;\r
2056         return;\r
2057     }\r
2058 \r
2059     if( fontBitmapSquareSize != squareSize ) {\r
2060         hdc_window = GetDC( hwndMain );\r
2061         hdc = CreateCompatibleDC( hdc_window );\r
2062 \r
2063         if( hPieceFont != NULL ) {\r
2064             DeleteObject( hPieceFont );\r
2065         }\r
2066         else {\r
2067             for( i=0; i<=(int)BlackKing; i++ ) {\r
2068                 hPieceMask[i] = NULL;\r
2069                 hPieceFace[i] = NULL;\r
2070             }\r
2071         }\r
2072 \r
2073         fontHeight = 75;\r
2074 \r
2075         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2076             fontHeight = appData.fontPieceSize;\r
2077         }\r
2078 \r
2079         fontHeight = (fontHeight * squareSize) / 100;\r
2080 \r
2081         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2082         lf.lfWidth = 0;\r
2083         lf.lfEscapement = 0;\r
2084         lf.lfOrientation = 0;\r
2085         lf.lfWeight = FW_NORMAL;\r
2086         lf.lfItalic = 0;\r
2087         lf.lfUnderline = 0;\r
2088         lf.lfStrikeOut = 0;\r
2089         lf.lfCharSet = DEFAULT_CHARSET;\r
2090         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2091         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2092         lf.lfQuality = PROOF_QUALITY;\r
2093         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2094         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2095         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2096 \r
2097         hPieceFont = CreateFontIndirect( &lf );\r
2098 \r
2099         if( hPieceFont == NULL ) {\r
2100             fontBitmapSquareSize = -2;\r
2101         }\r
2102         else {\r
2103             /* Setup font-to-piece character table */\r
2104             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2105                 /* No (or wrong) global settings, try to detect the font */\r
2106                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2107                     /* Alpha */\r
2108                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2109                 }\r
2110                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2111                     /* DiagramTT* family */\r
2112                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2113                 }\r
2114                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2115                     /* Fairy symbols */\r
2116                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2117                 }\r
2118                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2119                     /* Good Companion (Some characters get warped as literal :-( */\r
2120                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2121                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2122                     SetCharTable(pieceToFontChar, s);\r
2123                 }\r
2124                 else {\r
2125                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2126                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2127                 }\r
2128             }\r
2129 \r
2130             /* Create bitmaps */\r
2131             hfont_old = SelectObject( hdc, hPieceFont );\r
2132             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2133                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2134                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2135 \r
2136             SelectObject( hdc, hfont_old );\r
2137 \r
2138             fontBitmapSquareSize = squareSize;\r
2139         }\r
2140     }\r
2141 \r
2142     if( hdc != NULL ) {\r
2143         DeleteDC( hdc );\r
2144     }\r
2145 \r
2146     if( hdc_window != NULL ) {\r
2147         ReleaseDC( hwndMain, hdc_window );\r
2148     }\r
2149 }\r
2150 \r
2151 HBITMAP\r
2152 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2153 {\r
2154   char name[128], buf[MSG_SIZ];\r
2155 \r
2156     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2157   if(appData.pieceDirectory[0]) {\r
2158     HBITMAP res;\r
2159     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2160     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2161     if(res) return res;\r
2162   }\r
2163   if (gameInfo.event &&\r
2164       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2165       strcmp(name, "k80s") == 0) {\r
2166     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2167   }\r
2168   return LoadBitmap(hinst, name);\r
2169 }\r
2170 \r
2171 \r
2172 /* Insert a color into the program's logical palette\r
2173    structure.  This code assumes the given color is\r
2174    the result of the RGB or PALETTERGB macro, and it\r
2175    knows how those macros work (which is documented).\r
2176 */\r
2177 VOID\r
2178 InsertInPalette(COLORREF color)\r
2179 {\r
2180   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2181 \r
2182   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2183     DisplayFatalError(_("Too many colors"), 0, 1);\r
2184     pLogPal->palNumEntries--;\r
2185     return;\r
2186   }\r
2187 \r
2188   pe->peFlags = (char) 0;\r
2189   pe->peRed = (char) (0xFF & color);\r
2190   pe->peGreen = (char) (0xFF & (color >> 8));\r
2191   pe->peBlue = (char) (0xFF & (color >> 16));\r
2192   return;\r
2193 }\r
2194 \r
2195 \r
2196 VOID\r
2197 InitDrawingColors()\r
2198 {\r
2199   int i;\r
2200   if (pLogPal == NULL) {\r
2201     /* Allocate enough memory for a logical palette with\r
2202      * PALETTESIZE entries and set the size and version fields\r
2203      * of the logical palette structure.\r
2204      */\r
2205     pLogPal = (NPLOGPALETTE)\r
2206       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2207                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2208     pLogPal->palVersion    = 0x300;\r
2209   }\r
2210   pLogPal->palNumEntries = 0;\r
2211 \r
2212   InsertInPalette(lightSquareColor);\r
2213   InsertInPalette(darkSquareColor);\r
2214   InsertInPalette(whitePieceColor);\r
2215   InsertInPalette(blackPieceColor);\r
2216   InsertInPalette(highlightSquareColor);\r
2217   InsertInPalette(premoveHighlightColor);\r
2218 \r
2219   /*  create a logical color palette according the information\r
2220    *  in the LOGPALETTE structure.\r
2221    */\r
2222   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2223 \r
2224   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2225   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2226   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2227   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2228   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2229   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2230   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2231     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2232 \r
2233    /* [AS] Force rendering of the font-based pieces */\r
2234   if( fontBitmapSquareSize > 0 ) {\r
2235     fontBitmapSquareSize = 0;\r
2236   }\r
2237 }\r
2238 \r
2239 \r
2240 int\r
2241 BoardWidth(int boardSize, int n)\r
2242 { /* [HGM] argument n added to allow different width and height */\r
2243   int lineGap = sizeInfo[boardSize].lineGap;\r
2244 \r
2245   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2246       lineGap = appData.overrideLineGap;\r
2247   }\r
2248 \r
2249   return (n + 1) * lineGap +\r
2250           n * sizeInfo[boardSize].squareSize;\r
2251 }\r
2252 \r
2253 /* Respond to board resize by dragging edge */\r
2254 VOID\r
2255 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2256 {\r
2257   BoardSize newSize = NUM_SIZES - 1;\r
2258   static int recurse = 0;\r
2259   if (IsIconic(hwndMain)) return;\r
2260   if (recurse > 0) return;\r
2261   recurse++;\r
2262   while (newSize > 0) {\r
2263         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2264         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2265            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2266     newSize--;\r
2267   } \r
2268   boardSize = newSize;\r
2269   InitDrawingSizes(boardSize, flags);\r
2270   recurse--;\r
2271 }\r
2272 \r
2273 \r
2274 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2275 \r
2276 VOID\r
2277 InitDrawingSizes(BoardSize boardSize, int flags)\r
2278 {\r
2279   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2280   ChessSquare piece;\r
2281   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2282   HDC hdc;\r
2283   SIZE clockSize, messageSize;\r
2284   HFONT oldFont;\r
2285   char buf[MSG_SIZ];\r
2286   char *str;\r
2287   HMENU hmenu = GetMenu(hwndMain);\r
2288   RECT crect, wrect, oldRect;\r
2289   int offby;\r
2290   LOGBRUSH logbrush;\r
2291   VariantClass v = gameInfo.variant;\r
2292 \r
2293   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2294   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2295 \r
2296   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2297   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2298   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2299   oldBoardSize = boardSize;\r
2300 \r
2301   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2302   { // correct board size to one where built-in pieces exist\r
2303     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2304        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2305 \r
2306       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2307       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2308       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2309       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2310       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2311                                    boardSize = SizeMiddling;\r
2312     }\r
2313   }\r
2314   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2315 \r
2316   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2317   oldRect.top = wpMain.y;\r
2318   oldRect.right = wpMain.x + wpMain.width;\r
2319   oldRect.bottom = wpMain.y + wpMain.height;\r
2320 \r
2321   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2322   smallLayout = sizeInfo[boardSize].smallLayout;\r
2323   squareSize = sizeInfo[boardSize].squareSize;\r
2324   lineGap = sizeInfo[boardSize].lineGap;\r
2325   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2326   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2327 \r
2328   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2329       lineGap = appData.overrideLineGap;\r
2330   }\r
2331 \r
2332   if (tinyLayout != oldTinyLayout) {\r
2333     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2334     if (tinyLayout) {\r
2335       style &= ~WS_SYSMENU;\r
2336       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2337                  "&Minimize\tCtrl+F4");\r
2338     } else {\r
2339       style |= WS_SYSMENU;\r
2340       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2341     }\r
2342     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2343 \r
2344     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2345       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2346         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2347     }\r
2348     DrawMenuBar(hwndMain);\r
2349   }\r
2350 \r
2351   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2352   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2353 \r
2354   /* Get text area sizes */\r
2355   hdc = GetDC(hwndMain);\r
2356   if (appData.clockMode) {\r
2357     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2358   } else {\r
2359     snprintf(buf, MSG_SIZ, _("White"));\r
2360   }\r
2361   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2362   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2363   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2364   str = _("We only care about the height here");\r
2365   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2366   SelectObject(hdc, oldFont);\r
2367   ReleaseDC(hwndMain, hdc);\r
2368 \r
2369   /* Compute where everything goes */\r
2370   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2371         /* [HGM] logo: if either logo is on, reserve space for it */\r
2372         logoHeight =  2*clockSize.cy;\r
2373         leftLogoRect.left   = OUTER_MARGIN;\r
2374         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2375         leftLogoRect.top    = OUTER_MARGIN;\r
2376         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2377 \r
2378         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2379         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2380         rightLogoRect.top    = OUTER_MARGIN;\r
2381         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2382 \r
2383 \r
2384     whiteRect.left = leftLogoRect.right;\r
2385     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2386     whiteRect.top = OUTER_MARGIN;\r
2387     whiteRect.bottom = whiteRect.top + logoHeight;\r
2388 \r
2389     blackRect.right = rightLogoRect.left;\r
2390     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2391     blackRect.top = whiteRect.top;\r
2392     blackRect.bottom = whiteRect.bottom;\r
2393   } else {\r
2394     whiteRect.left = OUTER_MARGIN;\r
2395     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2396     whiteRect.top = OUTER_MARGIN;\r
2397     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2398 \r
2399     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2400     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2401     blackRect.top = whiteRect.top;\r
2402     blackRect.bottom = whiteRect.bottom;\r
2403 \r
2404     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2405   }\r
2406 \r
2407   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2408   if (appData.showButtonBar) {\r
2409     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2410       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2411   } else {\r
2412     messageRect.right = OUTER_MARGIN + boardWidth;\r
2413   }\r
2414   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2415   messageRect.bottom = messageRect.top + messageSize.cy;\r
2416 \r
2417   boardRect.left = OUTER_MARGIN;\r
2418   boardRect.right = boardRect.left + boardWidth;\r
2419   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2420   boardRect.bottom = boardRect.top + boardHeight;\r
2421 \r
2422   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2423   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2424   oldTinyLayout = tinyLayout;\r
2425   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2426   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2427     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2428   winW *= 1 + twoBoards;\r
2429   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2430   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2431   wpMain.height = winH; //       without disturbing window attachments\r
2432   GetWindowRect(hwndMain, &wrect);\r
2433   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2434                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2435 \r
2436   // [HGM] placement: let attached windows follow size change.\r
2437   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2438   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2439   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2440   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2441   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2442 \r
2443   /* compensate if menu bar wrapped */\r
2444   GetClientRect(hwndMain, &crect);\r
2445   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2446   wpMain.height += offby;\r
2447   switch (flags) {\r
2448   case WMSZ_TOPLEFT:\r
2449     SetWindowPos(hwndMain, NULL, \r
2450                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2451                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2452     break;\r
2453 \r
2454   case WMSZ_TOPRIGHT:\r
2455   case WMSZ_TOP:\r
2456     SetWindowPos(hwndMain, NULL, \r
2457                  wrect.left, wrect.bottom - wpMain.height, \r
2458                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2459     break;\r
2460 \r
2461   case WMSZ_BOTTOMLEFT:\r
2462   case WMSZ_LEFT:\r
2463     SetWindowPos(hwndMain, NULL, \r
2464                  wrect.right - wpMain.width, wrect.top, \r
2465                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2466     break;\r
2467 \r
2468   case WMSZ_BOTTOMRIGHT:\r
2469   case WMSZ_BOTTOM:\r
2470   case WMSZ_RIGHT:\r
2471   default:\r
2472     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2473                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2474     break;\r
2475   }\r
2476 \r
2477   hwndPause = NULL;\r
2478   for (i = 0; i < N_BUTTONS; i++) {\r
2479     if (buttonDesc[i].hwnd != NULL) {\r
2480       DestroyWindow(buttonDesc[i].hwnd);\r
2481       buttonDesc[i].hwnd = NULL;\r
2482     }\r
2483     if (appData.showButtonBar) {\r
2484       buttonDesc[i].hwnd =\r
2485         CreateWindow("BUTTON", buttonDesc[i].label,\r
2486                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2487                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2488                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2489                      (HMENU) buttonDesc[i].id,\r
2490                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2491       if (tinyLayout) {\r
2492         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2493                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2494                     MAKELPARAM(FALSE, 0));\r
2495       }\r
2496       if (buttonDesc[i].id == IDM_Pause)\r
2497         hwndPause = buttonDesc[i].hwnd;\r
2498       buttonDesc[i].wndproc = (WNDPROC)\r
2499         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2500     }\r
2501   }\r
2502   if (gridPen != NULL) DeleteObject(gridPen);\r
2503   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2504   if (premovePen != NULL) DeleteObject(premovePen);\r
2505   if (lineGap != 0) {\r
2506     logbrush.lbStyle = BS_SOLID;\r
2507     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2508     gridPen =\r
2509       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2510                    lineGap, &logbrush, 0, NULL);\r
2511     logbrush.lbColor = highlightSquareColor;\r
2512     highlightPen =\r
2513       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2514                    lineGap, &logbrush, 0, NULL);\r
2515 \r
2516     logbrush.lbColor = premoveHighlightColor; \r
2517     premovePen =\r
2518       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2519                    lineGap, &logbrush, 0, NULL);\r
2520 \r
2521     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2522     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2523       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2524       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2525         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2526       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2527         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2528       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2529     }\r
2530     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2531       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2532       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2533         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2534         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2535       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2536         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2537       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2538     }\r
2539   }\r
2540 \r
2541   /* [HGM] Licensing requirement */\r
2542 #ifdef GOTHIC\r
2543   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2544 #endif\r
2545 #ifdef FALCON\r
2546   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2547 #endif\r
2548   GothicPopUp( "", VariantNormal);\r
2549 \r
2550 \r
2551 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2552 \r
2553   /* Load piece bitmaps for this board size */\r
2554   for (i=0; i<=2; i++) {\r
2555     for (piece = WhitePawn;\r
2556          (int) piece < (int) BlackPawn;\r
2557          piece = (ChessSquare) ((int) piece + 1)) {\r
2558       if (pieceBitmap[i][piece] != NULL)\r
2559         DeleteObject(pieceBitmap[i][piece]);\r
2560     }\r
2561   }\r
2562 \r
2563   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2564   // Orthodox Chess pieces\r
2565   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2566   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2567   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2568   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2569   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2570   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2571   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2572   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2573   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2574   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2575   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2576   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2577   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2578   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2579   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2580   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2581     // in Shogi, Hijack the unused Queen for Lance\r
2582     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2583     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2584     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2585   } else {\r
2586     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2587     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2588     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2589   }\r
2590 \r
2591   if(squareSize <= 72 && squareSize >= 33) { \r
2592     /* A & C are available in most sizes now */\r
2593     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2594       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2595       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2596       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2597       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2598       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2599       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2600       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2601       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2602       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2603       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2604       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2605       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2606     } else { // Smirf-like\r
2607       if(gameInfo.variant == VariantSChess) {\r
2608         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2609         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2610         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2611       } else {\r
2612         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2613         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2614         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2615       }\r
2616     }\r
2617     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2618       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2619       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2620       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2621     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2622       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2623       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2624       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2625     } else { // WinBoard standard\r
2626       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2627       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2628       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2629     }\r
2630   }\r
2631 \r
2632 \r
2633   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2634     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2635     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2636     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2637     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2638     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2639     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2640     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2641     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2642     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2643     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2644     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2645     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2646     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2647     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2648     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2649     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2650     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2651     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2652     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2653     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2654     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2655     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2656     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2657     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2658     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2659     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2660     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2661     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2662     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2663     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2664     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2665     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2666     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2667 \r
2668     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2669       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2670       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2671       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2672       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2673       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2674       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2675       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2676       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2677       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2678       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2679       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2680       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2681     } else {\r
2682       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2683       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2684       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2685       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2686       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2687       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2688       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2689       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2690       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2691       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2692       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2693       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2694     }\r
2695 \r
2696   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2697     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2698     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2699     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2700     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2701     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2702     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2703     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2704     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2705     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2706     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2707     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2708     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2709     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2710     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2711   }\r
2712 \r
2713 \r
2714   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2715   /* special Shogi support in this size */\r
2716   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2717       for (piece = WhitePawn;\r
2718            (int) piece < (int) BlackPawn;\r
2719            piece = (ChessSquare) ((int) piece + 1)) {\r
2720         if (pieceBitmap[i][piece] != NULL)\r
2721           DeleteObject(pieceBitmap[i][piece]);\r
2722       }\r
2723     }\r
2724   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2725   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2726   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2727   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2728   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2729   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2730   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2731   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2732   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2733   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2734   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2735   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2736   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2737   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2738   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2739   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2740   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2741   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2742   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2743   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2744   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2745   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2746   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2747   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2748   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2749   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2750   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2751   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2752   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2753   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2754   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2755   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2756   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2757   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2758   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2759   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2760   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2761   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2762   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2763   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2764   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2765   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2766   minorSize = 0;\r
2767   }\r
2768 }\r
2769 \r
2770 HBITMAP\r
2771 PieceBitmap(ChessSquare p, int kind)\r
2772 {\r
2773   if ((int) p >= (int) BlackPawn)\r
2774     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2775 \r
2776   return pieceBitmap[kind][(int) p];\r
2777 }\r
2778 \r
2779 /***************************************************************/\r
2780 \r
2781 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2782 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2783 /*\r
2784 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2785 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2786 */\r
2787 \r
2788 VOID\r
2789 SquareToPos(int row, int column, int * x, int * y)\r
2790 {\r
2791   if (flipView) {\r
2792     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2793     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2794   } else {\r
2795     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2796     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2797   }\r
2798 }\r
2799 \r
2800 VOID\r
2801 DrawCoordsOnDC(HDC hdc)\r
2802 {\r
2803   static char files[] = "0123456789012345678901221098765432109876543210";\r
2804   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2805   char str[2] = { NULLCHAR, NULLCHAR };\r
2806   int oldMode, oldAlign, x, y, start, i;\r
2807   HFONT oldFont;\r
2808   HBRUSH oldBrush;\r
2809 \r
2810   if (!appData.showCoords)\r
2811     return;\r
2812 \r
2813   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2814 \r
2815   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2816   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2817   oldAlign = GetTextAlign(hdc);\r
2818   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2819 \r
2820   y = boardRect.top + lineGap;\r
2821   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2822 \r
2823   if(border) {\r
2824     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2825     x += border - lineGap - 4; y += squareSize - 6;\r
2826   } else\r
2827   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2828   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2829     str[0] = files[start + i];\r
2830     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2831     y += squareSize + lineGap;\r
2832   }\r
2833 \r
2834   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2835 \r
2836   if(border) {\r
2837     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2838     x += -border + 4; y += border - squareSize + 6;\r
2839   } else\r
2840   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2841   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2842     str[0] = ranks[start + i];\r
2843     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2844     x += squareSize + lineGap;\r
2845   }    \r
2846 \r
2847   SelectObject(hdc, oldBrush);\r
2848   SetBkMode(hdc, oldMode);\r
2849   SetTextAlign(hdc, oldAlign);\r
2850   SelectObject(hdc, oldFont);\r
2851 }\r
2852 \r
2853 VOID\r
2854 DrawGridOnDC(HDC hdc)\r
2855 {\r
2856   HPEN oldPen;\r
2857  \r
2858   if (lineGap != 0) {\r
2859     oldPen = SelectObject(hdc, gridPen);\r
2860     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2861     SelectObject(hdc, oldPen);\r
2862   }\r
2863 }\r
2864 \r
2865 #define HIGHLIGHT_PEN 0\r
2866 #define PREMOVE_PEN   1\r
2867 \r
2868 VOID\r
2869 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2870 {\r
2871   int x1, y1;\r
2872   HPEN oldPen, hPen;\r
2873   if (lineGap == 0) return;\r
2874   if (flipView) {\r
2875     x1 = boardRect.left +\r
2876       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2877     y1 = boardRect.top +\r
2878       lineGap/2 + y * (squareSize + lineGap) + border;\r
2879   } else {\r
2880     x1 = boardRect.left +\r
2881       lineGap/2 + x * (squareSize + lineGap) + border;\r
2882     y1 = boardRect.top +\r
2883       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2884   }\r
2885   hPen = pen ? premovePen : highlightPen;\r
2886   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2887   MoveToEx(hdc, x1, y1, NULL);\r
2888   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2889   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2890   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2891   LineTo(hdc, x1, y1);\r
2892   SelectObject(hdc, oldPen);\r
2893 }\r
2894 \r
2895 VOID\r
2896 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2897 {\r
2898   int i;\r
2899   for (i=0; i<2; i++) {\r
2900     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2901       DrawHighlightOnDC(hdc, TRUE,\r
2902                         h->sq[i].x, h->sq[i].y,\r
2903                         pen);\r
2904   }\r
2905 }\r
2906 \r
2907 /* Note: sqcolor is used only in monoMode */\r
2908 /* Note that this code is largely duplicated in woptions.c,\r
2909    function DrawSampleSquare, so that needs to be updated too */\r
2910 VOID\r
2911 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2912 {\r
2913   HBITMAP oldBitmap;\r
2914   HBRUSH oldBrush;\r
2915   int tmpSize;\r
2916 \r
2917   if (appData.blindfold) return;\r
2918 \r
2919   /* [AS] Use font-based pieces if needed */\r
2920   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2921     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2922     CreatePiecesFromFont();\r
2923 \r
2924     if( fontBitmapSquareSize == squareSize ) {\r
2925         int index = TranslatePieceToFontPiece(piece);\r
2926 \r
2927         SelectObject( tmphdc, hPieceMask[ index ] );\r
2928 \r
2929       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2930         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2931       else\r
2932         BitBlt( hdc,\r
2933             x, y,\r
2934             squareSize, squareSize,\r
2935             tmphdc,\r
2936             0, 0,\r
2937             SRCAND );\r
2938 \r
2939         SelectObject( tmphdc, hPieceFace[ index ] );\r
2940 \r
2941       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2942         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2943       else\r
2944         BitBlt( hdc,\r
2945             x, y,\r
2946             squareSize, squareSize,\r
2947             tmphdc,\r
2948             0, 0,\r
2949             SRCPAINT );\r
2950 \r
2951         return;\r
2952     }\r
2953   }\r
2954 \r
2955   if (appData.monoMode) {\r
2956     SelectObject(tmphdc, PieceBitmap(piece, \r
2957       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2958     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2959            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2960   } else {\r
2961     HBRUSH xBrush = whitePieceBrush;\r
2962     tmpSize = squareSize;\r
2963     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2964     if(minorSize &&\r
2965         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2966          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2967       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2968       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2969       x += (squareSize - minorSize)>>1;\r
2970       y += squareSize - minorSize - 2;\r
2971       tmpSize = minorSize;\r
2972     }\r
2973     if (color || appData.allWhite ) {\r
2974       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2975       if( color )\r
2976               oldBrush = SelectObject(hdc, xBrush);\r
2977       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2978       if(appData.upsideDown && color==flipView)\r
2979         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2980       else\r
2981         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2982       /* Use black for outline of white pieces */\r
2983       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2984       if(appData.upsideDown && color==flipView)\r
2985         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2986       else\r
2987         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2988     } else if(appData.pieceDirectory[0]) {\r
2989       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2990       oldBrush = SelectObject(hdc, xBrush);\r
2991       if(appData.upsideDown && color==flipView)\r
2992         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2993       else\r
2994         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2995       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2996       if(appData.upsideDown && color==flipView)\r
2997         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2998       else\r
2999         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3000     } else {\r
3001       /* Use square color for details of black pieces */\r
3002       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3003       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3004       if(appData.upsideDown && !flipView)\r
3005         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3006       else\r
3007         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3008     }\r
3009     SelectObject(hdc, oldBrush);\r
3010     SelectObject(tmphdc, oldBitmap);\r
3011   }\r
3012 }\r
3013 \r
3014 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3015 int GetBackTextureMode( int algo )\r
3016 {\r
3017     int result = BACK_TEXTURE_MODE_DISABLED;\r
3018 \r
3019     switch( algo ) \r
3020     {\r
3021         case BACK_TEXTURE_MODE_PLAIN:\r
3022             result = 1; /* Always use identity map */\r
3023             break;\r
3024         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3025             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3026             break;\r
3027     }\r
3028 \r
3029     return result;\r
3030 }\r
3031 \r
3032 /* \r
3033     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3034     to handle redraws cleanly (as random numbers would always be different).\r
3035 */\r
3036 VOID RebuildTextureSquareInfo()\r
3037 {\r
3038     BITMAP bi;\r
3039     int lite_w = 0;\r
3040     int lite_h = 0;\r
3041     int dark_w = 0;\r
3042     int dark_h = 0;\r
3043     int row;\r
3044     int col;\r
3045 \r
3046     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3047 \r
3048     if( liteBackTexture != NULL ) {\r
3049         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3050             lite_w = bi.bmWidth;\r
3051             lite_h = bi.bmHeight;\r
3052         }\r
3053     }\r
3054 \r
3055     if( darkBackTexture != NULL ) {\r
3056         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3057             dark_w = bi.bmWidth;\r
3058             dark_h = bi.bmHeight;\r
3059         }\r
3060     }\r
3061 \r
3062     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3063         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3064             if( (col + row) & 1 ) {\r
3065                 /* Lite square */\r
3066                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3067                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3068                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3069                   else\r
3070                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3071                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3072                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3073                   else\r
3074                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3075                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3076                 }\r
3077             }\r
3078             else {\r
3079                 /* Dark square */\r
3080                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3081                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3082                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3083                   else\r
3084                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3085                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3086                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3087                   else\r
3088                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3089                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3090                 }\r
3091             }\r
3092         }\r
3093     }\r
3094 }\r
3095 \r
3096 /* [AS] Arrow highlighting support */\r
3097 \r
3098 static double A_WIDTH = 5; /* Width of arrow body */\r
3099 \r
3100 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3101 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3102 \r
3103 static double Sqr( double x )\r
3104 {\r
3105     return x*x;\r
3106 }\r
3107 \r
3108 static int Round( double x )\r
3109 {\r
3110     return (int) (x + 0.5);\r
3111 }\r
3112 \r
3113 /* Draw an arrow between two points using current settings */\r
3114 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3115 {\r
3116     POINT arrow[7];\r
3117     double dx, dy, j, k, x, y;\r
3118 \r
3119     if( d_x == s_x ) {\r
3120         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3121 \r
3122         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3123         arrow[0].y = s_y;\r
3124 \r
3125         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3126         arrow[1].y = d_y - h;\r
3127 \r
3128         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3129         arrow[2].y = d_y - h;\r
3130 \r
3131         arrow[3].x = d_x;\r
3132         arrow[3].y = d_y;\r
3133 \r
3134         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3135         arrow[5].y = d_y - h;\r
3136 \r
3137         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3138         arrow[4].y = d_y - h;\r
3139 \r
3140         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3141         arrow[6].y = s_y;\r
3142     }\r
3143     else if( d_y == s_y ) {\r
3144         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3145 \r
3146         arrow[0].x = s_x;\r
3147         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3148 \r
3149         arrow[1].x = d_x - w;\r
3150         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3151 \r
3152         arrow[2].x = d_x - w;\r
3153         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3154 \r
3155         arrow[3].x = d_x;\r
3156         arrow[3].y = d_y;\r
3157 \r
3158         arrow[5].x = d_x - w;\r
3159         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3160 \r
3161         arrow[4].x = d_x - w;\r
3162         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3163 \r
3164         arrow[6].x = s_x;\r
3165         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3166     }\r
3167     else {\r
3168         /* [AS] Needed a lot of paper for this! :-) */\r
3169         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3170         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3171   \r
3172         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3173 \r
3174         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3175 \r
3176         x = s_x;\r
3177         y = s_y;\r
3178 \r
3179         arrow[0].x = Round(x - j);\r
3180         arrow[0].y = Round(y + j*dx);\r
3181 \r
3182         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3183         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3184 \r
3185         if( d_x > s_x ) {\r
3186             x = (double) d_x - k;\r
3187             y = (double) d_y - k*dy;\r
3188         }\r
3189         else {\r
3190             x = (double) d_x + k;\r
3191             y = (double) d_y + k*dy;\r
3192         }\r
3193 \r
3194         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3195 \r
3196         arrow[6].x = Round(x - j);\r
3197         arrow[6].y = Round(y + j*dx);\r
3198 \r
3199         arrow[2].x = Round(arrow[6].x + 2*j);\r
3200         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3201 \r
3202         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3203         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3204 \r
3205         arrow[4].x = d_x;\r
3206         arrow[4].y = d_y;\r
3207 \r
3208         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3209         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3210     }\r
3211 \r
3212     Polygon( hdc, arrow, 7 );\r
3213 }\r
3214 \r
3215 /* [AS] Draw an arrow between two squares */\r
3216 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3217 {\r
3218     int s_x, s_y, d_x, d_y;\r
3219     HPEN hpen;\r
3220     HPEN holdpen;\r
3221     HBRUSH hbrush;\r
3222     HBRUSH holdbrush;\r
3223     LOGBRUSH stLB;\r
3224 \r
3225     if( s_col == d_col && s_row == d_row ) {\r
3226         return;\r
3227     }\r
3228 \r
3229     /* Get source and destination points */\r
3230     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3231     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3232 \r
3233     if( d_y > s_y ) {\r
3234         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3235     }\r
3236     else if( d_y < s_y ) {\r
3237         d_y += squareSize / 2 + squareSize / 4;\r
3238     }\r
3239     else {\r
3240         d_y += squareSize / 2;\r
3241     }\r
3242 \r
3243     if( d_x > s_x ) {\r
3244         d_x += squareSize / 2 - squareSize / 4;\r
3245     }\r
3246     else if( d_x < s_x ) {\r
3247         d_x += squareSize / 2 + squareSize / 4;\r
3248     }\r
3249     else {\r
3250         d_x += squareSize / 2;\r
3251     }\r
3252 \r
3253     s_x += squareSize / 2;\r
3254     s_y += squareSize / 2;\r
3255 \r
3256     /* Adjust width */\r
3257     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3258 \r
3259     /* Draw */\r
3260     stLB.lbStyle = BS_SOLID;\r
3261     stLB.lbColor = appData.highlightArrowColor;\r
3262     stLB.lbHatch = 0;\r
3263 \r
3264     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3265     holdpen = SelectObject( hdc, hpen );\r
3266     hbrush = CreateBrushIndirect( &stLB );\r
3267     holdbrush = SelectObject( hdc, hbrush );\r
3268 \r
3269     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3270 \r
3271     SelectObject( hdc, holdpen );\r
3272     SelectObject( hdc, holdbrush );\r
3273     DeleteObject( hpen );\r
3274     DeleteObject( hbrush );\r
3275 }\r
3276 \r
3277 BOOL HasHighlightInfo()\r
3278 {\r
3279     BOOL result = FALSE;\r
3280 \r
3281     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3282         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3283     {\r
3284         result = TRUE;\r
3285     }\r
3286 \r
3287     return result;\r
3288 \r
3289 \r
3290 \r
3291 }\r
3292 \r
3293 BOOL IsDrawArrowEnabled()\r
3294 {\r
3295     BOOL result = FALSE;\r
3296 \r
3297     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3298         result = TRUE;\r
3299     }\r
3300 \r
3301     return result;\r
3302 }\r
3303 \r
3304 VOID DrawArrowHighlight( HDC hdc )\r
3305 {\r
3306     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3307         DrawArrowBetweenSquares( hdc,\r
3308             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3309             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3310     }\r
3311 }\r
3312 \r
3313 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3314 {\r
3315     HRGN result = NULL;\r
3316 \r
3317     if( HasHighlightInfo() ) {\r
3318         int x1, y1, x2, y2;\r
3319         int sx, sy, dx, dy;\r
3320 \r
3321         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3322         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3323 \r
3324         sx = MIN( x1, x2 );\r
3325         sy = MIN( y1, y2 );\r
3326         dx = MAX( x1, x2 ) + squareSize;\r
3327         dy = MAX( y1, y2 ) + squareSize;\r
3328 \r
3329         result = CreateRectRgn( sx, sy, dx, dy );\r
3330     }\r
3331 \r
3332     return result;\r
3333 }\r
3334 \r
3335 /*\r
3336     Warning: this function modifies the behavior of several other functions. \r
3337     \r
3338     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3339     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3340     repaint is scattered all over the place, which is not good for features such as\r
3341     "arrow highlighting" that require a full repaint of the board.\r
3342 \r
3343     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3344     user interaction, when speed is not so important) but especially to avoid errors\r
3345     in the displayed graphics.\r
3346 \r
3347     In such patched places, I always try refer to this function so there is a single\r
3348     place to maintain knowledge.\r
3349     \r
3350     To restore the original behavior, just return FALSE unconditionally.\r
3351 */\r
3352 BOOL IsFullRepaintPreferrable()\r
3353 {\r
3354     BOOL result = FALSE;\r
3355 \r
3356     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3357         /* Arrow may appear on the board */\r
3358         result = TRUE;\r
3359     }\r
3360 \r
3361     return result;\r
3362 }\r
3363 \r
3364 /* \r
3365     This function is called by DrawPosition to know whether a full repaint must\r
3366     be forced or not.\r
3367 \r
3368     Only DrawPosition may directly call this function, which makes use of \r
3369     some state information. Other function should call DrawPosition specifying \r
3370     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3371 */\r
3372 BOOL DrawPositionNeedsFullRepaint()\r
3373 {\r
3374     BOOL result = FALSE;\r
3375 \r
3376     /* \r
3377         Probably a slightly better policy would be to trigger a full repaint\r
3378         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3379         but animation is fast enough that it's difficult to notice.\r
3380     */\r
3381     if( animInfo.piece == EmptySquare ) {\r
3382         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3383             result = TRUE;\r
3384         }\r
3385     }\r
3386 \r
3387     return result;\r
3388 }\r
3389 \r
3390 static HBITMAP borderBitmap;\r
3391 \r
3392 VOID\r
3393 DrawBackgroundOnDC(HDC hdc)\r
3394 {\r
3395   \r
3396   BITMAP bi;\r
3397   HDC tmphdc;\r
3398   HBITMAP hbm;\r
3399   static char oldBorder[MSG_SIZ];\r
3400   int w = 600, h = 600, mode;\r
3401 \r
3402   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3403     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3404     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3405   }\r
3406   if(borderBitmap == NULL) { // loading failed, use white\r
3407     FillRect( hdc, &boardRect, whitePieceBrush );\r
3408     return;\r
3409   }\r
3410   tmphdc = CreateCompatibleDC(hdc);\r
3411   hbm = SelectObject(tmphdc, borderBitmap);\r
3412   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3413             w = bi.bmWidth;\r
3414             h = bi.bmHeight;\r
3415   }\r
3416   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3417   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3418                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3419   SetStretchBltMode(hdc, mode);\r
3420   SelectObject(tmphdc, hbm);\r
3421   DeleteDC(tmphdc);\r
3422 }\r
3423 \r
3424 VOID\r
3425 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3426 {\r
3427   int row, column, x, y, square_color, piece_color;\r
3428   ChessSquare piece;\r
3429   HBRUSH oldBrush;\r
3430   HDC texture_hdc = NULL;\r
3431 \r
3432   /* [AS] Initialize background textures if needed */\r
3433   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3434       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3435       if( backTextureSquareSize != squareSize \r
3436        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3437           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3438           backTextureSquareSize = squareSize;\r
3439           RebuildTextureSquareInfo();\r
3440       }\r
3441 \r
3442       texture_hdc = CreateCompatibleDC( hdc );\r
3443   }\r
3444 \r
3445   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3446     for (column = 0; column < BOARD_WIDTH; column++) {\r
3447   \r
3448       SquareToPos(row, column, &x, &y);\r
3449 \r
3450       piece = board[row][column];\r
3451 \r
3452       square_color = ((column + row) % 2) == 1;\r
3453       if( gameInfo.variant == VariantXiangqi ) {\r
3454           square_color = !InPalace(row, column);\r
3455           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3456           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3457       }\r
3458       piece_color = (int) piece < (int) BlackPawn;\r
3459 \r
3460 \r
3461       /* [HGM] holdings file: light square or black */\r
3462       if(column == BOARD_LEFT-2) {\r
3463             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3464                 square_color = 1;\r
3465             else {\r
3466                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3467                 continue;\r
3468             }\r
3469       } else\r
3470       if(column == BOARD_RGHT + 1 ) {\r
3471             if( row < gameInfo.holdingsSize )\r
3472                 square_color = 1;\r
3473             else {\r
3474                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3475                 continue;\r
3476             }\r
3477       }\r
3478       if(column == BOARD_LEFT-1 ) /* left align */\r
3479             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3480       else if( column == BOARD_RGHT) /* right align */\r
3481             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3482       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3483       else\r
3484       if (appData.monoMode) {\r
3485         if (piece == EmptySquare) {\r
3486           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3487                  square_color ? WHITENESS : BLACKNESS);\r
3488         } else {\r
3489           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3490         }\r
3491       } \r
3492       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3493           /* [AS] Draw the square using a texture bitmap */\r
3494           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3495           int r = row, c = column; // [HGM] do not flip board in flipView\r
3496           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3497 \r
3498           DrawTile( x, y, \r
3499               squareSize, squareSize, \r
3500               hdc, \r
3501               texture_hdc,\r
3502               backTextureSquareInfo[r][c].mode,\r
3503               backTextureSquareInfo[r][c].x,\r
3504               backTextureSquareInfo[r][c].y );\r
3505 \r
3506           SelectObject( texture_hdc, hbm );\r
3507 \r
3508           if (piece != EmptySquare) {\r
3509               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3510           }\r
3511       }\r
3512       else {\r
3513         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3514 \r
3515         oldBrush = SelectObject(hdc, brush );\r
3516         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3517         SelectObject(hdc, oldBrush);\r
3518         if (piece != EmptySquare)\r
3519           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3520       }\r
3521     }\r
3522   }\r
3523 \r
3524   if( texture_hdc != NULL ) {\r
3525     DeleteDC( texture_hdc );\r
3526   }\r
3527 }\r
3528 \r
3529 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3530 void fputDW(FILE *f, int x)\r
3531 {\r
3532         fputc(x     & 255, f);\r
3533         fputc(x>>8  & 255, f);\r
3534         fputc(x>>16 & 255, f);\r
3535         fputc(x>>24 & 255, f);\r
3536 }\r
3537 \r
3538 #define MAX_CLIPS 200   /* more than enough */\r
3539 \r
3540 VOID\r
3541 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3542 {\r
3543 //  HBITMAP bufferBitmap;\r
3544   BITMAP bi;\r
3545 //  RECT Rect;\r
3546   HDC tmphdc;\r
3547   HBITMAP hbm;\r
3548   int w = 100, h = 50;\r
3549 \r
3550   if(logo == NULL) {\r
3551     if(!logoHeight) return;\r
3552     FillRect( hdc, &logoRect, whitePieceBrush );\r
3553   }\r
3554 //  GetClientRect(hwndMain, &Rect);\r
3555 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3556 //                                      Rect.bottom-Rect.top+1);\r
3557   tmphdc = CreateCompatibleDC(hdc);\r
3558   hbm = SelectObject(tmphdc, logo);\r
3559   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3560             w = bi.bmWidth;\r
3561             h = bi.bmHeight;\r
3562   }\r
3563   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3564                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3565   SelectObject(tmphdc, hbm);\r
3566   DeleteDC(tmphdc);\r
3567 }\r
3568 \r
3569 VOID\r
3570 DisplayLogos()\r
3571 {\r
3572   if(logoHeight) {\r
3573         HDC hdc = GetDC(hwndMain);\r
3574         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3575         if(appData.autoLogo) {\r
3576           \r
3577           switch(gameMode) { // pick logos based on game mode\r
3578             case IcsObserving:\r
3579                 whiteLogo = second.programLogo; // ICS logo\r
3580                 blackLogo = second.programLogo;\r
3581             default:\r
3582                 break;\r
3583             case IcsPlayingWhite:\r
3584                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3585                 blackLogo = second.programLogo; // ICS logo\r
3586                 break;\r
3587             case IcsPlayingBlack:\r
3588                 whiteLogo = second.programLogo; // ICS logo\r
3589                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3590                 break;\r
3591             case TwoMachinesPlay:\r
3592                 if(first.twoMachinesColor[0] == 'b') {\r
3593                     whiteLogo = second.programLogo;\r
3594                     blackLogo = first.programLogo;\r
3595                 }\r
3596                 break;\r
3597             case MachinePlaysWhite:\r
3598                 blackLogo = userLogo;\r
3599                 break;\r
3600             case MachinePlaysBlack:\r
3601                 whiteLogo = userLogo;\r
3602                 blackLogo = first.programLogo;\r
3603           }\r
3604         }\r
3605         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3606         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3607         ReleaseDC(hwndMain, hdc);\r
3608   }\r
3609 }\r
3610 \r
3611 void\r
3612 UpdateLogos(int display)\r
3613 { // called after loading new engine(s), in tourney or from menu\r
3614   LoadLogo(&first, 0, FALSE);\r
3615   LoadLogo(&second, 1, appData.icsActive);\r
3616   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3617   if(display) DisplayLogos();\r
3618 }\r
3619 \r
3620 static HDC hdcSeek;\r
3621 \r
3622 // [HGM] seekgraph\r
3623 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3624 {\r
3625     POINT stPt;\r
3626     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3627     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3628     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3629     SelectObject( hdcSeek, hp );\r
3630 }\r
3631 \r
3632 // front-end wrapper for drawing functions to do rectangles\r
3633 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3634 {\r
3635     HPEN hp;\r
3636     RECT rc;\r
3637 \r
3638     if (hdcSeek == NULL) {\r
3639     hdcSeek = GetDC(hwndMain);\r
3640       if (!appData.monoMode) {\r
3641         SelectPalette(hdcSeek, hPal, FALSE);\r
3642         RealizePalette(hdcSeek);\r
3643       }\r
3644     }\r
3645     hp = SelectObject( hdcSeek, gridPen );\r
3646     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3647     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3648     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3649     SelectObject( hdcSeek, hp );\r
3650 }\r
3651 \r
3652 // front-end wrapper for putting text in graph\r
3653 void DrawSeekText(char *buf, int x, int y)\r
3654 {\r
3655         SIZE stSize;\r
3656         SetBkMode( hdcSeek, TRANSPARENT );\r
3657         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3658         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3659 }\r
3660 \r
3661 void DrawSeekDot(int x, int y, int color)\r
3662 {\r
3663         int square = color & 0x80;\r
3664         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3665                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3666         color &= 0x7F;\r
3667         if(square)\r
3668             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3669                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3670         else\r
3671             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3672                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3673             SelectObject(hdcSeek, oldBrush);\r
3674 }\r
3675 \r
3676 void DrawSeekOpen()\r
3677 {\r
3678 }\r
3679 \r
3680 void DrawSeekClose()\r
3681 {\r
3682 }\r
3683 \r
3684 VOID\r
3685 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3686 {\r
3687   static Board lastReq[2], lastDrawn[2];\r
3688   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3689   static int lastDrawnFlipView = 0;\r
3690   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3691   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3692   HDC tmphdc;\r
3693   HDC hdcmem;\r
3694   HBITMAP bufferBitmap;\r
3695   HBITMAP oldBitmap;\r
3696   RECT Rect;\r
3697   HRGN clips[MAX_CLIPS];\r
3698   ChessSquare dragged_piece = EmptySquare;\r
3699   int nr = twoBoards*partnerUp;\r
3700 \r
3701   /* I'm undecided on this - this function figures out whether a full\r
3702    * repaint is necessary on its own, so there's no real reason to have the\r
3703    * caller tell it that.  I think this can safely be set to FALSE - but\r
3704    * if we trust the callers not to request full repaints unnessesarily, then\r
3705    * we could skip some clipping work.  In other words, only request a full\r
3706    * redraw when the majority of pieces have changed positions (ie. flip, \r
3707    * gamestart and similar)  --Hawk\r
3708    */\r
3709   Boolean fullrepaint = repaint;\r
3710 \r
3711   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3712 \r
3713   if( DrawPositionNeedsFullRepaint() ) {\r
3714       fullrepaint = TRUE;\r
3715   }\r
3716 \r
3717   if (board == NULL) {\r
3718     if (!lastReqValid[nr]) {\r
3719       return;\r
3720     }\r
3721     board = lastReq[nr];\r
3722   } else {\r
3723     CopyBoard(lastReq[nr], board);\r
3724     lastReqValid[nr] = 1;\r
3725   }\r
3726 \r
3727   if (doingSizing) {\r
3728     return;\r
3729   }\r
3730 \r
3731   if (IsIconic(hwndMain)) {\r
3732     return;\r
3733   }\r
3734 \r
3735   if (hdc == NULL) {\r
3736     hdc = GetDC(hwndMain);\r
3737     if (!appData.monoMode) {\r
3738       SelectPalette(hdc, hPal, FALSE);\r
3739       RealizePalette(hdc);\r
3740     }\r
3741     releaseDC = TRUE;\r
3742   } else {\r
3743     releaseDC = FALSE;\r
3744   }\r
3745 \r
3746   /* Create some work-DCs */\r
3747   hdcmem = CreateCompatibleDC(hdc);\r
3748   tmphdc = CreateCompatibleDC(hdc);\r
3749 \r
3750   /* If dragging is in progress, we temporarely remove the piece */\r
3751   /* [HGM] or temporarily decrease count if stacked              */\r
3752   /*       !! Moved to before board compare !!                   */\r
3753   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3754     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3755     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3756             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3757         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3758     } else \r
3759     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3760             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3761         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3762     } else \r
3763         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3764   }\r
3765 \r
3766   /* Figure out which squares need updating by comparing the \r
3767    * newest board with the last drawn board and checking if\r
3768    * flipping has changed.\r
3769    */\r
3770   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3771     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3772       for (column = 0; column < BOARD_WIDTH; column++) {\r
3773         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3774           SquareToPos(row, column, &x, &y);\r
3775           clips[num_clips++] =\r
3776             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3777         }\r
3778       }\r
3779     }\r
3780    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3781     for (i=0; i<2; i++) {\r
3782       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3783           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3784         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3785             lastDrawnHighlight.sq[i].y >= 0) {\r
3786           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3787                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3788           clips[num_clips++] =\r
3789             CreateRectRgn(x - lineGap, y - lineGap, \r
3790                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3791         }\r
3792         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3793           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3794           clips[num_clips++] =\r
3795             CreateRectRgn(x - lineGap, y - lineGap, \r
3796                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3797         }\r
3798       }\r
3799     }\r
3800     for (i=0; i<2; i++) {\r
3801       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3802           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3803         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3804             lastDrawnPremove.sq[i].y >= 0) {\r
3805           SquareToPos(lastDrawnPremove.sq[i].y,\r
3806                       lastDrawnPremove.sq[i].x, &x, &y);\r
3807           clips[num_clips++] =\r
3808             CreateRectRgn(x - lineGap, y - lineGap, \r
3809                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3810         }\r
3811         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3812             premoveHighlightInfo.sq[i].y >= 0) {\r
3813           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3814                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3815           clips[num_clips++] =\r
3816             CreateRectRgn(x - lineGap, y - lineGap, \r
3817                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3818         }\r
3819       }\r
3820     }\r
3821    } else { // nr == 1\r
3822         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3823         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3824         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3825         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3826       for (i=0; i<2; i++) {\r
3827         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3828             partnerHighlightInfo.sq[i].y >= 0) {\r
3829           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3830                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3831           clips[num_clips++] =\r
3832             CreateRectRgn(x - lineGap, y - lineGap, \r
3833                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3834         }\r
3835         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3836             oldPartnerHighlight.sq[i].y >= 0) {\r
3837           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3838                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3839           clips[num_clips++] =\r
3840             CreateRectRgn(x - lineGap, y - lineGap, \r
3841                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3842         }\r
3843       }\r
3844    }\r
3845   } else {\r
3846     fullrepaint = TRUE;\r
3847   }\r
3848 \r
3849   /* Create a buffer bitmap - this is the actual bitmap\r
3850    * being written to.  When all the work is done, we can\r
3851    * copy it to the real DC (the screen).  This avoids\r
3852    * the problems with flickering.\r
3853    */\r
3854   GetClientRect(hwndMain, &Rect);\r
3855   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3856                                         Rect.bottom-Rect.top+1);\r
3857   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3858   if (!appData.monoMode) {\r
3859     SelectPalette(hdcmem, hPal, FALSE);\r
3860   }\r
3861 \r
3862   /* Create clips for dragging */\r
3863   if (!fullrepaint) {\r
3864     if (dragInfo.from.x >= 0) {\r
3865       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3866       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3867     }\r
3868     if (dragInfo.start.x >= 0) {\r
3869       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3870       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3871     }\r
3872     if (dragInfo.pos.x >= 0) {\r
3873       x = dragInfo.pos.x - squareSize / 2;\r
3874       y = dragInfo.pos.y - squareSize / 2;\r
3875       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3876     }\r
3877     if (dragInfo.lastpos.x >= 0) {\r
3878       x = dragInfo.lastpos.x - squareSize / 2;\r
3879       y = dragInfo.lastpos.y - squareSize / 2;\r
3880       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3881     }\r
3882   }\r
3883 \r
3884   /* Are we animating a move?  \r
3885    * If so, \r
3886    *   - remove the piece from the board (temporarely)\r
3887    *   - calculate the clipping region\r
3888    */\r
3889   if (!fullrepaint) {\r
3890     if (animInfo.piece != EmptySquare) {\r
3891       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3892       x = boardRect.left + animInfo.lastpos.x;\r
3893       y = boardRect.top + animInfo.lastpos.y;\r
3894       x2 = boardRect.left + animInfo.pos.x;\r
3895       y2 = boardRect.top + animInfo.pos.y;\r
3896       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3897       /* Slight kludge.  The real problem is that after AnimateMove is\r
3898          done, the position on the screen does not match lastDrawn.\r
3899          This currently causes trouble only on e.p. captures in\r
3900          atomic, where the piece moves to an empty square and then\r
3901          explodes.  The old and new positions both had an empty square\r
3902          at the destination, but animation has drawn a piece there and\r
3903          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3904       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3905     }\r
3906   }\r
3907 \r
3908   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3909   if (num_clips == 0)\r
3910     fullrepaint = TRUE;\r
3911 \r
3912   /* Set clipping on the memory DC */\r
3913   if (!fullrepaint) {\r
3914     SelectClipRgn(hdcmem, clips[0]);\r
3915     for (x = 1; x < num_clips; x++) {\r
3916       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3917         abort();  // this should never ever happen!\r
3918     }\r
3919   }\r
3920 \r
3921   /* Do all the drawing to the memory DC */\r
3922   if(explodeInfo.radius) { // [HGM] atomic\r
3923         HBRUSH oldBrush;\r
3924         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3925         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3926         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3927         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3928         x += squareSize/2;\r
3929         y += squareSize/2;\r
3930         if(!fullrepaint) {\r
3931           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3932           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3933         }\r
3934         DrawGridOnDC(hdcmem);\r
3935         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3936         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3937         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3938         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3939         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3940         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3941         SelectObject(hdcmem, oldBrush);\r
3942   } else {\r
3943     if(border) DrawBackgroundOnDC(hdcmem);\r
3944     DrawGridOnDC(hdcmem);\r
3945     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3946         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3947         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3948     } else {\r
3949         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3950         oldPartnerHighlight = partnerHighlightInfo;\r
3951     }\r
3952     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3953   }\r
3954   if(nr == 0) // [HGM] dual: markers only on left board\r
3955   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3956     for (column = 0; column < BOARD_WIDTH; column++) {\r
3957         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3958             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3959             SquareToPos(row, column, &x, &y);\r
3960             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3961                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3962             SelectObject(hdcmem, oldBrush);\r
3963         }\r
3964     }\r
3965   }\r
3966 \r
3967   if( appData.highlightMoveWithArrow ) {\r
3968     DrawArrowHighlight(hdcmem);\r
3969   }\r
3970 \r
3971   DrawCoordsOnDC(hdcmem);\r
3972 \r
3973   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3974                  /* to make sure lastDrawn contains what is actually drawn */\r
3975 \r
3976   /* Put the dragged piece back into place and draw it (out of place!) */\r
3977     if (dragged_piece != EmptySquare) {\r
3978     /* [HGM] or restack */\r
3979     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3980                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3981     else\r
3982     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3983                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3984 \r
3985     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3986     x = dragInfo.pos.x - squareSize / 2;\r
3987     y = dragInfo.pos.y - squareSize / 2;\r
3988     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3989                   ((int) dragInfo.piece < (int) BlackPawn), \r
3990                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3991   }   \r
3992   \r
3993   /* Put the animated piece back into place and draw it */\r
3994   if (animInfo.piece != EmptySquare) {\r
3995     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3996     x = boardRect.left + animInfo.pos.x;\r
3997     y = boardRect.top + animInfo.pos.y;\r
3998     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3999                   ((int) animInfo.piece < (int) BlackPawn),\r
4000                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4001   }\r
4002 \r
4003   /* Release the bufferBitmap by selecting in the old bitmap \r
4004    * and delete the memory DC\r
4005    */\r
4006   SelectObject(hdcmem, oldBitmap);\r
4007   DeleteDC(hdcmem);\r
4008 \r
4009   /* Set clipping on the target DC */\r
4010   if (!fullrepaint) {\r
4011     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4012         RECT rect;\r
4013         GetRgnBox(clips[x], &rect);\r
4014         DeleteObject(clips[x]);\r
4015         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4016                           rect.right + wpMain.width/2, rect.bottom);\r
4017     }\r
4018     SelectClipRgn(hdc, clips[0]);\r
4019     for (x = 1; x < num_clips; x++) {\r
4020       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4021         abort();   // this should never ever happen!\r
4022     } \r
4023   }\r
4024 \r
4025   /* Copy the new bitmap onto the screen in one go.\r
4026    * This way we avoid any flickering\r
4027    */\r
4028   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4029   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4030          boardRect.right - boardRect.left,\r
4031          boardRect.bottom - boardRect.top,\r
4032          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4033   if(saveDiagFlag) { \r
4034     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4035     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4036 \r
4037     GetObject(bufferBitmap, sizeof(b), &b);\r
4038     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4039         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4040         bih.biWidth = b.bmWidth;\r
4041         bih.biHeight = b.bmHeight;\r
4042         bih.biPlanes = 1;\r
4043         bih.biBitCount = b.bmBitsPixel;\r
4044         bih.biCompression = 0;\r
4045         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4046         bih.biXPelsPerMeter = 0;\r
4047         bih.biYPelsPerMeter = 0;\r
4048         bih.biClrUsed = 0;\r
4049         bih.biClrImportant = 0;\r
4050 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4051 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4052         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4053 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4054 \r
4055         wb = b.bmWidthBytes;\r
4056         // count colors\r
4057         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4058                 int k = ((int*) pData)[i];\r
4059                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4060                 if(j >= 16) break;\r
4061                 color[j] = k;\r
4062                 if(j >= nrColors) nrColors = j+1;\r
4063         }\r
4064         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4065                 INT p = 0;\r
4066                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4067                     for(w=0; w<(wb>>2); w+=2) {\r
4068                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4069                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4070                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4071                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4072                         pData[p++] = m | j<<4;\r
4073                     }\r
4074                     while(p&3) pData[p++] = 0;\r
4075                 }\r
4076                 fac = 3;\r
4077                 wb = ((wb+31)>>5)<<2;\r
4078         }\r
4079         // write BITMAPFILEHEADER\r
4080         fprintf(diagFile, "BM");\r
4081         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4082         fputDW(diagFile, 0);\r
4083         fputDW(diagFile, 0x36 + (fac?64:0));\r
4084         // write BITMAPINFOHEADER\r
4085         fputDW(diagFile, 40);\r
4086         fputDW(diagFile, b.bmWidth);\r
4087         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4088         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4089         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4090         fputDW(diagFile, 0);\r
4091         fputDW(diagFile, 0);\r
4092         fputDW(diagFile, 0);\r
4093         fputDW(diagFile, 0);\r
4094         fputDW(diagFile, 0);\r
4095         fputDW(diagFile, 0);\r
4096         // write color table\r
4097         if(fac)\r
4098         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4099         // write bitmap data\r
4100         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4101                 fputc(pData[i], diagFile);\r
4102         free(pData);\r
4103      }\r
4104   }\r
4105 \r
4106   SelectObject(tmphdc, oldBitmap);\r
4107 \r
4108   /* Massive cleanup */\r
4109   for (x = 0; x < num_clips; x++)\r
4110     DeleteObject(clips[x]);\r
4111 \r
4112   DeleteDC(tmphdc);\r
4113   DeleteObject(bufferBitmap);\r
4114 \r
4115   if (releaseDC) \r
4116     ReleaseDC(hwndMain, hdc);\r
4117   \r
4118   if (lastDrawnFlipView != flipView && nr == 0) {\r
4119     if (flipView)\r
4120       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4121     else\r
4122       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4123   }\r
4124 \r
4125 /*  CopyBoard(lastDrawn, board);*/\r
4126   lastDrawnHighlight = highlightInfo;\r
4127   lastDrawnPremove   = premoveHighlightInfo;\r
4128   lastDrawnFlipView = flipView;\r
4129   lastDrawnValid[nr] = 1;\r
4130 }\r
4131 \r
4132 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4133 int\r
4134 SaveDiagram(f)\r
4135      FILE *f;\r
4136 {\r
4137     saveDiagFlag = 1; diagFile = f;\r
4138     HDCDrawPosition(NULL, TRUE, NULL);\r
4139     saveDiagFlag = 0;\r
4140 \r
4141     fclose(f);\r
4142     return TRUE;\r
4143 }\r
4144 \r
4145 \r
4146 /*---------------------------------------------------------------------------*\\r
4147 | CLIENT PAINT PROCEDURE\r
4148 |   This is the main event-handler for the WM_PAINT message.\r
4149 |\r
4150 \*---------------------------------------------------------------------------*/\r
4151 VOID\r
4152 PaintProc(HWND hwnd)\r
4153 {\r
4154   HDC         hdc;\r
4155   PAINTSTRUCT ps;\r
4156   HFONT       oldFont;\r
4157 \r
4158   if((hdc = BeginPaint(hwnd, &ps))) {\r
4159     if (IsIconic(hwnd)) {\r
4160       DrawIcon(hdc, 2, 2, iconCurrent);\r
4161     } else {\r
4162       if (!appData.monoMode) {\r
4163         SelectPalette(hdc, hPal, FALSE);\r
4164         RealizePalette(hdc);\r
4165       }\r
4166       HDCDrawPosition(hdc, 1, NULL);\r
4167       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4168         flipView = !flipView; partnerUp = !partnerUp;\r
4169         HDCDrawPosition(hdc, 1, NULL);\r
4170         flipView = !flipView; partnerUp = !partnerUp;\r
4171       }\r
4172       oldFont =\r
4173         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4174       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4175                  ETO_CLIPPED|ETO_OPAQUE,\r
4176                  &messageRect, messageText, strlen(messageText), NULL);\r
4177       SelectObject(hdc, oldFont);\r
4178       DisplayBothClocks();\r
4179       DisplayLogos();\r
4180     }\r
4181     EndPaint(hwnd,&ps);\r
4182   }\r
4183 \r
4184   return;\r
4185 }\r
4186 \r
4187 \r
4188 /*\r
4189  * If the user selects on a border boundary, return -1; if off the board,\r
4190  *   return -2.  Otherwise map the event coordinate to the square.\r
4191  * The offset boardRect.left or boardRect.top must already have been\r
4192  *   subtracted from x.\r
4193  */\r
4194 int EventToSquare(x, limit)\r
4195      int x, limit;\r
4196 {\r
4197   if (x <= border)\r
4198     return -2;\r
4199   if (x < lineGap + border)\r
4200     return -1;\r
4201   x -= lineGap + border;\r
4202   if ((x % (squareSize + lineGap)) >= squareSize)\r
4203     return -1;\r
4204   x /= (squareSize + lineGap);\r
4205     if (x >= limit)\r
4206     return -2;\r
4207   return x;\r
4208 }\r
4209 \r
4210 typedef struct {\r
4211   char piece;\r
4212   int command;\r
4213   char* name;\r
4214 } DropEnable;\r
4215 \r
4216 DropEnable dropEnables[] = {\r
4217   { 'P', DP_Pawn, N_("Pawn") },\r
4218   { 'N', DP_Knight, N_("Knight") },\r
4219   { 'B', DP_Bishop, N_("Bishop") },\r
4220   { 'R', DP_Rook, N_("Rook") },\r
4221   { 'Q', DP_Queen, N_("Queen") },\r
4222 };\r
4223 \r
4224 VOID\r
4225 SetupDropMenu(HMENU hmenu)\r
4226 {\r
4227   int i, count, enable;\r
4228   char *p;\r
4229   extern char white_holding[], black_holding[];\r
4230   char item[MSG_SIZ];\r
4231 \r
4232   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4233     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4234                dropEnables[i].piece);\r
4235     count = 0;\r
4236     while (p && *p++ == dropEnables[i].piece) count++;\r
4237       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4238     enable = count > 0 || !appData.testLegality\r
4239       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4240                       && !appData.icsActive);\r
4241     ModifyMenu(hmenu, dropEnables[i].command,\r
4242                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4243                dropEnables[i].command, item);\r
4244   }\r
4245 }\r
4246 \r
4247 void DragPieceBegin(int x, int y, Boolean instantly)\r
4248 {\r
4249       dragInfo.lastpos.x = boardRect.left + x;\r
4250       dragInfo.lastpos.y = boardRect.top + y;\r
4251       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4252       dragInfo.from.x = fromX;\r
4253       dragInfo.from.y = fromY;\r
4254       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4255       dragInfo.start = dragInfo.from;\r
4256       SetCapture(hwndMain);\r
4257 }\r
4258 \r
4259 void DragPieceEnd(int x, int y)\r
4260 {\r
4261     ReleaseCapture();\r
4262     dragInfo.start.x = dragInfo.start.y = -1;\r
4263     dragInfo.from = dragInfo.start;\r
4264     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4265 }\r
4266 \r
4267 void ChangeDragPiece(ChessSquare piece)\r
4268 {\r
4269     dragInfo.piece = piece;\r
4270 }\r
4271 \r
4272 /* Event handler for mouse messages */\r
4273 VOID\r
4274 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4275 {\r
4276   int x, y, menuNr;\r
4277   POINT pt;\r
4278   static int recursive = 0;\r
4279   HMENU hmenu;\r
4280   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4281 \r
4282   if (recursive) {\r
4283     if (message == WM_MBUTTONUP) {\r
4284       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4285          to the middle button: we simulate pressing the left button too!\r
4286          */\r
4287       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4288       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4289     }\r
4290     return;\r
4291   }\r
4292   recursive++;\r
4293   \r
4294   pt.x = LOWORD(lParam);\r
4295   pt.y = HIWORD(lParam);\r
4296   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4297   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4298   if (!flipView && y >= 0) {\r
4299     y = BOARD_HEIGHT - 1 - y;\r
4300   }\r
4301   if (flipView && x >= 0) {\r
4302     x = BOARD_WIDTH - 1 - x;\r
4303   }\r
4304 \r
4305   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4306   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4307 \r
4308   switch (message) {\r
4309   case WM_LBUTTONDOWN:\r
4310       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4311         ClockClick(flipClock); break;\r
4312       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4313         ClockClick(!flipClock); break;\r
4314       }\r
4315     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4316       dragInfo.start.x = dragInfo.start.y = -1;\r
4317       dragInfo.from = dragInfo.start;\r
4318     }\r
4319     if(fromX == -1 && frozen) { // not sure where this is for\r
4320                 fromX = fromY = -1; \r
4321       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4322       break;\r
4323     }\r
4324       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4325       DrawPosition(TRUE, NULL);\r
4326     break;\r
4327 \r
4328   case WM_LBUTTONUP:\r
4329       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4330       DrawPosition(TRUE, NULL);\r
4331     break;\r
4332 \r
4333   case WM_MOUSEMOVE:\r
4334     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4335     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4336     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4337     if ((appData.animateDragging || appData.highlightDragging)\r
4338         && (wParam & MK_LBUTTON || dragging == 2)\r
4339         && dragInfo.from.x >= 0) \r
4340     {\r
4341       BOOL full_repaint = FALSE;\r
4342 \r
4343       if (appData.animateDragging) {\r
4344         dragInfo.pos = pt;\r
4345       }\r
4346       if (appData.highlightDragging) {\r
4347         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4348         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4349             full_repaint = TRUE;\r
4350         }\r
4351       }\r
4352       \r
4353       DrawPosition( full_repaint, NULL);\r
4354       \r
4355       dragInfo.lastpos = dragInfo.pos;\r
4356     }\r
4357     break;\r
4358 \r
4359   case WM_MOUSEWHEEL: // [DM]\r
4360     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4361        /* Mouse Wheel is being rolled forward\r
4362         * Play moves forward\r
4363         */\r
4364        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4365                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4366        /* Mouse Wheel is being rolled backward\r
4367         * Play moves backward\r
4368         */\r
4369        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4370                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4371     }\r
4372     break;\r
4373 \r
4374   case WM_MBUTTONUP:\r
4375   case WM_RBUTTONUP:\r
4376     ReleaseCapture();\r
4377     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4378     break;\r
4379  \r
4380   case WM_MBUTTONDOWN:\r
4381   case WM_RBUTTONDOWN:\r
4382     ErrorPopDown();\r
4383     ReleaseCapture();\r
4384     fromX = fromY = -1;\r
4385     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4386     dragInfo.start.x = dragInfo.start.y = -1;\r
4387     dragInfo.from = dragInfo.start;\r
4388     dragInfo.lastpos = dragInfo.pos;\r
4389     if (appData.highlightDragging) {\r
4390       ClearHighlights();\r
4391     }\r
4392     if(y == -2) {\r
4393       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4394       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4395           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4396       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4397           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4398       }\r
4399       break;\r
4400     }\r
4401     DrawPosition(TRUE, NULL);\r
4402 \r
4403     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4404     switch (menuNr) {\r
4405     case 0:\r
4406       if (message == WM_MBUTTONDOWN) {\r
4407         buttonCount = 3;  /* even if system didn't think so */\r
4408         if (wParam & MK_SHIFT) \r
4409           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4410         else\r
4411           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4412       } else { /* message == WM_RBUTTONDOWN */\r
4413         /* Just have one menu, on the right button.  Windows users don't\r
4414            think to try the middle one, and sometimes other software steals\r
4415            it, or it doesn't really exist. */\r
4416         if(gameInfo.variant != VariantShogi)\r
4417             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4418         else\r
4419             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4420       }\r
4421       break;\r
4422     case 2:\r
4423       SetCapture(hwndMain);\r
4424       break;\r
4425     case 1:\r
4426       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4427       SetupDropMenu(hmenu);\r
4428       MenuPopup(hwnd, pt, hmenu, -1);\r
4429     default:\r
4430       break;\r
4431     }\r
4432     break;\r
4433   }\r
4434 \r
4435   recursive--;\r
4436 }\r
4437 \r
4438 /* Preprocess messages for buttons in main window */\r
4439 LRESULT CALLBACK\r
4440 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4441 {\r
4442   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4443   int i, dir;\r
4444 \r
4445   for (i=0; i<N_BUTTONS; i++) {\r
4446     if (buttonDesc[i].id == id) break;\r
4447   }\r
4448   if (i == N_BUTTONS) return 0;\r
4449   switch (message) {\r
4450   case WM_KEYDOWN:\r
4451     switch (wParam) {\r
4452     case VK_LEFT:\r
4453     case VK_RIGHT:\r
4454       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4455       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4456       return TRUE;\r
4457     }\r
4458     break;\r
4459   case WM_CHAR:\r
4460     switch (wParam) {\r
4461     case '\r':\r
4462       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4463       return TRUE;\r
4464     default:\r
4465       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4466         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4467         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4468         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4469         SetFocus(h);\r
4470         SendMessage(h, WM_CHAR, wParam, lParam);\r
4471         return TRUE;\r
4472       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4473         TypeInEvent((char)wParam);\r
4474       }\r
4475       break;\r
4476     }\r
4477     break;\r
4478   }\r
4479   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4480 }\r
4481 \r
4482 static int promoStyle;\r
4483 \r
4484 /* Process messages for Promotion dialog box */\r
4485 LRESULT CALLBACK\r
4486 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4487 {\r
4488   char promoChar;\r
4489 \r
4490   switch (message) {\r
4491   case WM_INITDIALOG: /* message: initialize dialog box */\r
4492     /* Center the dialog over the application window */\r
4493     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4494     Translate(hDlg, DLG_PromotionKing);\r
4495     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4496       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4497        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4498        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4499                SW_SHOW : SW_HIDE);\r
4500     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4501     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4502        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4503          PieceToChar(WhiteAngel) != '~') ||\r
4504         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4505          PieceToChar(BlackAngel) != '~')   ) ?\r
4506                SW_SHOW : SW_HIDE);\r
4507     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4508        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4509          PieceToChar(WhiteMarshall) != '~') ||\r
4510         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4511          PieceToChar(BlackMarshall) != '~')   ) ?\r
4512                SW_SHOW : SW_HIDE);\r
4513     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4514     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4515     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4516     if(promoStyle) {\r
4517         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4518         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4519         SetWindowText(hDlg, "Promote?");\r
4520     }\r
4521     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4522        gameInfo.variant == VariantSuper ?\r
4523                SW_SHOW : SW_HIDE);\r
4524     return TRUE;\r
4525 \r
4526   case WM_COMMAND: /* message: received a command */\r
4527     switch (LOWORD(wParam)) {\r
4528     case IDCANCEL:\r
4529       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4530       ClearHighlights();\r
4531       DrawPosition(FALSE, NULL);\r
4532       return TRUE;\r
4533     case PB_King:\r
4534       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4535       break;\r
4536     case PB_Queen:\r
4537       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4538       break;\r
4539     case PB_Rook:\r
4540       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4541       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4542       break;\r
4543     case PB_Bishop:\r
4544       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4545       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4546       break;\r
4547     case PB_Chancellor:\r
4548       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4549       break;\r
4550     case PB_Archbishop:\r
4551       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4552       break;\r
4553     case PB_Knight:\r
4554       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4555                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4556       break;\r
4557     default:\r
4558       return FALSE;\r
4559     }\r
4560     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4561     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4562     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4563     fromX = fromY = -1;\r
4564     if (!appData.highlightLastMove) {\r
4565       ClearHighlights();\r
4566       DrawPosition(FALSE, NULL);\r
4567     }\r
4568     return TRUE;\r
4569   }\r
4570   return FALSE;\r
4571 }\r
4572 \r
4573 /* Pop up promotion dialog */\r
4574 VOID\r
4575 PromotionPopup(HWND hwnd)\r
4576 {\r
4577   FARPROC lpProc;\r
4578 \r
4579   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4580   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4581     hwnd, (DLGPROC)lpProc);\r
4582   FreeProcInstance(lpProc);\r
4583 }\r
4584 \r
4585 void\r
4586 PromotionPopUp(char choice)\r
4587 {\r
4588   promoStyle = (choice == '+');\r
4589   DrawPosition(TRUE, NULL);\r
4590   PromotionPopup(hwndMain);\r
4591 }\r
4592 \r
4593 VOID\r
4594 LoadGameDialog(HWND hwnd, char* title)\r
4595 {\r
4596   UINT number = 0;\r
4597   FILE *f;\r
4598   char fileTitle[MSG_SIZ];\r
4599   f = OpenFileDialog(hwnd, "rb", "",\r
4600                      appData.oldSaveStyle ? "gam" : "pgn",\r
4601                      GAME_FILT,\r
4602                      title, &number, fileTitle, NULL);\r
4603   if (f != NULL) {\r
4604     cmailMsgLoaded = FALSE;\r
4605     if (number == 0) {\r
4606       int error = GameListBuild(f);\r
4607       if (error) {\r
4608         DisplayError(_("Cannot build game list"), error);\r
4609       } else if (!ListEmpty(&gameList) &&\r
4610                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4611         GameListPopUp(f, fileTitle);\r
4612         return;\r
4613       }\r
4614       GameListDestroy();\r
4615       number = 1;\r
4616     }\r
4617     LoadGame(f, number, fileTitle, FALSE);\r
4618   }\r
4619 }\r
4620 \r
4621 int get_term_width()\r
4622 {\r
4623     HDC hdc;\r
4624     TEXTMETRIC tm;\r
4625     RECT rc;\r
4626     HFONT hfont, hold_font;\r
4627     LOGFONT lf;\r
4628     HWND hText;\r
4629 \r
4630     if (hwndConsole)\r
4631         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4632     else\r
4633         return 79;\r
4634 \r
4635     // get the text metrics\r
4636     hdc = GetDC(hText);\r
4637     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4638     if (consoleCF.dwEffects & CFE_BOLD)\r
4639         lf.lfWeight = FW_BOLD;\r
4640     if (consoleCF.dwEffects & CFE_ITALIC)\r
4641         lf.lfItalic = TRUE;\r
4642     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4643         lf.lfStrikeOut = TRUE;\r
4644     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4645         lf.lfUnderline = TRUE;\r
4646     hfont = CreateFontIndirect(&lf);\r
4647     hold_font = SelectObject(hdc, hfont);\r
4648     GetTextMetrics(hdc, &tm);\r
4649     SelectObject(hdc, hold_font);\r
4650     DeleteObject(hfont);\r
4651     ReleaseDC(hText, hdc);\r
4652 \r
4653     // get the rectangle\r
4654     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4655 \r
4656     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4657 }\r
4658 \r
4659 void UpdateICSWidth(HWND hText)\r
4660 {\r
4661     LONG old_width, new_width;\r
4662 \r
4663     new_width = get_term_width(hText, FALSE);\r
4664     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4665     if (new_width != old_width)\r
4666     {\r
4667         ics_update_width(new_width);\r
4668         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4669     }\r
4670 }\r
4671 \r
4672 VOID\r
4673 ChangedConsoleFont()\r
4674 {\r
4675   CHARFORMAT cfmt;\r
4676   CHARRANGE tmpsel, sel;\r
4677   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4678   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4679   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4680   PARAFORMAT paraf;\r
4681 \r
4682   cfmt.cbSize = sizeof(CHARFORMAT);\r
4683   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4684     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4685                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4686   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4687    * size.  This was undocumented in the version of MSVC++ that I had\r
4688    * when I wrote the code, but is apparently documented now.\r
4689    */\r
4690   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4691   cfmt.bCharSet = f->lf.lfCharSet;\r
4692   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4693   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4694   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4695   /* Why are the following seemingly needed too? */\r
4696   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4697   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4698   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4699   tmpsel.cpMin = 0;\r
4700   tmpsel.cpMax = -1; /*999999?*/\r
4701   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4702   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4703   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4704    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4705    */\r
4706   paraf.cbSize = sizeof(paraf);\r
4707   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4708   paraf.dxStartIndent = 0;\r
4709   paraf.dxOffset = WRAP_INDENT;\r
4710   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4711   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4712   UpdateICSWidth(hText);\r
4713 }\r
4714 \r
4715 /*---------------------------------------------------------------------------*\\r
4716  *\r
4717  * Window Proc for main window\r
4718  *\r
4719 \*---------------------------------------------------------------------------*/\r
4720 \r
4721 /* Process messages for main window, etc. */\r
4722 LRESULT CALLBACK\r
4723 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4724 {\r
4725   FARPROC lpProc;\r
4726   int wmId;\r
4727   char *defName;\r
4728   FILE *f;\r
4729   UINT number;\r
4730   char fileTitle[MSG_SIZ];\r
4731   static SnapData sd;\r
4732   static int peek=0;\r
4733 \r
4734   switch (message) {\r
4735 \r
4736   case WM_PAINT: /* message: repaint portion of window */\r
4737     PaintProc(hwnd);\r
4738     break;\r
4739 \r
4740   case WM_ERASEBKGND:\r
4741     if (IsIconic(hwnd)) {\r
4742       /* Cheat; change the message */\r
4743       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4744     } else {\r
4745       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4746     }\r
4747     break;\r
4748 \r
4749   case WM_LBUTTONDOWN:\r
4750   case WM_MBUTTONDOWN:\r
4751   case WM_RBUTTONDOWN:\r
4752   case WM_LBUTTONUP:\r
4753   case WM_MBUTTONUP:\r
4754   case WM_RBUTTONUP:\r
4755   case WM_MOUSEMOVE:\r
4756   case WM_MOUSEWHEEL:\r
4757     MouseEvent(hwnd, message, wParam, lParam);\r
4758     break;\r
4759 \r
4760   case WM_KEYUP:\r
4761     if((char)wParam == '\b') {\r
4762       ForwardEvent(); peek = 0;\r
4763     }\r
4764 \r
4765     JAWS_KBUP_NAVIGATION\r
4766 \r
4767     break;\r
4768 \r
4769   case WM_KEYDOWN:\r
4770     if((char)wParam == '\b') {\r
4771       if(!peek) BackwardEvent(), peek = 1;\r
4772     }\r
4773 \r
4774     JAWS_KBDOWN_NAVIGATION\r
4775 \r
4776     break;\r
4777 \r
4778   case WM_CHAR:\r
4779     \r
4780     JAWS_ALT_INTERCEPT\r
4781 \r
4782     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4783         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4784         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4785         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4786         SetFocus(h);\r
4787         SendMessage(h, message, wParam, lParam);\r
4788     } else if(lParam != KF_REPEAT) {\r
4789         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4790                 TypeInEvent((char)wParam);\r
4791         } else if((char)wParam == 003) CopyGameToClipboard();\r
4792          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4793     }\r
4794 \r
4795     break;\r
4796 \r
4797   case WM_PALETTECHANGED:\r
4798     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4799       int nnew;\r
4800       HDC hdc = GetDC(hwndMain);\r
4801       SelectPalette(hdc, hPal, TRUE);\r
4802       nnew = RealizePalette(hdc);\r
4803       if (nnew > 0) {\r
4804         paletteChanged = TRUE;\r
4805 \r
4806         InvalidateRect(hwnd, &boardRect, FALSE);\r
4807       }\r
4808       ReleaseDC(hwnd, hdc);\r
4809     }\r
4810     break;\r
4811 \r
4812   case WM_QUERYNEWPALETTE:\r
4813     if (!appData.monoMode /*&& paletteChanged*/) {\r
4814       int nnew;\r
4815       HDC hdc = GetDC(hwndMain);\r
4816       paletteChanged = FALSE;\r
4817       SelectPalette(hdc, hPal, FALSE);\r
4818       nnew = RealizePalette(hdc);\r
4819       if (nnew > 0) {\r
4820         InvalidateRect(hwnd, &boardRect, FALSE);\r
4821       }\r
4822       ReleaseDC(hwnd, hdc);\r
4823       return TRUE;\r
4824     }\r
4825     return FALSE;\r
4826 \r
4827   case WM_COMMAND: /* message: command from application menu */\r
4828     wmId    = LOWORD(wParam);\r
4829 \r
4830     switch (wmId) {\r
4831     case IDM_NewGame:\r
4832       ResetGameEvent();\r
4833       SAY("new game enter a move to play against the computer with white");\r
4834       break;\r
4835 \r
4836     case IDM_NewGameFRC:\r
4837       if( NewGameFRC() == 0 ) {\r
4838         ResetGameEvent();\r
4839       }\r
4840       break;\r
4841 \r
4842     case IDM_NewVariant:\r
4843       NewVariantPopup(hwnd);\r
4844       break;\r
4845 \r
4846     case IDM_LoadGame:\r
4847       LoadGameDialog(hwnd, _("Load Game from File"));\r
4848       break;\r
4849 \r
4850     case IDM_LoadNextGame:\r
4851       ReloadGame(1);\r
4852       break;\r
4853 \r
4854     case IDM_LoadPrevGame:\r
4855       ReloadGame(-1);\r
4856       break;\r
4857 \r
4858     case IDM_ReloadGame:\r
4859       ReloadGame(0);\r
4860       break;\r
4861 \r
4862     case IDM_LoadPosition:\r
4863       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4864         Reset(FALSE, TRUE);\r
4865       }\r
4866       number = 1;\r
4867       f = OpenFileDialog(hwnd, "rb", "",\r
4868                          appData.oldSaveStyle ? "pos" : "fen",\r
4869                          POSITION_FILT,\r
4870                          _("Load Position from File"), &number, fileTitle, NULL);\r
4871       if (f != NULL) {\r
4872         LoadPosition(f, number, fileTitle);\r
4873       }\r
4874       break;\r
4875 \r
4876     case IDM_LoadNextPosition:\r
4877       ReloadPosition(1);\r
4878       break;\r
4879 \r
4880     case IDM_LoadPrevPosition:\r
4881       ReloadPosition(-1);\r
4882       break;\r
4883 \r
4884     case IDM_ReloadPosition:\r
4885       ReloadPosition(0);\r
4886       break;\r
4887 \r
4888     case IDM_SaveGame:\r
4889       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4890       f = OpenFileDialog(hwnd, "a", defName,\r
4891                          appData.oldSaveStyle ? "gam" : "pgn",\r
4892                          GAME_FILT,\r
4893                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4894       if (f != NULL) {\r
4895         SaveGame(f, 0, "");\r
4896       }\r
4897       break;\r
4898 \r
4899     case IDM_SavePosition:\r
4900       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4901       f = OpenFileDialog(hwnd, "a", defName,\r
4902                          appData.oldSaveStyle ? "pos" : "fen",\r
4903                          POSITION_FILT,\r
4904                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4905       if (f != NULL) {\r
4906         SavePosition(f, 0, "");\r
4907       }\r
4908       break;\r
4909 \r
4910     case IDM_SaveDiagram:\r
4911       defName = "diagram";\r
4912       f = OpenFileDialog(hwnd, "wb", defName,\r
4913                          "bmp",\r
4914                          DIAGRAM_FILT,\r
4915                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4916       if (f != NULL) {\r
4917         SaveDiagram(f);\r
4918       }\r
4919       break;\r
4920 \r
4921     case IDM_CreateBook:\r
4922       CreateBookEvent();\r
4923       break;\r
4924 \r
4925     case IDM_CopyGame:\r
4926       CopyGameToClipboard();\r
4927       break;\r
4928 \r
4929     case IDM_PasteGame:\r
4930       PasteGameFromClipboard();\r
4931       break;\r
4932 \r
4933     case IDM_CopyGameListToClipboard:\r
4934       CopyGameListToClipboard();\r
4935       break;\r
4936 \r
4937     /* [AS] Autodetect FEN or PGN data */\r
4938     case IDM_PasteAny:\r
4939       PasteGameOrFENFromClipboard();\r
4940       break;\r
4941 \r
4942     /* [AS] Move history */\r
4943     case IDM_ShowMoveHistory:\r
4944         if( MoveHistoryIsUp() ) {\r
4945             MoveHistoryPopDown();\r
4946         }\r
4947         else {\r
4948             MoveHistoryPopUp();\r
4949         }\r
4950         break;\r
4951 \r
4952     /* [AS] Eval graph */\r
4953     case IDM_ShowEvalGraph:\r
4954         if( EvalGraphIsUp() ) {\r
4955             EvalGraphPopDown();\r
4956         }\r
4957         else {\r
4958             EvalGraphPopUp();\r
4959             SetFocus(hwndMain);\r
4960         }\r
4961         break;\r
4962 \r
4963     /* [AS] Engine output */\r
4964     case IDM_ShowEngineOutput:\r
4965         if( EngineOutputIsUp() ) {\r
4966             EngineOutputPopDown();\r
4967         }\r
4968         else {\r
4969             EngineOutputPopUp();\r
4970         }\r
4971         break;\r
4972 \r
4973     /* [AS] User adjudication */\r
4974     case IDM_UserAdjudication_White:\r
4975         UserAdjudicationEvent( +1 );\r
4976         break;\r
4977 \r
4978     case IDM_UserAdjudication_Black:\r
4979         UserAdjudicationEvent( -1 );\r
4980         break;\r
4981 \r
4982     case IDM_UserAdjudication_Draw:\r
4983         UserAdjudicationEvent( 0 );\r
4984         break;\r
4985 \r
4986     /* [AS] Game list options dialog */\r
4987     case IDM_GameListOptions:\r
4988       GameListOptions();\r
4989       break;\r
4990 \r
4991     case IDM_NewChat:\r
4992       ChatPopUp(NULL);\r
4993       break;\r
4994 \r
4995     case IDM_CopyPosition:\r
4996       CopyFENToClipboard();\r
4997       break;\r
4998 \r
4999     case IDM_PastePosition:\r
5000       PasteFENFromClipboard();\r
5001       break;\r
5002 \r
5003     case IDM_MailMove:\r
5004       MailMoveEvent();\r
5005       break;\r
5006 \r
5007     case IDM_ReloadCMailMsg:\r
5008       Reset(TRUE, TRUE);\r
5009       ReloadCmailMsgEvent(FALSE);\r
5010       break;\r
5011 \r
5012     case IDM_Minimize:\r
5013       ShowWindow(hwnd, SW_MINIMIZE);\r
5014       break;\r
5015 \r
5016     case IDM_Exit:\r
5017       ExitEvent(0);\r
5018       break;\r
5019 \r
5020     case IDM_MachineWhite:\r
5021       MachineWhiteEvent();\r
5022       /*\r
5023        * refresh the tags dialog only if it's visible\r
5024        */\r
5025       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5026           char *tags;\r
5027           tags = PGNTags(&gameInfo);\r
5028           TagsPopUp(tags, CmailMsg());\r
5029           free(tags);\r
5030       }\r
5031       SAY("computer starts playing white");\r
5032       break;\r
5033 \r
5034     case IDM_MachineBlack:\r
5035       MachineBlackEvent();\r
5036       /*\r
5037        * refresh the tags dialog only if it's visible\r
5038        */\r
5039       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5040           char *tags;\r
5041           tags = PGNTags(&gameInfo);\r
5042           TagsPopUp(tags, CmailMsg());\r
5043           free(tags);\r
5044       }\r
5045       SAY("computer starts playing black");\r
5046       break;\r
5047 \r
5048     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5049       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5050       break;\r
5051 \r
5052     case IDM_TwoMachines:\r
5053       TwoMachinesEvent();\r
5054       /*\r
5055        * refresh the tags dialog only if it's visible\r
5056        */\r
5057       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5058           char *tags;\r
5059           tags = PGNTags(&gameInfo);\r
5060           TagsPopUp(tags, CmailMsg());\r
5061           free(tags);\r
5062       }\r
5063       SAY("computer starts playing both sides");\r
5064       break;\r
5065 \r
5066     case IDM_AnalysisMode:\r
5067       if(AnalyzeModeEvent()) {\r
5068         SAY("analyzing current position");\r
5069       }\r
5070       break;\r
5071 \r
5072     case IDM_AnalyzeFile:\r
5073       AnalyzeFileEvent();\r
5074       break;\r
5075 \r
5076     case IDM_IcsClient:\r
5077       IcsClientEvent();\r
5078       break;\r
5079 \r
5080     case IDM_EditGame:\r
5081     case IDM_EditGame2:\r
5082       EditGameEvent();\r
5083       SAY("edit game");\r
5084       break;\r
5085 \r
5086     case IDM_EditPosition:\r
5087     case IDM_EditPosition2:\r
5088       EditPositionEvent();\r
5089       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5090       break;\r
5091 \r
5092     case IDM_Training:\r
5093       TrainingEvent();\r
5094       break;\r
5095 \r
5096     case IDM_ShowGameList:\r
5097       ShowGameListProc();\r
5098       break;\r
5099 \r
5100     case IDM_EditProgs1:\r
5101       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5102       break;\r
5103 \r
5104     case IDM_LoadProg1:\r
5105      LoadEnginePopUp(hwndMain, 0);\r
5106       break;\r
5107 \r
5108     case IDM_LoadProg2:\r
5109      LoadEnginePopUp(hwndMain, 1);\r
5110       break;\r
5111 \r
5112     case IDM_EditServers:\r
5113       EditTagsPopUp(icsNames, &icsNames);\r
5114       break;\r
5115 \r
5116     case IDM_EditTags:\r
5117     case IDM_Tags:\r
5118       EditTagsProc();\r
5119       break;\r
5120 \r
5121     case IDM_EditBook:\r
5122       EditBookEvent();\r
5123       break;\r
5124 \r
5125     case IDM_EditComment:\r
5126     case IDM_Comment:\r
5127       if (commentUp && editComment) {\r
5128         CommentPopDown();\r
5129       } else {\r
5130         EditCommentEvent();\r
5131       }\r
5132       break;\r
5133 \r
5134     case IDM_Pause:\r
5135       PauseEvent();\r
5136       break;\r
5137 \r
5138     case IDM_Accept:\r
5139       AcceptEvent();\r
5140       break;\r
5141 \r
5142     case IDM_Decline:\r
5143       DeclineEvent();\r
5144       break;\r
5145 \r
5146     case IDM_Rematch:\r
5147 \r
5148       RematchEvent();\r
5149       break;\r
5150 \r
5151     case IDM_CallFlag:\r
5152       CallFlagEvent();\r
5153       break;\r
5154 \r
5155     case IDM_Draw:\r
5156       DrawEvent();\r
5157       break;\r
5158 \r
5159     case IDM_Adjourn:\r
5160       AdjournEvent();\r
5161       break;\r
5162 \r
5163     case IDM_Abort:\r
5164       AbortEvent();\r
5165       break;\r
5166 \r
5167     case IDM_Resign:\r
5168       ResignEvent();\r
5169       break;\r
5170 \r
5171     case IDM_StopObserving:\r
5172       StopObservingEvent();\r
5173       break;\r
5174 \r
5175     case IDM_StopExamining:\r
5176       StopExaminingEvent();\r
5177       break;\r
5178 \r
5179     case IDM_Upload:\r
5180       UploadGameEvent();\r
5181       break;\r
5182 \r
5183     case IDM_TypeInMove:\r
5184       TypeInEvent('\000');\r
5185       break;\r
5186 \r
5187     case IDM_TypeInName:\r
5188       PopUpNameDialog('\000');\r
5189       break;\r
5190 \r
5191     case IDM_Backward:\r
5192       BackwardEvent();\r
5193       SetFocus(hwndMain);\r
5194       break;\r
5195 \r
5196     JAWS_MENU_ITEMS\r
5197 \r
5198     case IDM_Forward:\r
5199       ForwardEvent();\r
5200       SetFocus(hwndMain);\r
5201       break;\r
5202 \r
5203     case IDM_ToStart:\r
5204       ToStartEvent();\r
5205       SetFocus(hwndMain);\r
5206       break;\r
5207 \r
5208     case IDM_ToEnd:\r
5209       ToEndEvent();\r
5210       SetFocus(hwndMain);\r
5211       break;\r
5212 \r
5213     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5214     case OPT_GameListPrev:\r
5215       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5216       break;\r
5217 \r
5218     case IDM_Revert:\r
5219       RevertEvent(FALSE);\r
5220       break;\r
5221 \r
5222     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5223       RevertEvent(TRUE);\r
5224       break;\r
5225 \r
5226     case IDM_TruncateGame:\r
5227       TruncateGameEvent();\r
5228       break;\r
5229 \r
5230     case IDM_MoveNow:\r
5231       MoveNowEvent();\r
5232       break;\r
5233 \r
5234     case IDM_RetractMove:\r
5235       RetractMoveEvent();\r
5236       break;\r
5237 \r
5238     case IDM_FlipView:\r
5239       flipView = !flipView;\r
5240       DrawPosition(FALSE, NULL);\r
5241       break;\r
5242 \r
5243     case IDM_FlipClock:\r
5244       flipClock = !flipClock;\r
5245       DisplayBothClocks();\r
5246       DisplayLogos();\r
5247       break;\r
5248 \r
5249     case IDM_MuteSounds:\r
5250       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5251       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5252                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5253       break;\r
5254 \r
5255     case IDM_GeneralOptions:\r
5256       GeneralOptionsPopup(hwnd);\r
5257       DrawPosition(TRUE, NULL);\r
5258       break;\r
5259 \r
5260     case IDM_BoardOptions:\r
5261       BoardOptionsPopup(hwnd);\r
5262       break;\r
5263 \r
5264     case IDM_ThemeOptions:\r
5265       ThemeOptionsPopup(hwnd);\r
5266       break;\r
5267 \r
5268     case IDM_EnginePlayOptions:\r
5269       EnginePlayOptionsPopup(hwnd);\r
5270       break;\r
5271 \r
5272     case IDM_Engine1Options:\r
5273       EngineOptionsPopup(hwnd, &first);\r
5274       break;\r
5275 \r
5276     case IDM_Engine2Options:\r
5277       savedHwnd = hwnd;\r
5278       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5279       EngineOptionsPopup(hwnd, &second);\r
5280       break;\r
5281 \r
5282     case IDM_OptionsUCI:\r
5283       UciOptionsPopup(hwnd);\r
5284       break;\r
5285 \r
5286     case IDM_Tourney:\r
5287       TourneyPopup(hwnd);\r
5288       break;\r
5289 \r
5290     case IDM_IcsOptions:\r
5291       IcsOptionsPopup(hwnd);\r
5292       break;\r
5293 \r
5294     case IDM_Fonts:\r
5295       FontsOptionsPopup(hwnd);\r
5296       break;\r
5297 \r
5298     case IDM_Sounds:\r
5299       SoundOptionsPopup(hwnd);\r
5300       break;\r
5301 \r
5302     case IDM_CommPort:\r
5303       CommPortOptionsPopup(hwnd);\r
5304       break;\r
5305 \r
5306     case IDM_LoadOptions:\r
5307       LoadOptionsPopup(hwnd);\r
5308       break;\r
5309 \r
5310     case IDM_SaveOptions:\r
5311       SaveOptionsPopup(hwnd);\r
5312       break;\r
5313 \r
5314     case IDM_TimeControl:\r
5315       TimeControlOptionsPopup(hwnd);\r
5316       break;\r
5317 \r
5318     case IDM_SaveSettings:\r
5319       SaveSettings(settingsFileName);\r
5320       break;\r
5321 \r
5322     case IDM_SaveSettingsOnExit:\r
5323       saveSettingsOnExit = !saveSettingsOnExit;\r
5324       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5325                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5326                                          MF_CHECKED : MF_UNCHECKED));\r
5327       break;\r
5328 \r
5329     case IDM_Hint:\r
5330       HintEvent();\r
5331       break;\r
5332 \r
5333     case IDM_Book:\r
5334       BookEvent();\r
5335       break;\r
5336 \r
5337     case IDM_AboutGame:\r
5338       AboutGameEvent();\r
5339       break;\r
5340 \r
5341     case IDM_Debug:\r
5342       appData.debugMode = !appData.debugMode;\r
5343       if (appData.debugMode) {\r
5344         char dir[MSG_SIZ];\r
5345         GetCurrentDirectory(MSG_SIZ, dir);\r
5346         SetCurrentDirectory(installDir);\r
5347         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5348         SetCurrentDirectory(dir);\r
5349         setbuf(debugFP, NULL);\r
5350       } else {\r
5351         fclose(debugFP);\r
5352         debugFP = NULL;\r
5353       }\r
5354       break;\r
5355 \r
5356     case IDM_HELPCONTENTS:\r
5357       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5358           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5359           MessageBox (GetFocus(),\r
5360                     _("Unable to activate help"),\r
5361                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5362       }\r
5363       break;\r
5364 \r
5365     case IDM_HELPSEARCH:\r
5366         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5367             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5368         MessageBox (GetFocus(),\r
5369                     _("Unable to activate help"),\r
5370                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5371       }\r
5372       break;\r
5373 \r
5374     case IDM_HELPHELP:\r
5375       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5376         MessageBox (GetFocus(),\r
5377                     _("Unable to activate help"),\r
5378                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5379       }\r
5380       break;\r
5381 \r
5382     case IDM_ABOUT:\r
5383       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5384       DialogBox(hInst, \r
5385         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5386         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5387       FreeProcInstance(lpProc);\r
5388       break;\r
5389 \r
5390     case IDM_DirectCommand1:\r
5391       AskQuestionEvent(_("Direct Command"),\r
5392                        _("Send to chess program:"), "", "1");\r
5393       break;\r
5394     case IDM_DirectCommand2:\r
5395       AskQuestionEvent(_("Direct Command"),\r
5396                        _("Send to second chess program:"), "", "2");\r
5397       break;\r
5398 \r
5399     case EP_WhitePawn:\r
5400       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5401       fromX = fromY = -1;\r
5402       break;\r
5403 \r
5404     case EP_WhiteKnight:\r
5405       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5406       fromX = fromY = -1;\r
5407       break;\r
5408 \r
5409     case EP_WhiteBishop:\r
5410       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5411       fromX = fromY = -1;\r
5412       break;\r
5413 \r
5414     case EP_WhiteRook:\r
5415       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5416       fromX = fromY = -1;\r
5417       break;\r
5418 \r
5419     case EP_WhiteQueen:\r
5420       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5421       fromX = fromY = -1;\r
5422       break;\r
5423 \r
5424     case EP_WhiteFerz:\r
5425       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5426       fromX = fromY = -1;\r
5427       break;\r
5428 \r
5429     case EP_WhiteWazir:\r
5430       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5431       fromX = fromY = -1;\r
5432       break;\r
5433 \r
5434     case EP_WhiteAlfil:\r
5435       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5436       fromX = fromY = -1;\r
5437       break;\r
5438 \r
5439     case EP_WhiteCannon:\r
5440       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5441       fromX = fromY = -1;\r
5442       break;\r
5443 \r
5444     case EP_WhiteCardinal:\r
5445       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5446       fromX = fromY = -1;\r
5447       break;\r
5448 \r
5449     case EP_WhiteMarshall:\r
5450       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5451       fromX = fromY = -1;\r
5452       break;\r
5453 \r
5454     case EP_WhiteKing:\r
5455       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5456       fromX = fromY = -1;\r
5457       break;\r
5458 \r
5459     case EP_BlackPawn:\r
5460       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5461       fromX = fromY = -1;\r
5462       break;\r
5463 \r
5464     case EP_BlackKnight:\r
5465       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5466       fromX = fromY = -1;\r
5467       break;\r
5468 \r
5469     case EP_BlackBishop:\r
5470       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5471       fromX = fromY = -1;\r
5472       break;\r
5473 \r
5474     case EP_BlackRook:\r
5475       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5476       fromX = fromY = -1;\r
5477       break;\r
5478 \r
5479     case EP_BlackQueen:\r
5480       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5481       fromX = fromY = -1;\r
5482       break;\r
5483 \r
5484     case EP_BlackFerz:\r
5485       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5486       fromX = fromY = -1;\r
5487       break;\r
5488 \r
5489     case EP_BlackWazir:\r
5490       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5491       fromX = fromY = -1;\r
5492       break;\r
5493 \r
5494     case EP_BlackAlfil:\r
5495       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5496       fromX = fromY = -1;\r
5497       break;\r
5498 \r
5499     case EP_BlackCannon:\r
5500       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5501       fromX = fromY = -1;\r
5502       break;\r
5503 \r
5504     case EP_BlackCardinal:\r
5505       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5506       fromX = fromY = -1;\r
5507       break;\r
5508 \r
5509     case EP_BlackMarshall:\r
5510       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5511       fromX = fromY = -1;\r
5512       break;\r
5513 \r
5514     case EP_BlackKing:\r
5515       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5516       fromX = fromY = -1;\r
5517       break;\r
5518 \r
5519     case EP_EmptySquare:\r
5520       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5521       fromX = fromY = -1;\r
5522       break;\r
5523 \r
5524     case EP_ClearBoard:\r
5525       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5526       fromX = fromY = -1;\r
5527       break;\r
5528 \r
5529     case EP_White:\r
5530       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5531       fromX = fromY = -1;\r
5532       break;\r
5533 \r
5534     case EP_Black:\r
5535       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5536       fromX = fromY = -1;\r
5537       break;\r
5538 \r
5539     case EP_Promote:\r
5540       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5541       fromX = fromY = -1;\r
5542       break;\r
5543 \r
5544     case EP_Demote:\r
5545       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5546       fromX = fromY = -1;\r
5547       break;\r
5548 \r
5549     case DP_Pawn:\r
5550       DropMenuEvent(WhitePawn, fromX, fromY);\r
5551       fromX = fromY = -1;\r
5552       break;\r
5553 \r
5554     case DP_Knight:\r
5555       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5556       fromX = fromY = -1;\r
5557       break;\r
5558 \r
5559     case DP_Bishop:\r
5560       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5561       fromX = fromY = -1;\r
5562       break;\r
5563 \r
5564     case DP_Rook:\r
5565       DropMenuEvent(WhiteRook, fromX, fromY);\r
5566       fromX = fromY = -1;\r
5567       break;\r
5568 \r
5569     case DP_Queen:\r
5570       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5571       fromX = fromY = -1;\r
5572       break;\r
5573 \r
5574     case IDM_English:\r
5575       barbaric = 0; appData.language = "";\r
5576       TranslateMenus(0);\r
5577       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5578       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5579       lastChecked = wmId;\r
5580       break;\r
5581 \r
5582     default:\r
5583       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5584           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5585       else\r
5586       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5587           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5588           TranslateMenus(0);\r
5589           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5590           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5591           lastChecked = wmId;\r
5592           break;\r
5593       }\r
5594       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5595     }\r
5596     break;\r
5597 \r
5598   case WM_TIMER:\r
5599     switch (wParam) {\r
5600     case CLOCK_TIMER_ID:\r
5601       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5602       clockTimerEvent = 0;\r
5603       DecrementClocks(); /* call into back end */\r
5604       break;\r
5605     case LOAD_GAME_TIMER_ID:\r
5606       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5607       loadGameTimerEvent = 0;\r
5608       AutoPlayGameLoop(); /* call into back end */\r
5609       break;\r
5610     case ANALYSIS_TIMER_ID:\r
5611       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5612                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5613         AnalysisPeriodicEvent(0);\r
5614       } else {\r
5615         KillTimer(hwnd, analysisTimerEvent);\r
5616         analysisTimerEvent = 0;\r
5617       }\r
5618       break;\r
5619     case DELAYED_TIMER_ID:\r
5620       KillTimer(hwnd, delayedTimerEvent);\r
5621       delayedTimerEvent = 0;\r
5622       delayedTimerCallback();\r
5623       break;\r
5624     }\r
5625     break;\r
5626 \r
5627   case WM_USER_Input:\r
5628     InputEvent(hwnd, message, wParam, lParam);\r
5629     break;\r
5630 \r
5631   /* [AS] Also move "attached" child windows */\r
5632   case WM_WINDOWPOSCHANGING:\r
5633 \r
5634     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5635         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5636 \r
5637         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5638             /* Window is moving */\r
5639             RECT rcMain;\r
5640 \r
5641 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5642             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5643             rcMain.right  = wpMain.x + wpMain.width;\r
5644             rcMain.top    = wpMain.y;\r
5645             rcMain.bottom = wpMain.y + wpMain.height;\r
5646             \r
5647             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5648             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5649             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5650             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5651             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5652             wpMain.x = lpwp->x;\r
5653             wpMain.y = lpwp->y;\r
5654         }\r
5655     }\r
5656     break;\r
5657 \r
5658   /* [AS] Snapping */\r
5659   case WM_ENTERSIZEMOVE:\r
5660     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5661     if (hwnd == hwndMain) {\r
5662       doingSizing = TRUE;\r
5663       lastSizing = 0;\r
5664     }\r
5665     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5666     break;\r
5667 \r
5668   case WM_SIZING:\r
5669     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5670     if (hwnd == hwndMain) {\r
5671       lastSizing = wParam;\r
5672     }\r
5673     break;\r
5674 \r
5675   case WM_MOVING:\r
5676     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5677       return OnMoving( &sd, hwnd, wParam, lParam );\r
5678 \r
5679   case WM_EXITSIZEMOVE:\r
5680     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5681     if (hwnd == hwndMain) {\r
5682       RECT client;\r
5683       doingSizing = FALSE;\r
5684       InvalidateRect(hwnd, &boardRect, FALSE);\r
5685       GetClientRect(hwnd, &client);\r
5686       ResizeBoard(client.right, client.bottom, lastSizing);\r
5687       lastSizing = 0;\r
5688       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5689     }\r
5690     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5691     break;\r
5692 \r
5693   case WM_DESTROY: /* message: window being destroyed */\r
5694     PostQuitMessage(0);\r
5695     break;\r
5696 \r
5697   case WM_CLOSE:\r
5698     if (hwnd == hwndMain) {\r
5699       ExitEvent(0);\r
5700     }\r
5701     break;\r
5702 \r
5703   default:      /* Passes it on if unprocessed */\r
5704     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5705   }\r
5706   return 0;\r
5707 }\r
5708 \r
5709 /*---------------------------------------------------------------------------*\\r
5710  *\r
5711  * Misc utility routines\r
5712  *\r
5713 \*---------------------------------------------------------------------------*/\r
5714 \r
5715 /*\r
5716  * Decent random number generator, at least not as bad as Windows\r
5717  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5718  */\r
5719 unsigned int randstate;\r
5720 \r
5721 int\r
5722 myrandom(void)\r
5723 {\r
5724   randstate = randstate * 1664525 + 1013904223;\r
5725   return (int) randstate & 0x7fffffff;\r
5726 }\r
5727 \r
5728 void\r
5729 mysrandom(unsigned int seed)\r
5730 {\r
5731   randstate = seed;\r
5732 }\r
5733 \r
5734 \r
5735 /* \r
5736  * returns TRUE if user selects a different color, FALSE otherwise \r
5737  */\r
5738 \r
5739 BOOL\r
5740 ChangeColor(HWND hwnd, COLORREF *which)\r
5741 {\r
5742   static BOOL firstTime = TRUE;\r
5743   static DWORD customColors[16];\r
5744   CHOOSECOLOR cc;\r
5745   COLORREF newcolor;\r
5746   int i;\r
5747   ColorClass ccl;\r
5748 \r
5749   if (firstTime) {\r
5750     /* Make initial colors in use available as custom colors */\r
5751     /* Should we put the compiled-in defaults here instead? */\r
5752     i = 0;\r
5753     customColors[i++] = lightSquareColor & 0xffffff;\r
5754     customColors[i++] = darkSquareColor & 0xffffff;\r
5755     customColors[i++] = whitePieceColor & 0xffffff;\r
5756     customColors[i++] = blackPieceColor & 0xffffff;\r
5757     customColors[i++] = highlightSquareColor & 0xffffff;\r
5758     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5759 \r
5760     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5761       customColors[i++] = textAttribs[ccl].color;\r
5762     }\r
5763     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5764     firstTime = FALSE;\r
5765   }\r
5766 \r
5767   cc.lStructSize = sizeof(cc);\r
5768   cc.hwndOwner = hwnd;\r
5769   cc.hInstance = NULL;\r
5770   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5771   cc.lpCustColors = (LPDWORD) customColors;\r
5772   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5773 \r
5774   if (!ChooseColor(&cc)) return FALSE;\r
5775 \r
5776   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5777   if (newcolor == *which) return FALSE;\r
5778   *which = newcolor;\r
5779   return TRUE;\r
5780 \r
5781   /*\r
5782   InitDrawingColors();\r
5783   InvalidateRect(hwnd, &boardRect, FALSE);\r
5784   */\r
5785 }\r
5786 \r
5787 BOOLEAN\r
5788 MyLoadSound(MySound *ms)\r
5789 {\r
5790   BOOL ok = FALSE;\r
5791   struct stat st;\r
5792   FILE *f;\r
5793 \r
5794   if (ms->data && ms->flag) free(ms->data);\r
5795   ms->data = NULL;\r
5796 \r
5797   switch (ms->name[0]) {\r
5798   case NULLCHAR:\r
5799     /* Silence */\r
5800     ok = TRUE;\r
5801     break;\r
5802   case '$':\r
5803     /* System sound from Control Panel.  Don't preload here. */\r
5804     ok = TRUE;\r
5805     break;\r
5806   case '!':\r
5807     if (ms->name[1] == NULLCHAR) {\r
5808       /* "!" alone = silence */\r
5809       ok = TRUE;\r
5810     } else {\r
5811       /* Builtin wave resource.  Error if not found. */\r
5812       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5813       if (h == NULL) break;\r
5814       ms->data = (void *)LoadResource(hInst, h);\r
5815       ms->flag = 0; // not maloced, so cannot be freed!\r
5816       if (h == NULL) break;\r
5817       ok = TRUE;\r
5818     }\r
5819     break;\r
5820   default:\r
5821     /* .wav file.  Error if not found. */\r
5822     f = fopen(ms->name, "rb");\r
5823     if (f == NULL) break;\r
5824     if (fstat(fileno(f), &st) < 0) break;\r
5825     ms->data = malloc(st.st_size);\r
5826     ms->flag = 1;\r
5827     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5828     fclose(f);\r
5829     ok = TRUE;\r
5830     break;\r
5831   }\r
5832   if (!ok) {\r
5833     char buf[MSG_SIZ];\r
5834       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5835     DisplayError(buf, GetLastError());\r
5836   }\r
5837   return ok;\r
5838 }\r
5839 \r
5840 BOOLEAN\r
5841 MyPlaySound(MySound *ms)\r
5842 {\r
5843   BOOLEAN ok = FALSE;\r
5844 \r
5845   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5846   switch (ms->name[0]) {\r
5847   case NULLCHAR:\r
5848         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5849     /* Silence */\r
5850     ok = TRUE;\r
5851     break;\r
5852   case '$':\r
5853     /* System sound from Control Panel (deprecated feature).\r
5854        "$" alone or an unset sound name gets default beep (still in use). */\r
5855     if (ms->name[1]) {\r
5856       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5857     }\r
5858     if (!ok) ok = MessageBeep(MB_OK);\r
5859     break; \r
5860   case '!':\r
5861     /* Builtin wave resource, or "!" alone for silence */\r
5862     if (ms->name[1]) {\r
5863       if (ms->data == NULL) return FALSE;\r
5864       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5865     } else {\r
5866       ok = TRUE;\r
5867     }\r
5868     break;\r
5869   default:\r
5870     /* .wav file.  Error if not found. */\r
5871     if (ms->data == NULL) return FALSE;\r
5872     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5873     break;\r
5874   }\r
5875   /* Don't print an error: this can happen innocently if the sound driver\r
5876      is busy; for instance, if another instance of WinBoard is playing\r
5877      a sound at about the same time. */\r
5878   return ok;\r
5879 }\r
5880 \r
5881 \r
5882 LRESULT CALLBACK\r
5883 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5884 {\r
5885   BOOL ok;\r
5886   OPENFILENAME *ofn;\r
5887   static UINT *number; /* gross that this is static */\r
5888 \r
5889   switch (message) {\r
5890   case WM_INITDIALOG: /* message: initialize dialog box */\r
5891     /* Center the dialog over the application window */\r
5892     ofn = (OPENFILENAME *) lParam;\r
5893     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5894       number = (UINT *) ofn->lCustData;\r
5895       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5896     } else {\r
5897       number = NULL;\r
5898     }\r
5899     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5900     Translate(hDlg, 1536);\r
5901     return FALSE;  /* Allow for further processing */\r
5902 \r
5903   case WM_COMMAND:\r
5904     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5905       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5906     }\r
5907     return FALSE;  /* Allow for further processing */\r
5908   }\r
5909   return FALSE;\r
5910 }\r
5911 \r
5912 UINT APIENTRY\r
5913 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5914 {\r
5915   static UINT *number;\r
5916   OPENFILENAME *ofname;\r
5917   OFNOTIFY *ofnot;\r
5918   switch (uiMsg) {\r
5919   case WM_INITDIALOG:\r
5920     Translate(hdlg, DLG_IndexNumber);\r
5921     ofname = (OPENFILENAME *)lParam;\r
5922     number = (UINT *)(ofname->lCustData);\r
5923     break;\r
5924   case WM_NOTIFY:\r
5925     ofnot = (OFNOTIFY *)lParam;\r
5926     if (ofnot->hdr.code == CDN_FILEOK) {\r
5927       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5928     }\r
5929     break;\r
5930   }\r
5931   return 0;\r
5932 }\r
5933 \r
5934 \r
5935 FILE *\r
5936 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5937                char *nameFilt, char *dlgTitle, UINT *number,\r
5938                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5939 {\r
5940   OPENFILENAME openFileName;\r
5941   char buf1[MSG_SIZ];\r
5942   FILE *f;\r
5943 \r
5944   if (fileName == NULL) fileName = buf1;\r
5945   if (defName == NULL) {\r
5946     safeStrCpy(fileName, "*.", 3 );\r
5947     strcat(fileName, defExt);\r
5948   } else {\r
5949     safeStrCpy(fileName, defName, MSG_SIZ );\r
5950   }\r
5951     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5952   if (number) *number = 0;\r
5953 \r
5954   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5955   openFileName.hwndOwner         = hwnd;\r
5956   openFileName.hInstance         = (HANDLE) hInst;\r
5957   openFileName.lpstrFilter       = nameFilt;\r
5958   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5959   openFileName.nMaxCustFilter    = 0L;\r
5960   openFileName.nFilterIndex      = 1L;\r
5961   openFileName.lpstrFile         = fileName;\r
5962   openFileName.nMaxFile          = MSG_SIZ;\r
5963   openFileName.lpstrFileTitle    = fileTitle;\r
5964   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5965   openFileName.lpstrInitialDir   = NULL;\r
5966   openFileName.lpstrTitle        = dlgTitle;\r
5967   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5968     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5969     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5970     | (oldDialog ? 0 : OFN_EXPLORER);\r
5971   openFileName.nFileOffset       = 0;\r
5972   openFileName.nFileExtension    = 0;\r
5973   openFileName.lpstrDefExt       = defExt;\r
5974   openFileName.lCustData         = (LONG) number;\r
5975   openFileName.lpfnHook          = oldDialog ?\r
5976     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5977   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5978 \r
5979   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5980                         GetOpenFileName(&openFileName)) {\r
5981     /* open the file */\r
5982     f = fopen(openFileName.lpstrFile, write);\r
5983     if (f == NULL) {\r
5984       MessageBox(hwnd, _("File open failed"), NULL,\r
5985                  MB_OK|MB_ICONEXCLAMATION);\r
5986       return NULL;\r
5987     }\r
5988   } else {\r
5989     int err = CommDlgExtendedError();\r
5990     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5991     return FALSE;\r
5992   }\r
5993   return f;\r
5994 }\r
5995 \r
5996 \r
5997 \r
5998 VOID APIENTRY\r
5999 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6000 {\r
6001   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6002 \r
6003   /*\r
6004    * Get the first pop-up menu in the menu template. This is the\r
6005    * menu that TrackPopupMenu displays.\r
6006    */\r
6007   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6008   TranslateOneMenu(10, hmenuTrackPopup);\r
6009 \r
6010   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6011 \r
6012   /*\r
6013    * TrackPopup uses screen coordinates, so convert the\r
6014    * coordinates of the mouse click to screen coordinates.\r
6015    */\r
6016   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6017 \r
6018   /* Draw and track the floating pop-up menu. */\r
6019   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6020                  pt.x, pt.y, 0, hwnd, NULL);\r
6021 \r
6022   /* Destroy the menu.*/\r
6023   DestroyMenu(hmenu);\r
6024 }\r
6025    \r
6026 typedef struct {\r
6027   HWND hDlg, hText;\r
6028   int sizeX, sizeY, newSizeX, newSizeY;\r
6029   HDWP hdwp;\r
6030 } ResizeEditPlusButtonsClosure;\r
6031 \r
6032 BOOL CALLBACK\r
6033 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6034 {\r
6035   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6036   RECT rect;\r
6037   POINT pt;\r
6038 \r
6039   if (hChild == cl->hText) return TRUE;\r
6040   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6041   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6042   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6043   ScreenToClient(cl->hDlg, &pt);\r
6044   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6045     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6046   return TRUE;\r
6047 }\r
6048 \r
6049 /* Resize a dialog that has a (rich) edit field filling most of\r
6050    the top, with a row of buttons below */\r
6051 VOID\r
6052 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6053 {\r
6054   RECT rectText;\r
6055   int newTextHeight, newTextWidth;\r
6056   ResizeEditPlusButtonsClosure cl;\r
6057   \r
6058   /*if (IsIconic(hDlg)) return;*/\r
6059   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6060   \r
6061   cl.hdwp = BeginDeferWindowPos(8);\r
6062 \r
6063   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6064   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6065   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6066   if (newTextHeight < 0) {\r
6067     newSizeY += -newTextHeight;\r
6068     newTextHeight = 0;\r
6069   }\r
6070   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6071     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6072 \r
6073   cl.hDlg = hDlg;\r
6074   cl.hText = hText;\r
6075   cl.sizeX = sizeX;\r
6076   cl.sizeY = sizeY;\r
6077   cl.newSizeX = newSizeX;\r
6078   cl.newSizeY = newSizeY;\r
6079   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6080 \r
6081   EndDeferWindowPos(cl.hdwp);\r
6082 }\r
6083 \r
6084 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6085 {\r
6086     RECT    rChild, rParent;\r
6087     int     wChild, hChild, wParent, hParent;\r
6088     int     wScreen, hScreen, xNew, yNew;\r
6089     HDC     hdc;\r
6090 \r
6091     /* Get the Height and Width of the child window */\r
6092     GetWindowRect (hwndChild, &rChild);\r
6093     wChild = rChild.right - rChild.left;\r
6094     hChild = rChild.bottom - rChild.top;\r
6095 \r
6096     /* Get the Height and Width of the parent window */\r
6097     GetWindowRect (hwndParent, &rParent);\r
6098     wParent = rParent.right - rParent.left;\r
6099     hParent = rParent.bottom - rParent.top;\r
6100 \r
6101     /* Get the display limits */\r
6102     hdc = GetDC (hwndChild);\r
6103     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6104     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6105     ReleaseDC(hwndChild, hdc);\r
6106 \r
6107     /* Calculate new X position, then adjust for screen */\r
6108     xNew = rParent.left + ((wParent - wChild) /2);\r
6109     if (xNew < 0) {\r
6110         xNew = 0;\r
6111     } else if ((xNew+wChild) > wScreen) {\r
6112         xNew = wScreen - wChild;\r
6113     }\r
6114 \r
6115     /* Calculate new Y position, then adjust for screen */\r
6116     if( mode == 0 ) {\r
6117         yNew = rParent.top  + ((hParent - hChild) /2);\r
6118     }\r
6119     else {\r
6120         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6121     }\r
6122 \r
6123     if (yNew < 0) {\r
6124         yNew = 0;\r
6125     } else if ((yNew+hChild) > hScreen) {\r
6126         yNew = hScreen - hChild;\r
6127     }\r
6128 \r
6129     /* Set it, and return */\r
6130     return SetWindowPos (hwndChild, NULL,\r
6131                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6132 }\r
6133 \r
6134 /* Center one window over another */\r
6135 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6136 {\r
6137     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6138 }\r
6139 \r
6140 /*---------------------------------------------------------------------------*\\r
6141  *\r
6142  * Startup Dialog functions\r
6143  *\r
6144 \*---------------------------------------------------------------------------*/\r
6145 void\r
6146 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6147 {\r
6148   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6149 \r
6150   while (*cd != NULL) {\r
6151     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6152     cd++;\r
6153   }\r
6154 }\r
6155 \r
6156 void\r
6157 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6158 {\r
6159   char buf1[MAX_ARG_LEN];\r
6160   int len;\r
6161 \r
6162   if (str[0] == '@') {\r
6163     FILE* f = fopen(str + 1, "r");\r
6164     if (f == NULL) {\r
6165       DisplayFatalError(str + 1, errno, 2);\r
6166       return;\r
6167     }\r
6168     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6169     fclose(f);\r
6170     buf1[len] = NULLCHAR;\r
6171     str = buf1;\r
6172   }\r
6173 \r
6174   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6175 \r
6176   for (;;) {\r
6177     char buf[MSG_SIZ];\r
6178     char *end = strchr(str, '\n');\r
6179     if (end == NULL) return;\r
6180     memcpy(buf, str, end - str);\r
6181     buf[end - str] = NULLCHAR;\r
6182     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6183     str = end + 1;\r
6184   }\r
6185 }\r
6186 \r
6187 void\r
6188 SetStartupDialogEnables(HWND hDlg)\r
6189 {\r
6190   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6191     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6192     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6193   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6194     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6195   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6196     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6197   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6198     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6199   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6200     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6201     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6202     IsDlgButtonChecked(hDlg, OPT_View));\r
6203 }\r
6204 \r
6205 char *\r
6206 QuoteForFilename(char *filename)\r
6207 {\r
6208   int dquote, space;\r
6209   dquote = strchr(filename, '"') != NULL;\r
6210   space = strchr(filename, ' ') != NULL;\r
6211   if (dquote || space) {\r
6212     if (dquote) {\r
6213       return "'";\r
6214     } else {\r
6215       return "\"";\r
6216     }\r
6217   } else {\r
6218     return "";\r
6219   }\r
6220 }\r
6221 \r
6222 VOID\r
6223 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6224 {\r
6225   char buf[MSG_SIZ];\r
6226   char *q;\r
6227 \r
6228   InitComboStringsFromOption(hwndCombo, nthnames);\r
6229   q = QuoteForFilename(nthcp);\r
6230     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6231   if (*nthdir != NULLCHAR) {\r
6232     q = QuoteForFilename(nthdir);\r
6233       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6234   }\r
6235   if (*nthcp == NULLCHAR) {\r
6236     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6237   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6238     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6239     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6240   }\r
6241 }\r
6242 \r
6243 LRESULT CALLBACK\r
6244 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6245 {\r
6246   char buf[MSG_SIZ];\r
6247   HANDLE hwndCombo;\r
6248   char *p;\r
6249 \r
6250   switch (message) {\r
6251   case WM_INITDIALOG:\r
6252     /* Center the dialog */\r
6253     CenterWindow (hDlg, GetDesktopWindow());\r
6254     Translate(hDlg, DLG_Startup);\r
6255     /* Initialize the dialog items */\r
6256     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6257                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6258                   firstChessProgramNames);\r
6259     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6260                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6261                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6262     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6263     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6264       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6265     if (*appData.icsHelper != NULLCHAR) {\r
6266       char *q = QuoteForFilename(appData.icsHelper);\r
6267       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6268     }\r
6269     if (*appData.icsHost == NULLCHAR) {\r
6270       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6271       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6272     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6273       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6274       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6275     }\r
6276 \r
6277     if (appData.icsActive) {\r
6278       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6279     }\r
6280     else if (appData.noChessProgram) {\r
6281       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6282     }\r
6283     else {\r
6284       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6285     }\r
6286 \r
6287     SetStartupDialogEnables(hDlg);\r
6288     return TRUE;\r
6289 \r
6290   case WM_COMMAND:\r
6291     switch (LOWORD(wParam)) {\r
6292     case IDOK:\r
6293       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6294         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6295         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6296         p = buf;\r
6297         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6298         ParseArgs(StringGet, &p);\r
6299         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6300         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6301         p = buf;\r
6302         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6303         ParseArgs(StringGet, &p);\r
6304         SwapEngines(singleList); // ... and then make it 'second'\r
6305 \r
6306         appData.noChessProgram = FALSE;\r
6307         appData.icsActive = FALSE;\r
6308       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6309         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6310         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6311         p = buf;\r
6312         ParseArgs(StringGet, &p);\r
6313         if (appData.zippyPlay) {\r
6314           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6315           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6316           p = buf;\r
6317           ParseArgs(StringGet, &p);\r
6318         }\r
6319       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6320         appData.noChessProgram = TRUE;\r
6321         appData.icsActive = FALSE;\r
6322       } else {\r
6323         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6324                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6325         return TRUE;\r
6326       }\r
6327       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6328         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6329         p = buf;\r
6330         ParseArgs(StringGet, &p);\r
6331       }\r
6332       EndDialog(hDlg, TRUE);\r
6333       return TRUE;\r
6334 \r
6335     case IDCANCEL:\r
6336       ExitEvent(0);\r
6337       return TRUE;\r
6338 \r
6339     case IDM_HELPCONTENTS:\r
6340       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6341         MessageBox (GetFocus(),\r
6342                     _("Unable to activate help"),\r
6343                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6344       }\r
6345       break;\r
6346 \r
6347     default:\r
6348       SetStartupDialogEnables(hDlg);\r
6349       break;\r
6350     }\r
6351     break;\r
6352   }\r
6353   return FALSE;\r
6354 }\r
6355 \r
6356 /*---------------------------------------------------------------------------*\\r
6357  *\r
6358  * About box dialog functions\r
6359  *\r
6360 \*---------------------------------------------------------------------------*/\r
6361 \r
6362 /* Process messages for "About" dialog box */\r
6363 LRESULT CALLBACK\r
6364 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6365 {\r
6366   switch (message) {\r
6367   case WM_INITDIALOG: /* message: initialize dialog box */\r
6368     /* Center the dialog over the application window */\r
6369     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6370     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6371     Translate(hDlg, ABOUTBOX);\r
6372     JAWS_COPYRIGHT\r
6373     return (TRUE);\r
6374 \r
6375   case WM_COMMAND: /* message: received a command */\r
6376     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6377         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6378       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6379       return (TRUE);\r
6380     }\r
6381     break;\r
6382   }\r
6383   return (FALSE);\r
6384 }\r
6385 \r
6386 /*---------------------------------------------------------------------------*\\r
6387  *\r
6388  * Comment Dialog functions\r
6389  *\r
6390 \*---------------------------------------------------------------------------*/\r
6391 \r
6392 LRESULT CALLBACK\r
6393 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6394 {\r
6395   static HANDLE hwndText = NULL;\r
6396   int len, newSizeX, newSizeY;\r
6397   static int sizeX, sizeY;\r
6398   char *str;\r
6399   RECT rect;\r
6400   MINMAXINFO *mmi;\r
6401 \r
6402   switch (message) {\r
6403   case WM_INITDIALOG: /* message: initialize dialog box */\r
6404     /* Initialize the dialog items */\r
6405     Translate(hDlg, DLG_EditComment);\r
6406     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6407     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6408     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6409     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6410     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6411     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6412     SetWindowText(hDlg, commentTitle);\r
6413     if (editComment) {\r
6414       SetFocus(hwndText);\r
6415     } else {\r
6416       SetFocus(GetDlgItem(hDlg, IDOK));\r
6417     }\r
6418     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6419                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6420                 MAKELPARAM(FALSE, 0));\r
6421     /* Size and position the dialog */\r
6422     if (!commentDialog) {\r
6423       commentDialog = hDlg;\r
6424       GetClientRect(hDlg, &rect);\r
6425       sizeX = rect.right;\r
6426       sizeY = rect.bottom;\r
6427       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6428           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6429         WINDOWPLACEMENT wp;\r
6430         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6431         wp.length = sizeof(WINDOWPLACEMENT);\r
6432         wp.flags = 0;\r
6433         wp.showCmd = SW_SHOW;\r
6434         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6435         wp.rcNormalPosition.left = wpComment.x;\r
6436         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6437         wp.rcNormalPosition.top = wpComment.y;\r
6438         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6439         SetWindowPlacement(hDlg, &wp);\r
6440 \r
6441         GetClientRect(hDlg, &rect);\r
6442         newSizeX = rect.right;\r
6443         newSizeY = rect.bottom;\r
6444         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6445                               newSizeX, newSizeY);\r
6446         sizeX = newSizeX;\r
6447         sizeY = newSizeY;\r
6448       }\r
6449     }\r
6450     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6451     return FALSE;\r
6452 \r
6453   case WM_COMMAND: /* message: received a command */\r
6454     switch (LOWORD(wParam)) {\r
6455     case IDOK:\r
6456       if (editComment) {\r
6457         char *p, *q;\r
6458         /* Read changed options from the dialog box */\r
6459         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6460         len = GetWindowTextLength(hwndText);\r
6461         str = (char *) malloc(len + 1);\r
6462         GetWindowText(hwndText, str, len + 1);\r
6463         p = q = str;\r
6464         while (*q) {\r
6465           if (*q == '\r')\r
6466             q++;\r
6467           else\r
6468             *p++ = *q++;\r
6469         }\r
6470         *p = NULLCHAR;\r
6471         ReplaceComment(commentIndex, str);\r
6472         free(str);\r
6473       }\r
6474       CommentPopDown();\r
6475       return TRUE;\r
6476 \r
6477     case IDCANCEL:\r
6478     case OPT_CancelComment:\r
6479       CommentPopDown();\r
6480       return TRUE;\r
6481 \r
6482     case OPT_ClearComment:\r
6483       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6484       break;\r
6485 \r
6486     case OPT_EditComment:\r
6487       EditCommentEvent();\r
6488       return TRUE;\r
6489 \r
6490     default:\r
6491       break;\r
6492     }\r
6493     break;\r
6494 \r
6495   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6496         if( wParam == OPT_CommentText ) {\r
6497             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6498 \r
6499             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6500                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6501                 POINTL pt;\r
6502                 LRESULT index;\r
6503 \r
6504                 pt.x = LOWORD( lpMF->lParam );\r
6505                 pt.y = HIWORD( lpMF->lParam );\r
6506 \r
6507                 if(lpMF->msg == WM_CHAR) {\r
6508                         CHARRANGE sel;\r
6509                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6510                         index = sel.cpMin;\r
6511                 } else\r
6512                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6513 \r
6514                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6515                 len = GetWindowTextLength(hwndText);\r
6516                 str = (char *) malloc(len + 1);\r
6517                 GetWindowText(hwndText, str, len + 1);\r
6518                 ReplaceComment(commentIndex, str);\r
6519                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6520                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6521                 free(str);\r
6522 \r
6523                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6524                 lpMF->msg = WM_USER;\r
6525 \r
6526                 return TRUE;\r
6527             }\r
6528         }\r
6529         break;\r
6530 \r
6531   case WM_SIZE:\r
6532     newSizeX = LOWORD(lParam);\r
6533     newSizeY = HIWORD(lParam);\r
6534     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6535     sizeX = newSizeX;\r
6536     sizeY = newSizeY;\r
6537     break;\r
6538 \r
6539   case WM_GETMINMAXINFO:\r
6540     /* Prevent resizing window too small */\r
6541     mmi = (MINMAXINFO *) lParam;\r
6542     mmi->ptMinTrackSize.x = 100;\r
6543     mmi->ptMinTrackSize.y = 100;\r
6544     break;\r
6545   }\r
6546   return FALSE;\r
6547 }\r
6548 \r
6549 VOID\r
6550 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6551 {\r
6552   FARPROC lpProc;\r
6553   char *p, *q;\r
6554 \r
6555   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6556 \r
6557   if (str == NULL) str = "";\r
6558   p = (char *) malloc(2 * strlen(str) + 2);\r
6559   q = p;\r
6560   while (*str) {\r
6561     if (*str == '\n') *q++ = '\r';\r
6562     *q++ = *str++;\r
6563   }\r
6564   *q = NULLCHAR;\r
6565   if (commentText != NULL) free(commentText);\r
6566 \r
6567   commentIndex = index;\r
6568   commentTitle = title;\r
6569   commentText = p;\r
6570   editComment = edit;\r
6571 \r
6572   if (commentDialog) {\r
6573     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6574     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6575   } else {\r
6576     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6577     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6578                  hwndMain, (DLGPROC)lpProc);\r
6579     FreeProcInstance(lpProc);\r
6580   }\r
6581   commentUp = TRUE;\r
6582 }\r
6583 \r
6584 \r
6585 /*---------------------------------------------------------------------------*\\r
6586  *\r
6587  * Type-in move dialog functions\r
6588  * \r
6589 \*---------------------------------------------------------------------------*/\r
6590 \r
6591 LRESULT CALLBACK\r
6592 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6593 {\r
6594   char move[MSG_SIZ];\r
6595   HWND hInput;\r
6596 \r
6597   switch (message) {\r
6598   case WM_INITDIALOG:\r
6599     move[0] = (char) lParam;\r
6600     move[1] = NULLCHAR;\r
6601     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6602     Translate(hDlg, DLG_TypeInMove);\r
6603     hInput = GetDlgItem(hDlg, OPT_Move);\r
6604     SetWindowText(hInput, move);\r
6605     SetFocus(hInput);\r
6606     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6607     return FALSE;\r
6608 \r
6609   case WM_COMMAND:\r
6610     switch (LOWORD(wParam)) {\r
6611     case IDOK:\r
6612 \r
6613       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6614       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6615       TypeInDoneEvent(move);\r
6616       EndDialog(hDlg, TRUE);\r
6617       return TRUE;\r
6618     case IDCANCEL:\r
6619       EndDialog(hDlg, FALSE);\r
6620       return TRUE;\r
6621     default:\r
6622       break;\r
6623     }\r
6624     break;\r
6625   }\r
6626   return FALSE;\r
6627 }\r
6628 \r
6629 VOID\r
6630 PopUpMoveDialog(char firstchar)\r
6631 {\r
6632     FARPROC lpProc;\r
6633 \r
6634       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6635       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6636         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6637       FreeProcInstance(lpProc);\r
6638 }\r
6639 \r
6640 /*---------------------------------------------------------------------------*\\r
6641  *\r
6642  * Type-in name dialog functions\r
6643  * \r
6644 \*---------------------------------------------------------------------------*/\r
6645 \r
6646 LRESULT CALLBACK\r
6647 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6648 {\r
6649   char move[MSG_SIZ];\r
6650   HWND hInput;\r
6651 \r
6652   switch (message) {\r
6653   case WM_INITDIALOG:\r
6654     move[0] = (char) lParam;\r
6655     move[1] = NULLCHAR;\r
6656     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6657     Translate(hDlg, DLG_TypeInName);\r
6658     hInput = GetDlgItem(hDlg, OPT_Name);\r
6659     SetWindowText(hInput, move);\r
6660     SetFocus(hInput);\r
6661     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6662     return FALSE;\r
6663 \r
6664   case WM_COMMAND:\r
6665     switch (LOWORD(wParam)) {\r
6666     case IDOK:\r
6667       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6668       appData.userName = strdup(move);\r
6669       SetUserLogo();\r
6670       SetGameInfo();\r
6671       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6672         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6673         DisplayTitle(move);\r
6674       }\r
6675 \r
6676 \r
6677       EndDialog(hDlg, TRUE);\r
6678       return TRUE;\r
6679     case IDCANCEL:\r
6680       EndDialog(hDlg, FALSE);\r
6681       return TRUE;\r
6682     default:\r
6683       break;\r
6684     }\r
6685     break;\r
6686   }\r
6687   return FALSE;\r
6688 }\r
6689 \r
6690 VOID\r
6691 PopUpNameDialog(char firstchar)\r
6692 {\r
6693     FARPROC lpProc;\r
6694     \r
6695       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6696       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6697         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6698       FreeProcInstance(lpProc);\r
6699 }\r
6700 \r
6701 /*---------------------------------------------------------------------------*\\r
6702  *\r
6703  *  Error dialogs\r
6704  * \r
6705 \*---------------------------------------------------------------------------*/\r
6706 \r
6707 /* Nonmodal error box */\r
6708 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6709                              WPARAM wParam, LPARAM lParam);\r
6710 \r
6711 VOID\r
6712 ErrorPopUp(char *title, char *content)\r
6713 {\r
6714   FARPROC lpProc;\r
6715   char *p, *q;\r
6716   BOOLEAN modal = hwndMain == NULL;\r
6717 \r
6718   p = content;\r
6719   q = errorMessage;\r
6720   while (*p) {\r
6721     if (*p == '\n') {\r
6722       if (modal) {\r
6723         *q++ = ' ';\r
6724         p++;\r
6725       } else {\r
6726         *q++ = '\r';\r
6727         *q++ = *p++;\r
6728       }\r
6729     } else {\r
6730       *q++ = *p++;\r
6731     }\r
6732   }\r
6733   *q = NULLCHAR;\r
6734   strncpy(errorTitle, title, sizeof(errorTitle));\r
6735   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6736   \r
6737   if (modal) {\r
6738     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6739   } else {\r
6740     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6741     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6742                  hwndMain, (DLGPROC)lpProc);\r
6743     FreeProcInstance(lpProc);\r
6744   }\r
6745 }\r
6746 \r
6747 VOID\r
6748 ErrorPopDown()\r
6749 {\r
6750   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6751   if (errorDialog == NULL) return;\r
6752   DestroyWindow(errorDialog);\r
6753   errorDialog = NULL;\r
6754   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6755 }\r
6756 \r
6757 LRESULT CALLBACK\r
6758 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6759 {\r
6760   RECT rChild;\r
6761 \r
6762   switch (message) {\r
6763   case WM_INITDIALOG:\r
6764     GetWindowRect(hDlg, &rChild);\r
6765 \r
6766     /*\r
6767     SetWindowPos(hDlg, NULL, rChild.left,\r
6768       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6769       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6770     */\r
6771 \r
6772     /* \r
6773         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6774         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6775         and it doesn't work when you resize the dialog.\r
6776         For now, just give it a default position.\r
6777     */\r
6778     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6779     Translate(hDlg, DLG_Error);\r
6780 \r
6781     errorDialog = hDlg;\r
6782     SetWindowText(hDlg, errorTitle);\r
6783     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6784     return FALSE;\r
6785 \r
6786   case WM_COMMAND:\r
6787     switch (LOWORD(wParam)) {\r
6788     case IDOK:\r
6789     case IDCANCEL:\r
6790       if (errorDialog == hDlg) errorDialog = NULL;\r
6791       DestroyWindow(hDlg);\r
6792       return TRUE;\r
6793 \r
6794     default:\r
6795       break;\r
6796     }\r
6797     break;\r
6798   }\r
6799   return FALSE;\r
6800 }\r
6801 \r
6802 #ifdef GOTHIC\r
6803 HWND gothicDialog = NULL;\r
6804 \r
6805 LRESULT CALLBACK\r
6806 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6807 {\r
6808   RECT rChild;\r
6809   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6810 \r
6811   switch (message) {\r
6812   case WM_INITDIALOG:\r
6813     GetWindowRect(hDlg, &rChild);\r
6814 \r
6815     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6816                                                              SWP_NOZORDER);\r
6817 \r
6818     /* \r
6819         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6820         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6821         and it doesn't work when you resize the dialog.\r
6822         For now, just give it a default position.\r
6823     */\r
6824     gothicDialog = hDlg;\r
6825     SetWindowText(hDlg, errorTitle);\r
6826     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6827     return FALSE;\r
6828 \r
6829   case WM_COMMAND:\r
6830     switch (LOWORD(wParam)) {\r
6831     case IDOK:\r
6832     case IDCANCEL:\r
6833       if (errorDialog == hDlg) errorDialog = NULL;\r
6834       DestroyWindow(hDlg);\r
6835       return TRUE;\r
6836 \r
6837     default:\r
6838       break;\r
6839     }\r
6840     break;\r
6841   }\r
6842   return FALSE;\r
6843 }\r
6844 \r
6845 VOID\r
6846 GothicPopUp(char *title, VariantClass variant)\r
6847 {\r
6848   FARPROC lpProc;\r
6849   static char *lastTitle;\r
6850 \r
6851   strncpy(errorTitle, title, sizeof(errorTitle));\r
6852   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6853 \r
6854   if(lastTitle != title && gothicDialog != NULL) {\r
6855     DestroyWindow(gothicDialog);\r
6856     gothicDialog = NULL;\r
6857   }\r
6858   if(variant != VariantNormal && gothicDialog == NULL) {\r
6859     title = lastTitle;\r
6860     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6861     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6862                  hwndMain, (DLGPROC)lpProc);\r
6863     FreeProcInstance(lpProc);\r
6864   }\r
6865 }\r
6866 #endif\r
6867 \r
6868 /*---------------------------------------------------------------------------*\\r
6869  *\r
6870  *  Ics Interaction console functions\r
6871  *\r
6872 \*---------------------------------------------------------------------------*/\r
6873 \r
6874 #define HISTORY_SIZE 64\r
6875 static char *history[HISTORY_SIZE];\r
6876 int histIn = 0, histP = 0;\r
6877 \r
6878 \r
6879 VOID\r
6880 SaveInHistory(char *cmd)\r
6881 {\r
6882   if (history[histIn] != NULL) {\r
6883     free(history[histIn]);\r
6884     history[histIn] = NULL;\r
6885   }\r
6886   if (*cmd == NULLCHAR) return;\r
6887   history[histIn] = StrSave(cmd);\r
6888   histIn = (histIn + 1) % HISTORY_SIZE;\r
6889   if (history[histIn] != NULL) {\r
6890     free(history[histIn]);\r
6891 \r
6892     history[histIn] = NULL;\r
6893   }\r
6894   histP = histIn;\r
6895 }\r
6896 \r
6897 char *\r
6898 PrevInHistory(char *cmd)\r
6899 {\r
6900   int newhp;\r
6901   if (histP == histIn) {\r
6902     if (history[histIn] != NULL) free(history[histIn]);\r
6903     history[histIn] = StrSave(cmd);\r
6904   }\r
6905   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6906   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6907   histP = newhp;\r
6908   return history[histP];\r
6909 }\r
6910 \r
6911 char *\r
6912 NextInHistory()\r
6913 {\r
6914   if (histP == histIn) return NULL;\r
6915   histP = (histP + 1) % HISTORY_SIZE;\r
6916   return history[histP];   \r
6917 }\r
6918 \r
6919 HMENU\r
6920 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6921 {\r
6922   HMENU hmenu, h;\r
6923   int i = 0;\r
6924   hmenu = LoadMenu(hInst, "TextMenu");\r
6925   h = GetSubMenu(hmenu, 0);\r
6926   while (e->item) {\r
6927     if (strcmp(e->item, "-") == 0) {\r
6928       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6929     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6930       int flags = MF_STRING, j = 0;\r
6931       if (e->item[0] == '|') {\r
6932         flags |= MF_MENUBARBREAK;\r
6933         j++;\r
6934       }\r
6935       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6936       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6937     }\r
6938     e++;\r
6939     i++;\r
6940   } \r
6941   return hmenu;\r
6942 }\r
6943 \r
6944 WNDPROC consoleTextWindowProc;\r
6945 \r
6946 void\r
6947 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6948 {\r
6949   char buf[MSG_SIZ], name[MSG_SIZ];\r
6950   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6951   CHARRANGE sel;\r
6952 \r
6953   if (!getname) {\r
6954     SetWindowText(hInput, command);\r
6955     if (immediate) {\r
6956       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6957     } else {\r
6958       sel.cpMin = 999999;\r
6959       sel.cpMax = 999999;\r
6960       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6961       SetFocus(hInput);\r
6962     }\r
6963     return;\r
6964   }    \r
6965   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6966   if (sel.cpMin == sel.cpMax) {\r
6967     /* Expand to surrounding word */\r
6968     TEXTRANGE tr;\r
6969     do {\r
6970       tr.chrg.cpMax = sel.cpMin;\r
6971       tr.chrg.cpMin = --sel.cpMin;\r
6972       if (sel.cpMin < 0) break;\r
6973       tr.lpstrText = name;\r
6974       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6975     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6976     sel.cpMin++;\r
6977 \r
6978     do {\r
6979       tr.chrg.cpMin = sel.cpMax;\r
6980       tr.chrg.cpMax = ++sel.cpMax;\r
6981       tr.lpstrText = name;\r
6982       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6983     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6984     sel.cpMax--;\r
6985 \r
6986     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6987       MessageBeep(MB_ICONEXCLAMATION);\r
6988       return;\r
6989     }\r
6990     tr.chrg = sel;\r
6991     tr.lpstrText = name;\r
6992     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6993   } else {\r
6994     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6995       MessageBeep(MB_ICONEXCLAMATION);\r
6996       return;\r
6997     }\r
6998     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6999   }\r
7000   if (immediate) {\r
7001     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7002     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7003     SetWindowText(hInput, buf);\r
7004     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7005   } else {\r
7006     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7007       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7008     SetWindowText(hInput, buf);\r
7009     sel.cpMin = 999999;\r
7010     sel.cpMax = 999999;\r
7011     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7012     SetFocus(hInput);\r
7013   }\r
7014 }\r
7015 \r
7016 LRESULT CALLBACK \r
7017 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7018 {\r
7019   HWND hInput;\r
7020   CHARRANGE sel;\r
7021 \r
7022   switch (message) {\r
7023   case WM_KEYDOWN:\r
7024     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7025     if(wParam=='R') return 0;\r
7026     switch (wParam) {\r
7027     case VK_PRIOR:\r
7028       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7029       return 0;\r
7030     case VK_NEXT:\r
7031       sel.cpMin = 999999;\r
7032       sel.cpMax = 999999;\r
7033       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7034       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7035       return 0;\r
7036     }\r
7037     break;\r
7038   case WM_CHAR:\r
7039    if(wParam != '\022') {\r
7040     if (wParam == '\t') {\r
7041       if (GetKeyState(VK_SHIFT) < 0) {\r
7042         /* shifted */\r
7043         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7044         if (buttonDesc[0].hwnd) {\r
7045           SetFocus(buttonDesc[0].hwnd);\r
7046         } else {\r
7047           SetFocus(hwndMain);\r
7048         }\r
7049       } else {\r
7050         /* unshifted */\r
7051         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7052       }\r
7053     } else {\r
7054       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7055       JAWS_DELETE( SetFocus(hInput); )\r
7056       SendMessage(hInput, message, wParam, lParam);\r
7057     }\r
7058     return 0;\r
7059    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7060    lParam = -1;\r
7061   case WM_RBUTTONDOWN:\r
7062     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7063       /* Move selection here if it was empty */\r
7064       POINT pt;\r
7065       pt.x = LOWORD(lParam);\r
7066       pt.y = HIWORD(lParam);\r
7067       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7068       if (sel.cpMin == sel.cpMax) {\r
7069         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7070         sel.cpMax = sel.cpMin;\r
7071         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7072       }\r
7073       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7074 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7075       POINT pt;\r
7076       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7077       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7078       if (sel.cpMin == sel.cpMax) {\r
7079         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7080         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7081       }\r
7082       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7083         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7084       }\r
7085       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7086       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7087       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7088       MenuPopup(hwnd, pt, hmenu, -1);\r
7089 }\r
7090     }\r
7091     return 0;\r
7092   case WM_RBUTTONUP:\r
7093     if (GetKeyState(VK_SHIFT) & ~1) {\r
7094       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7095         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7096     }\r
7097     return 0;\r
7098   case WM_PASTE:\r
7099     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7100     SetFocus(hInput);\r
7101     return SendMessage(hInput, message, wParam, lParam);\r
7102   case WM_MBUTTONDOWN:\r
7103     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7104   case WM_COMMAND:\r
7105     switch (LOWORD(wParam)) {\r
7106     case IDM_QuickPaste:\r
7107       {\r
7108         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7109         if (sel.cpMin == sel.cpMax) {\r
7110           MessageBeep(MB_ICONEXCLAMATION);\r
7111           return 0;\r
7112         }\r
7113         SendMessage(hwnd, WM_COPY, 0, 0);\r
7114         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7115         SendMessage(hInput, WM_PASTE, 0, 0);\r
7116         SetFocus(hInput);\r
7117         return 0;\r
7118       }\r
7119     case IDM_Cut:\r
7120       SendMessage(hwnd, WM_CUT, 0, 0);\r
7121       return 0;\r
7122     case IDM_Paste:\r
7123       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7124       return 0;\r
7125     case IDM_Copy:\r
7126       SendMessage(hwnd, WM_COPY, 0, 0);\r
7127       return 0;\r
7128     default:\r
7129       {\r
7130         int i = LOWORD(wParam) - IDM_CommandX;\r
7131         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7132             icsTextMenuEntry[i].command != NULL) {\r
7133           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7134                    icsTextMenuEntry[i].getname,\r
7135                    icsTextMenuEntry[i].immediate);\r
7136           return 0;\r
7137         }\r
7138       }\r
7139       break;\r
7140     }\r
7141     break;\r
7142   }\r
7143   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7144 }\r
7145 \r
7146 WNDPROC consoleInputWindowProc;\r
7147 \r
7148 LRESULT CALLBACK\r
7149 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7150 {\r
7151   char buf[MSG_SIZ];\r
7152   char *p;\r
7153   static BOOL sendNextChar = FALSE;\r
7154   static BOOL quoteNextChar = FALSE;\r
7155   InputSource *is = consoleInputSource;\r
7156   CHARFORMAT cf;\r
7157   CHARRANGE sel;\r
7158 \r
7159   switch (message) {\r
7160   case WM_CHAR:\r
7161     if (!appData.localLineEditing || sendNextChar) {\r
7162       is->buf[0] = (CHAR) wParam;\r
7163       is->count = 1;\r
7164       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7165       sendNextChar = FALSE;\r
7166       return 0;\r
7167     }\r
7168     if (quoteNextChar) {\r
7169       buf[0] = (char) wParam;\r
7170       buf[1] = NULLCHAR;\r
7171       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7172       quoteNextChar = FALSE;\r
7173       return 0;\r
7174     }\r
7175     switch (wParam) {\r
7176     case '\r':   /* Enter key */\r
7177       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7178       if (consoleEcho) SaveInHistory(is->buf);\r
7179       is->buf[is->count++] = '\n';\r
7180       is->buf[is->count] = NULLCHAR;\r
7181       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7182       if (consoleEcho) {\r
7183         ConsoleOutput(is->buf, is->count, TRUE);\r
7184       } else if (appData.localLineEditing) {\r
7185         ConsoleOutput("\n", 1, TRUE);\r
7186       }\r
7187       /* fall thru */\r
7188     case '\033': /* Escape key */\r
7189       SetWindowText(hwnd, "");\r
7190       cf.cbSize = sizeof(CHARFORMAT);\r
7191       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7192       if (consoleEcho) {\r
7193         cf.crTextColor = textAttribs[ColorNormal].color;\r
7194       } else {\r
7195         cf.crTextColor = COLOR_ECHOOFF;\r
7196       }\r
7197       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7198       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7199       return 0;\r
7200     case '\t':   /* Tab key */\r
7201       if (GetKeyState(VK_SHIFT) < 0) {\r
7202         /* shifted */\r
7203         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7204       } else {\r
7205         /* unshifted */\r
7206         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7207         if (buttonDesc[0].hwnd) {\r
7208           SetFocus(buttonDesc[0].hwnd);\r
7209         } else {\r
7210           SetFocus(hwndMain);\r
7211         }\r
7212       }\r
7213       return 0;\r
7214     case '\023': /* Ctrl+S */\r
7215       sendNextChar = TRUE;\r
7216       return 0;\r
7217     case '\021': /* Ctrl+Q */\r
7218       quoteNextChar = TRUE;\r
7219       return 0;\r
7220     JAWS_REPLAY\r
7221     default:\r
7222       break;\r
7223     }\r
7224     break;\r
7225   case WM_KEYDOWN:\r
7226     switch (wParam) {\r
7227     case VK_UP:\r
7228       GetWindowText(hwnd, buf, MSG_SIZ);\r
7229       p = PrevInHistory(buf);\r
7230       if (p != NULL) {\r
7231         SetWindowText(hwnd, p);\r
7232         sel.cpMin = 999999;\r
7233         sel.cpMax = 999999;\r
7234         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7235         return 0;\r
7236       }\r
7237       break;\r
7238     case VK_DOWN:\r
7239       p = NextInHistory();\r
7240       if (p != NULL) {\r
7241         SetWindowText(hwnd, p);\r
7242         sel.cpMin = 999999;\r
7243         sel.cpMax = 999999;\r
7244         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7245         return 0;\r
7246       }\r
7247       break;\r
7248     case VK_HOME:\r
7249     case VK_END:\r
7250       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7251       /* fall thru */\r
7252     case VK_PRIOR:\r
7253     case VK_NEXT:\r
7254       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7255       return 0;\r
7256     }\r
7257     break;\r
7258   case WM_MBUTTONDOWN:\r
7259     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7260       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7261     break;\r
7262   case WM_RBUTTONUP:\r
7263     if (GetKeyState(VK_SHIFT) & ~1) {\r
7264       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7265         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7266     } else {\r
7267       POINT pt;\r
7268       HMENU hmenu;\r
7269       hmenu = LoadMenu(hInst, "InputMenu");\r
7270       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7271       if (sel.cpMin == sel.cpMax) {\r
7272         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7273         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7274       }\r
7275       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7276         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7277       }\r
7278       pt.x = LOWORD(lParam);\r
7279       pt.y = HIWORD(lParam);\r
7280       MenuPopup(hwnd, pt, hmenu, -1);\r
7281     }\r
7282     return 0;\r
7283   case WM_COMMAND:\r
7284     switch (LOWORD(wParam)) { \r
7285     case IDM_Undo:\r
7286       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7287       return 0;\r
7288     case IDM_SelectAll:\r
7289       sel.cpMin = 0;\r
7290       sel.cpMax = -1; /*999999?*/\r
7291       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7292       return 0;\r
7293     case IDM_Cut:\r
7294       SendMessage(hwnd, WM_CUT, 0, 0);\r
7295       return 0;\r
7296     case IDM_Paste:\r
7297       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7298       return 0;\r
7299     case IDM_Copy:\r
7300       SendMessage(hwnd, WM_COPY, 0, 0);\r
7301       return 0;\r
7302     }\r
7303     break;\r
7304   }\r
7305   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7306 }\r
7307 \r
7308 #define CO_MAX  100000\r
7309 #define CO_TRIM   1000\r
7310 \r
7311 LRESULT CALLBACK\r
7312 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7313 {\r
7314   static SnapData sd;\r
7315   HWND hText, hInput;\r
7316   RECT rect;\r
7317   static int sizeX, sizeY;\r
7318   int newSizeX, newSizeY;\r
7319   MINMAXINFO *mmi;\r
7320   WORD wMask;\r
7321 \r
7322   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7323   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7324 \r
7325   switch (message) {\r
7326   case WM_NOTIFY:\r
7327     if (((NMHDR*)lParam)->code == EN_LINK)\r
7328     {\r
7329       ENLINK *pLink = (ENLINK*)lParam;\r
7330       if (pLink->msg == WM_LBUTTONUP)\r
7331       {\r
7332         TEXTRANGE tr;\r
7333 \r
7334         tr.chrg = pLink->chrg;\r
7335         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7336         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7337         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7338         free(tr.lpstrText);\r
7339       }\r
7340     }\r
7341     break;\r
7342   case WM_INITDIALOG: /* message: initialize dialog box */\r
7343     hwndConsole = hDlg;\r
7344     SetFocus(hInput);\r
7345     consoleTextWindowProc = (WNDPROC)\r
7346       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7347     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7348     consoleInputWindowProc = (WNDPROC)\r
7349       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7350     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7351     Colorize(ColorNormal, TRUE);\r
7352     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7353     ChangedConsoleFont();\r
7354     GetClientRect(hDlg, &rect);\r
7355     sizeX = rect.right;\r
7356     sizeY = rect.bottom;\r
7357     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7358         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7359       WINDOWPLACEMENT wp;\r
7360       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7361       wp.length = sizeof(WINDOWPLACEMENT);\r
7362       wp.flags = 0;\r
7363       wp.showCmd = SW_SHOW;\r
7364       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7365       wp.rcNormalPosition.left = wpConsole.x;\r
7366       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7367       wp.rcNormalPosition.top = wpConsole.y;\r
7368       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7369       SetWindowPlacement(hDlg, &wp);\r
7370     }\r
7371 \r
7372    // [HGM] Chessknight's change 2004-07-13\r
7373    else { /* Determine Defaults */\r
7374        WINDOWPLACEMENT wp;\r
7375        wpConsole.x = wpMain.width + 1;\r
7376        wpConsole.y = wpMain.y;\r
7377        wpConsole.width = screenWidth -  wpMain.width;\r
7378        wpConsole.height = wpMain.height;\r
7379        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7380        wp.length = sizeof(WINDOWPLACEMENT);\r
7381        wp.flags = 0;\r
7382        wp.showCmd = SW_SHOW;\r
7383        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7384        wp.rcNormalPosition.left = wpConsole.x;\r
7385        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7386        wp.rcNormalPosition.top = wpConsole.y;\r
7387        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7388        SetWindowPlacement(hDlg, &wp);\r
7389     }\r
7390 \r
7391    // Allow hText to highlight URLs and send notifications on them\r
7392    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7393    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7394    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7395    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7396 \r
7397     return FALSE;\r
7398 \r
7399   case WM_SETFOCUS:\r
7400     SetFocus(hInput);\r
7401     return 0;\r
7402 \r
7403   case WM_CLOSE:\r
7404     ExitEvent(0);\r
7405     /* not reached */\r
7406     break;\r
7407 \r
7408   case WM_SIZE:\r
7409     if (IsIconic(hDlg)) break;\r
7410     newSizeX = LOWORD(lParam);\r
7411     newSizeY = HIWORD(lParam);\r
7412     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7413       RECT rectText, rectInput;\r
7414       POINT pt;\r
7415       int newTextHeight, newTextWidth;\r
7416       GetWindowRect(hText, &rectText);\r
7417       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7418       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7419       if (newTextHeight < 0) {\r
7420         newSizeY += -newTextHeight;\r
7421         newTextHeight = 0;\r
7422       }\r
7423       SetWindowPos(hText, NULL, 0, 0,\r
7424         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7425       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7426       pt.x = rectInput.left;\r
7427       pt.y = rectInput.top + newSizeY - sizeY;\r
7428       ScreenToClient(hDlg, &pt);\r
7429       SetWindowPos(hInput, NULL, \r
7430         pt.x, pt.y, /* needs client coords */   \r
7431         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7432         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7433     }\r
7434     sizeX = newSizeX;\r
7435     sizeY = newSizeY;\r
7436     break;\r
7437 \r
7438   case WM_GETMINMAXINFO:\r
7439     /* Prevent resizing window too small */\r
7440     mmi = (MINMAXINFO *) lParam;\r
7441     mmi->ptMinTrackSize.x = 100;\r
7442     mmi->ptMinTrackSize.y = 100;\r
7443     break;\r
7444 \r
7445   /* [AS] Snapping */\r
7446   case WM_ENTERSIZEMOVE:\r
7447     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7448 \r
7449   case WM_SIZING:\r
7450     return OnSizing( &sd, hDlg, wParam, lParam );\r
7451 \r
7452   case WM_MOVING:\r
7453     return OnMoving( &sd, hDlg, wParam, lParam );\r
7454 \r
7455   case WM_EXITSIZEMOVE:\r
7456         UpdateICSWidth(hText);\r
7457     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7458   }\r
7459 \r
7460   return DefWindowProc(hDlg, message, wParam, lParam);\r
7461 }\r
7462 \r
7463 \r
7464 VOID\r
7465 ConsoleCreate()\r
7466 {\r
7467   HWND hCons;\r
7468   if (hwndConsole) return;\r
7469   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7470   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7471 }\r
7472 \r
7473 \r
7474 VOID\r
7475 ConsoleOutput(char* data, int length, int forceVisible)\r
7476 {\r
7477   HWND hText;\r
7478   int trim, exlen;\r
7479   char *p, *q;\r
7480   char buf[CO_MAX+1];\r
7481   POINT pEnd;\r
7482   RECT rect;\r
7483   static int delayLF = 0;\r
7484   CHARRANGE savesel, sel;\r
7485 \r
7486   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7487   p = data;\r
7488   q = buf;\r
7489   if (delayLF) {\r
7490     *q++ = '\r';\r
7491     *q++ = '\n';\r
7492     delayLF = 0;\r
7493   }\r
7494   while (length--) {\r
7495     if (*p == '\n') {\r
7496       if (*++p) {\r
7497         *q++ = '\r';\r
7498         *q++ = '\n';\r
7499       } else {\r
7500         delayLF = 1;\r
7501       }\r
7502     } else if (*p == '\007') {\r
7503        MyPlaySound(&sounds[(int)SoundBell]);\r
7504        p++;\r
7505     } else {\r
7506       *q++ = *p++;\r
7507     }\r
7508   }\r
7509   *q = NULLCHAR;\r
7510   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7511   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7512   /* Save current selection */\r
7513   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7514   exlen = GetWindowTextLength(hText);\r
7515   /* Find out whether current end of text is visible */\r
7516   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7517   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7518   /* Trim existing text if it's too long */\r
7519   if (exlen + (q - buf) > CO_MAX) {\r
7520     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7521     sel.cpMin = 0;\r
7522     sel.cpMax = trim;\r
7523     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7524     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7525     exlen -= trim;\r
7526     savesel.cpMin -= trim;\r
7527     savesel.cpMax -= trim;\r
7528     if (exlen < 0) exlen = 0;\r
7529     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7530     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7531   }\r
7532   /* Append the new text */\r
7533   sel.cpMin = exlen;\r
7534   sel.cpMax = exlen;\r
7535   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7536   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7537   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7538   if (forceVisible || exlen == 0 ||\r
7539       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7540        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7541     /* Scroll to make new end of text visible if old end of text\r
7542        was visible or new text is an echo of user typein */\r
7543     sel.cpMin = 9999999;\r
7544     sel.cpMax = 9999999;\r
7545     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7546     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7547     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7548     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7549   }\r
7550   if (savesel.cpMax == exlen || forceVisible) {\r
7551     /* Move insert point to new end of text if it was at the old\r
7552        end of text or if the new text is an echo of user typein */\r
7553     sel.cpMin = 9999999;\r
7554     sel.cpMax = 9999999;\r
7555     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7556   } else {\r
7557     /* Restore previous selection */\r
7558     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7559   }\r
7560   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7561 }\r
7562 \r
7563 /*---------*/\r
7564 \r
7565 \r
7566 void\r
7567 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7568 {\r
7569   char buf[100];\r
7570   char *str;\r
7571   COLORREF oldFg, oldBg;\r
7572   HFONT oldFont;\r
7573   RECT rect;\r
7574 \r
7575   if(copyNumber > 1)\r
7576     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7577 \r
7578   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7579   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7580   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7581 \r
7582   rect.left = x;\r
7583   rect.right = x + squareSize;\r
7584   rect.top  = y;\r
7585   rect.bottom = y + squareSize;\r
7586   str = buf;\r
7587 \r
7588   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7589                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7590              y, ETO_CLIPPED|ETO_OPAQUE,\r
7591              &rect, str, strlen(str), NULL);\r
7592 \r
7593   (void) SetTextColor(hdc, oldFg);\r
7594   (void) SetBkColor(hdc, oldBg);\r
7595   (void) SelectObject(hdc, oldFont);\r
7596 }\r
7597 \r
7598 void\r
7599 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7600               RECT *rect, char *color, char *flagFell)\r
7601 {\r
7602   char buf[100];\r
7603   char *str;\r
7604   COLORREF oldFg, oldBg;\r
7605   HFONT oldFont;\r
7606 \r
7607   if (twoBoards && partnerUp) return;\r
7608   if (appData.clockMode) {\r
7609     if (tinyLayout)\r
7610       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7611     else\r
7612       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7613     str = buf;\r
7614   } else {\r
7615     str = color;\r
7616   }\r
7617 \r
7618   if (highlight) {\r
7619     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7620     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7621   } else {\r
7622     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7623     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7624   }\r
7625   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7626 \r
7627   JAWS_SILENCE\r
7628 \r
7629   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7630              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7631              rect, str, strlen(str), NULL);\r
7632   if(logoHeight > 0 && appData.clockMode) {\r
7633       RECT r;\r
7634       str += strlen(color)+2;\r
7635       r.top = rect->top + logoHeight/2;\r
7636       r.left = rect->left;\r
7637       r.right = rect->right;\r
7638       r.bottom = rect->bottom;\r
7639       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7640                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7641                  &r, str, strlen(str), NULL);\r
7642   }\r
7643   (void) SetTextColor(hdc, oldFg);\r
7644   (void) SetBkColor(hdc, oldBg);\r
7645   (void) SelectObject(hdc, oldFont);\r
7646 }\r
7647 \r
7648 \r
7649 int\r
7650 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7651            OVERLAPPED *ovl)\r
7652 {\r
7653   int ok, err;\r
7654 \r
7655   /* [AS]  */\r
7656   if( count <= 0 ) {\r
7657     if (appData.debugMode) {\r
7658       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7659     }\r
7660 \r
7661     return ERROR_INVALID_USER_BUFFER;\r
7662   }\r
7663 \r
7664   ResetEvent(ovl->hEvent);\r
7665   ovl->Offset = ovl->OffsetHigh = 0;\r
7666   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7667   if (ok) {\r
7668     err = NO_ERROR;\r
7669   } else {\r
7670     err = GetLastError();\r
7671     if (err == ERROR_IO_PENDING) {\r
7672       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7673       if (ok)\r
7674         err = NO_ERROR;\r
7675       else\r
7676         err = GetLastError();\r
7677     }\r
7678   }\r
7679   return err;\r
7680 }\r
7681 \r
7682 int\r
7683 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7684             OVERLAPPED *ovl)\r
7685 {\r
7686   int ok, err;\r
7687 \r
7688   ResetEvent(ovl->hEvent);\r
7689   ovl->Offset = ovl->OffsetHigh = 0;\r
7690   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7691   if (ok) {\r
7692     err = NO_ERROR;\r
7693   } else {\r
7694     err = GetLastError();\r
7695     if (err == ERROR_IO_PENDING) {\r
7696       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7697       if (ok)\r
7698         err = NO_ERROR;\r
7699       else\r
7700         err = GetLastError();\r
7701     }\r
7702 \r
7703   }\r
7704   return err;\r
7705 }\r
7706 \r
7707 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7708 void CheckForInputBufferFull( InputSource * is )\r
7709 {\r
7710     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7711         /* Look for end of line */\r
7712         char * p = is->buf;\r
7713         \r
7714         while( p < is->next && *p != '\n' ) {\r
7715             p++;\r
7716         }\r
7717 \r
7718         if( p >= is->next ) {\r
7719             if (appData.debugMode) {\r
7720                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7721             }\r
7722 \r
7723             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7724             is->count = (DWORD) -1;\r
7725             is->next = is->buf;\r
7726         }\r
7727     }\r
7728 }\r
7729 \r
7730 DWORD\r
7731 InputThread(LPVOID arg)\r
7732 {\r
7733   InputSource *is;\r
7734   OVERLAPPED ovl;\r
7735 \r
7736   is = (InputSource *) arg;\r
7737   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7738   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7739   while (is->hThread != NULL) {\r
7740     is->error = DoReadFile(is->hFile, is->next,\r
7741                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7742                            &is->count, &ovl);\r
7743     if (is->error == NO_ERROR) {\r
7744       is->next += is->count;\r
7745     } else {\r
7746       if (is->error == ERROR_BROKEN_PIPE) {\r
7747         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7748         is->count = 0;\r
7749       } else {\r
7750         is->count = (DWORD) -1;\r
7751         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7752         break; \r
7753       }\r
7754     }\r
7755 \r
7756     CheckForInputBufferFull( is );\r
7757 \r
7758     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7759 \r
7760     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7761 \r
7762     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7763   }\r
7764 \r
7765   CloseHandle(ovl.hEvent);\r
7766   CloseHandle(is->hFile);\r
7767 \r
7768   if (appData.debugMode) {\r
7769     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7770   }\r
7771 \r
7772   return 0;\r
7773 }\r
7774 \r
7775 \r
7776 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7777 DWORD\r
7778 NonOvlInputThread(LPVOID arg)\r
7779 {\r
7780   InputSource *is;\r
7781   char *p, *q;\r
7782   int i;\r
7783   char prev;\r
7784 \r
7785   is = (InputSource *) arg;\r
7786   while (is->hThread != NULL) {\r
7787     is->error = ReadFile(is->hFile, is->next,\r
7788                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7789                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7790     if (is->error == NO_ERROR) {\r
7791       /* Change CRLF to LF */\r
7792       if (is->next > is->buf) {\r
7793         p = is->next - 1;\r
7794         i = is->count + 1;\r
7795       } else {\r
7796         p = is->next;\r
7797         i = is->count;\r
7798       }\r
7799       q = p;\r
7800       prev = NULLCHAR;\r
7801       while (i > 0) {\r
7802         if (prev == '\r' && *p == '\n') {\r
7803           *(q-1) = '\n';\r
7804           is->count--;\r
7805         } else { \r
7806           *q++ = *p;\r
7807         }\r
7808         prev = *p++;\r
7809         i--;\r
7810       }\r
7811       *q = NULLCHAR;\r
7812       is->next = q;\r
7813     } else {\r
7814       if (is->error == ERROR_BROKEN_PIPE) {\r
7815         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7816         is->count = 0; \r
7817       } else {\r
7818         is->count = (DWORD) -1;\r
7819       }\r
7820     }\r
7821 \r
7822     CheckForInputBufferFull( is );\r
7823 \r
7824     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7825 \r
7826     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7827 \r
7828     if (is->count < 0) break;  /* Quit on error */\r
7829   }\r
7830   CloseHandle(is->hFile);\r
7831   return 0;\r
7832 }\r
7833 \r
7834 DWORD\r
7835 SocketInputThread(LPVOID arg)\r
7836 {\r
7837   InputSource *is;\r
7838 \r
7839   is = (InputSource *) arg;\r
7840   while (is->hThread != NULL) {\r
7841     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7842     if ((int)is->count == SOCKET_ERROR) {\r
7843       is->count = (DWORD) -1;\r
7844       is->error = WSAGetLastError();\r
7845     } else {\r
7846       is->error = NO_ERROR;\r
7847       is->next += is->count;\r
7848       if (is->count == 0 && is->second == is) {\r
7849         /* End of file on stderr; quit with no message */\r
7850         break;\r
7851       }\r
7852     }\r
7853     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7854 \r
7855     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7856 \r
7857     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7858   }\r
7859   return 0;\r
7860 }\r
7861 \r
7862 VOID\r
7863 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7864 {\r
7865   InputSource *is;\r
7866 \r
7867   is = (InputSource *) lParam;\r
7868   if (is->lineByLine) {\r
7869     /* Feed in lines one by one */\r
7870     char *p = is->buf;\r
7871     char *q = p;\r
7872     while (q < is->next) {\r
7873       if (*q++ == '\n') {\r
7874         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7875         p = q;\r
7876       }\r
7877     }\r
7878     \r
7879     /* Move any partial line to the start of the buffer */\r
7880     q = is->buf;\r
7881     while (p < is->next) {\r
7882       *q++ = *p++;\r
7883     }\r
7884     is->next = q;\r
7885 \r
7886     if (is->error != NO_ERROR || is->count == 0) {\r
7887       /* Notify backend of the error.  Note: If there was a partial\r
7888          line at the end, it is not flushed through. */\r
7889       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7890     }\r
7891   } else {\r
7892     /* Feed in the whole chunk of input at once */\r
7893     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7894     is->next = is->buf;\r
7895   }\r
7896 }\r
7897 \r
7898 /*---------------------------------------------------------------------------*\\r
7899  *\r
7900  *  Menu enables. Used when setting various modes.\r
7901  *\r
7902 \*---------------------------------------------------------------------------*/\r
7903 \r
7904 typedef struct {\r
7905   int item;\r
7906   int flags;\r
7907 } Enables;\r
7908 \r
7909 VOID\r
7910 GreyRevert(Boolean grey)\r
7911 { // [HGM] vari: for retracting variations in local mode\r
7912   HMENU hmenu = GetMenu(hwndMain);\r
7913   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7914   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7915 }\r
7916 \r
7917 VOID\r
7918 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7919 {\r
7920   while (enab->item > 0) {\r
7921     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7922     enab++;\r
7923   }\r
7924 }\r
7925 \r
7926 Enables gnuEnables[] = {\r
7927   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7929   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7940 \r
7941   // Needed to switch from ncp to GNU mode on Engine Load\r
7942   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7943   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7944   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7945   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7946   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7948   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7949   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7950   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7951   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7952   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7953   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7954   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7955   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7956   { -1, -1 }\r
7957 };\r
7958 \r
7959 Enables icsEnables[] = {\r
7960   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7968   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7976   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7980   { -1, -1 }\r
7981 };\r
7982 \r
7983 #if ZIPPY\r
7984 Enables zippyEnables[] = {\r
7985   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7989   { -1, -1 }\r
7990 };\r
7991 #endif\r
7992 \r
7993 Enables ncpEnables[] = {\r
7994   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7995   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8001   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8002   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8003   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8004   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8005   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8016   { -1, -1 }\r
8017 };\r
8018 \r
8019 Enables trainingOnEnables[] = {\r
8020   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8027   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8028   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8029   { -1, -1 }\r
8030 };\r
8031 \r
8032 Enables trainingOffEnables[] = {\r
8033   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8034   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8035   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8042   { -1, -1 }\r
8043 };\r
8044 \r
8045 /* These modify either ncpEnables or gnuEnables */\r
8046 Enables cmailEnables[] = {\r
8047   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8048   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8049   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8050   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8051   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8052   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8053   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8054   { -1, -1 }\r
8055 };\r
8056 \r
8057 Enables machineThinkingEnables[] = {\r
8058   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8059   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8060   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8065   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8066   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8068   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8069   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8071 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8072   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8073   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8074   { -1, -1 }\r
8075 };\r
8076 \r
8077 Enables userThinkingEnables[] = {\r
8078   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8079   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8080   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8081   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8082   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8083   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8084   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8085   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8086   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8087   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8088   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8089   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8090   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8091 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8092   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8093   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8094   { -1, -1 }\r
8095 };\r
8096 \r
8097 /*---------------------------------------------------------------------------*\\r
8098  *\r
8099  *  Front-end interface functions exported by XBoard.\r
8100  *  Functions appear in same order as prototypes in frontend.h.\r
8101  * \r
8102 \*---------------------------------------------------------------------------*/\r
8103 VOID\r
8104 CheckMark(UINT item, int state)\r
8105 {\r
8106     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8107 }\r
8108 \r
8109 VOID\r
8110 ModeHighlight()\r
8111 {\r
8112   static UINT prevChecked = 0;\r
8113   static int prevPausing = 0;\r
8114   UINT nowChecked;\r
8115 \r
8116   if (pausing != prevPausing) {\r
8117     prevPausing = pausing;\r
8118     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8119                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8120     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8121   }\r
8122 \r
8123   switch (gameMode) {\r
8124   case BeginningOfGame:\r
8125     if (appData.icsActive)\r
8126       nowChecked = IDM_IcsClient;\r
8127     else if (appData.noChessProgram)\r
8128       nowChecked = IDM_EditGame;\r
8129     else\r
8130       nowChecked = IDM_MachineBlack;\r
8131     break;\r
8132   case MachinePlaysBlack:\r
8133     nowChecked = IDM_MachineBlack;\r
8134     break;\r
8135   case MachinePlaysWhite:\r
8136     nowChecked = IDM_MachineWhite;\r
8137     break;\r
8138   case TwoMachinesPlay:\r
8139     nowChecked = IDM_TwoMachines;\r
8140     break;\r
8141   case AnalyzeMode:\r
8142     nowChecked = IDM_AnalysisMode;\r
8143     break;\r
8144   case AnalyzeFile:\r
8145     nowChecked = IDM_AnalyzeFile;\r
8146     break;\r
8147   case EditGame:\r
8148     nowChecked = IDM_EditGame;\r
8149     break;\r
8150   case PlayFromGameFile:\r
8151     nowChecked = IDM_LoadGame;\r
8152     break;\r
8153   case EditPosition:\r
8154     nowChecked = IDM_EditPosition;\r
8155     break;\r
8156   case Training:\r
8157     nowChecked = IDM_Training;\r
8158     break;\r
8159   case IcsPlayingWhite:\r
8160   case IcsPlayingBlack:\r
8161   case IcsObserving:\r
8162   case IcsIdle:\r
8163     nowChecked = IDM_IcsClient;\r
8164     break;\r
8165   default:\r
8166   case EndOfGame:\r
8167     nowChecked = 0;\r
8168     break;\r
8169   }\r
8170   CheckMark(prevChecked, MF_UNCHECKED);\r
8171   CheckMark(nowChecked, MF_CHECKED);\r
8172   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8173 \r
8174   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8175     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8176                           MF_BYCOMMAND|MF_ENABLED);\r
8177   } else {\r
8178     (void) EnableMenuItem(GetMenu(hwndMain), \r
8179                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8180   }\r
8181 \r
8182   prevChecked = nowChecked;\r
8183 \r
8184   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8185   if (appData.icsActive) {\r
8186        if (appData.icsEngineAnalyze) {\r
8187                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8188        } else {\r
8189                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8190        }\r
8191   }\r
8192   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8193 }\r
8194 \r
8195 VOID\r
8196 SetICSMode()\r
8197 {\r
8198   HMENU hmenu = GetMenu(hwndMain);\r
8199   SetMenuEnables(hmenu, icsEnables);\r
8200   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8201     MF_BYCOMMAND|MF_ENABLED);\r
8202 #if ZIPPY\r
8203   if (appData.zippyPlay) {\r
8204     SetMenuEnables(hmenu, zippyEnables);\r
8205     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8206          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8207           MF_BYCOMMAND|MF_ENABLED);\r
8208   }\r
8209 #endif\r
8210 }\r
8211 \r
8212 VOID\r
8213 SetGNUMode()\r
8214 {\r
8215   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8216 }\r
8217 \r
8218 VOID\r
8219 SetNCPMode()\r
8220 {\r
8221   HMENU hmenu = GetMenu(hwndMain);\r
8222   SetMenuEnables(hmenu, ncpEnables);\r
8223     DrawMenuBar(hwndMain);\r
8224 }\r
8225 \r
8226 VOID\r
8227 SetCmailMode()\r
8228 {\r
8229   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8230 }\r
8231 \r
8232 VOID \r
8233 SetTrainingModeOn()\r
8234 {\r
8235   int i;\r
8236   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8237   for (i = 0; i < N_BUTTONS; i++) {\r
8238     if (buttonDesc[i].hwnd != NULL)\r
8239       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8240   }\r
8241   CommentPopDown();\r
8242 }\r
8243 \r
8244 VOID SetTrainingModeOff()\r
8245 {\r
8246   int i;\r
8247   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8248   for (i = 0; i < N_BUTTONS; i++) {\r
8249     if (buttonDesc[i].hwnd != NULL)\r
8250       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8251   }\r
8252 }\r
8253 \r
8254 \r
8255 VOID\r
8256 SetUserThinkingEnables()\r
8257 {\r
8258   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8259 }\r
8260 \r
8261 VOID\r
8262 SetMachineThinkingEnables()\r
8263 {\r
8264   HMENU hMenu = GetMenu(hwndMain);\r
8265   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8266 \r
8267   SetMenuEnables(hMenu, machineThinkingEnables);\r
8268 \r
8269   if (gameMode == MachinePlaysBlack) {\r
8270     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8271   } else if (gameMode == MachinePlaysWhite) {\r
8272     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8273   } else if (gameMode == TwoMachinesPlay) {\r
8274     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8275   }\r
8276 }\r
8277 \r
8278 \r
8279 VOID\r
8280 DisplayTitle(char *str)\r
8281 {\r
8282   char title[MSG_SIZ], *host;\r
8283   if (str[0] != NULLCHAR) {\r
8284     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8285   } else if (appData.icsActive) {\r
8286     if (appData.icsCommPort[0] != NULLCHAR)\r
8287       host = "ICS";\r
8288     else \r
8289       host = appData.icsHost;\r
8290       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8291   } else if (appData.noChessProgram) {\r
8292     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8293   } else {\r
8294     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8295     strcat(title, ": ");\r
8296     strcat(title, first.tidy);\r
8297   }\r
8298   SetWindowText(hwndMain, title);\r
8299 }\r
8300 \r
8301 \r
8302 VOID\r
8303 DisplayMessage(char *str1, char *str2)\r
8304 {\r
8305   HDC hdc;\r
8306   HFONT oldFont;\r
8307   int remain = MESSAGE_TEXT_MAX - 1;\r
8308   int len;\r
8309 \r
8310   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8311   messageText[0] = NULLCHAR;\r
8312   if (*str1) {\r
8313     len = strlen(str1);\r
8314     if (len > remain) len = remain;\r
8315     strncpy(messageText, str1, len);\r
8316     messageText[len] = NULLCHAR;\r
8317     remain -= len;\r
8318   }\r
8319   if (*str2 && remain >= 2) {\r
8320     if (*str1) {\r
8321       strcat(messageText, "  ");\r
8322       remain -= 2;\r
8323     }\r
8324     len = strlen(str2);\r
8325     if (len > remain) len = remain;\r
8326     strncat(messageText, str2, len);\r
8327   }\r
8328   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8329   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8330 \r
8331   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8332 \r
8333   SAYMACHINEMOVE();\r
8334 \r
8335   hdc = GetDC(hwndMain);\r
8336   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8337   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8338              &messageRect, messageText, strlen(messageText), NULL);\r
8339   (void) SelectObject(hdc, oldFont);\r
8340   (void) ReleaseDC(hwndMain, hdc);\r
8341 }\r
8342 \r
8343 VOID\r
8344 DisplayError(char *str, int error)\r
8345 {\r
8346   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8347   int len;\r
8348 \r
8349   if (error == 0) {\r
8350     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8351   } else {\r
8352     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8353                         NULL, error, LANG_NEUTRAL,\r
8354                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8355     if (len > 0) {\r
8356       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8357     } else {\r
8358       ErrorMap *em = errmap;\r
8359       while (em->err != 0 && em->err != error) em++;\r
8360       if (em->err != 0) {\r
8361         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8362       } else {\r
8363         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8364       }\r
8365     }\r
8366   }\r
8367   \r
8368   ErrorPopUp(_("Error"), buf);\r
8369 }\r
8370 \r
8371 \r
8372 VOID\r
8373 DisplayMoveError(char *str)\r
8374 {\r
8375   fromX = fromY = -1;\r
8376   ClearHighlights();\r
8377   DrawPosition(FALSE, NULL);\r
8378   if (appData.popupMoveErrors) {\r
8379     ErrorPopUp(_("Error"), str);\r
8380   } else {\r
8381     DisplayMessage(str, "");\r
8382     moveErrorMessageUp = TRUE;\r
8383   }\r
8384 }\r
8385 \r
8386 VOID\r
8387 DisplayFatalError(char *str, int error, int exitStatus)\r
8388 {\r
8389   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8390   int len;\r
8391   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8392 \r
8393   if (error != 0) {\r
8394     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8395                         NULL, error, LANG_NEUTRAL,\r
8396                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8397     if (len > 0) {\r
8398       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8399     } else {\r
8400       ErrorMap *em = errmap;\r
8401       while (em->err != 0 && em->err != error) em++;\r
8402       if (em->err != 0) {\r
8403         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8404       } else {\r
8405         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8406       }\r
8407     }\r
8408     str = buf;\r
8409   }\r
8410   if (appData.debugMode) {\r
8411     fprintf(debugFP, "%s: %s\n", label, str);\r
8412   }\r
8413   if (appData.popupExitMessage) {\r
8414     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8415                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8416   }\r
8417   ExitEvent(exitStatus);\r
8418 }\r
8419 \r
8420 \r
8421 VOID\r
8422 DisplayInformation(char *str)\r
8423 {\r
8424   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8425 }\r
8426 \r
8427 \r
8428 VOID\r
8429 DisplayNote(char *str)\r
8430 {\r
8431   ErrorPopUp(_("Note"), str);\r
8432 }\r
8433 \r
8434 \r
8435 typedef struct {\r
8436   char *title, *question, *replyPrefix;\r
8437   ProcRef pr;\r
8438 } QuestionParams;\r
8439 \r
8440 LRESULT CALLBACK\r
8441 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8442 {\r
8443   static QuestionParams *qp;\r
8444   char reply[MSG_SIZ];\r
8445   int len, err;\r
8446 \r
8447   switch (message) {\r
8448   case WM_INITDIALOG:\r
8449     qp = (QuestionParams *) lParam;\r
8450     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8451     Translate(hDlg, DLG_Question);\r
8452     SetWindowText(hDlg, qp->title);\r
8453     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8454     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8455     return FALSE;\r
8456 \r
8457   case WM_COMMAND:\r
8458     switch (LOWORD(wParam)) {\r
8459     case IDOK:\r
8460       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8461       if (*reply) strcat(reply, " ");\r
8462       len = strlen(reply);\r
8463       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8464       strcat(reply, "\n");\r
8465       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8466       EndDialog(hDlg, TRUE);\r
8467       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8468       return TRUE;\r
8469     case IDCANCEL:\r
8470       EndDialog(hDlg, FALSE);\r
8471       return TRUE;\r
8472     default:\r
8473       break;\r
8474     }\r
8475     break;\r
8476   }\r
8477   return FALSE;\r
8478 }\r
8479 \r
8480 VOID\r
8481 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8482 {\r
8483     QuestionParams qp;\r
8484     FARPROC lpProc;\r
8485     \r
8486     qp.title = title;\r
8487     qp.question = question;\r
8488     qp.replyPrefix = replyPrefix;\r
8489     qp.pr = pr;\r
8490     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8491     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8492       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8493     FreeProcInstance(lpProc);\r
8494 }\r
8495 \r
8496 /* [AS] Pick FRC position */\r
8497 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8498 {\r
8499     static int * lpIndexFRC;\r
8500     BOOL index_is_ok;\r
8501     char buf[16];\r
8502 \r
8503     switch( message )\r
8504     {\r
8505     case WM_INITDIALOG:\r
8506         lpIndexFRC = (int *) lParam;\r
8507 \r
8508         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8509         Translate(hDlg, DLG_NewGameFRC);\r
8510 \r
8511         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8512         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8513         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8514         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8515 \r
8516         break;\r
8517 \r
8518     case WM_COMMAND:\r
8519         switch( LOWORD(wParam) ) {\r
8520         case IDOK:\r
8521             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8522             EndDialog( hDlg, 0 );\r
8523             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8524             return TRUE;\r
8525         case IDCANCEL:\r
8526             EndDialog( hDlg, 1 );   \r
8527             return TRUE;\r
8528         case IDC_NFG_Edit:\r
8529             if( HIWORD(wParam) == EN_CHANGE ) {\r
8530                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8531 \r
8532                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8533             }\r
8534             return TRUE;\r
8535         case IDC_NFG_Random:\r
8536           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8537             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8538             return TRUE;\r
8539         }\r
8540 \r
8541         break;\r
8542     }\r
8543 \r
8544     return FALSE;\r
8545 }\r
8546 \r
8547 int NewGameFRC()\r
8548 {\r
8549     int result;\r
8550     int index = appData.defaultFrcPosition;\r
8551     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8552 \r
8553     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8554 \r
8555     if( result == 0 ) {\r
8556         appData.defaultFrcPosition = index;\r
8557     }\r
8558 \r
8559     return result;\r
8560 }\r
8561 \r
8562 /* [AS] Game list options. Refactored by HGM */\r
8563 \r
8564 HWND gameListOptionsDialog;\r
8565 \r
8566 // low-level front-end: clear text edit / list widget\r
8567 void\r
8568 \r
8569 GLT_ClearList()\r
8570 {\r
8571     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8572 }\r
8573 \r
8574 // low-level front-end: clear text edit / list widget\r
8575 void\r
8576 GLT_DeSelectList()\r
8577 {\r
8578     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8579 }\r
8580 \r
8581 // low-level front-end: append line to text edit / list widget\r
8582 void\r
8583 GLT_AddToList( char *name )\r
8584 {\r
8585     if( name != 0 ) {\r
8586             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8587     }\r
8588 }\r
8589 \r
8590 // low-level front-end: get line from text edit / list widget\r
8591 Boolean\r
8592 GLT_GetFromList( int index, char *name )\r
8593 {\r
8594     if( name != 0 ) {\r
8595             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8596                 return TRUE;\r
8597     }\r
8598     return FALSE;\r
8599 }\r
8600 \r
8601 void GLT_MoveSelection( HWND hDlg, int delta )\r
8602 {\r
8603     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8604     int idx2 = idx1 + delta;\r
8605     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8606 \r
8607     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8608         char buf[128];\r
8609 \r
8610         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8611         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8612         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8613         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8614     }\r
8615 }\r
8616 \r
8617 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8618 {\r
8619     switch( message )\r
8620     {\r
8621     case WM_INITDIALOG:\r
8622         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8623         \r
8624         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8625         Translate(hDlg, DLG_GameListOptions);\r
8626 \r
8627         /* Initialize list */\r
8628         GLT_TagsToList( lpUserGLT );\r
8629 \r
8630         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8631 \r
8632         break;\r
8633 \r
8634     case WM_COMMAND:\r
8635         switch( LOWORD(wParam) ) {\r
8636         case IDOK:\r
8637             GLT_ParseList();\r
8638             EndDialog( hDlg, 0 );\r
8639             return TRUE;\r
8640         case IDCANCEL:\r
8641             EndDialog( hDlg, 1 );\r
8642             return TRUE;\r
8643 \r
8644         case IDC_GLT_Default:\r
8645             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8646             return TRUE;\r
8647 \r
8648         case IDC_GLT_Restore:\r
8649             GLT_TagsToList( appData.gameListTags );\r
8650             return TRUE;\r
8651 \r
8652         case IDC_GLT_Up:\r
8653             GLT_MoveSelection( hDlg, -1 );\r
8654             return TRUE;\r
8655 \r
8656         case IDC_GLT_Down:\r
8657             GLT_MoveSelection( hDlg, +1 );\r
8658             return TRUE;\r
8659         }\r
8660 \r
8661         break;\r
8662     }\r
8663 \r
8664     return FALSE;\r
8665 }\r
8666 \r
8667 int GameListOptions()\r
8668 {\r
8669     int result;\r
8670     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8671 \r
8672       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8673 \r
8674     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8675 \r
8676     if( result == 0 ) {\r
8677         /* [AS] Memory leak here! */\r
8678         appData.gameListTags = strdup( lpUserGLT ); \r
8679     }\r
8680 \r
8681     return result;\r
8682 }\r
8683 \r
8684 VOID\r
8685 DisplayIcsInteractionTitle(char *str)\r
8686 {\r
8687   char consoleTitle[MSG_SIZ];\r
8688 \r
8689     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8690     SetWindowText(hwndConsole, consoleTitle);\r
8691 \r
8692     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8693       char buf[MSG_SIZ], *p = buf, *q;\r
8694         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8695       do {\r
8696         q = strchr(p, ';');\r
8697         if(q) *q++ = 0;\r
8698         if(*p) ChatPopUp(p);\r
8699       } while(p=q);\r
8700     }\r
8701 \r
8702     SetActiveWindow(hwndMain);\r
8703 }\r
8704 \r
8705 void\r
8706 DrawPosition(int fullRedraw, Board board)\r
8707 {\r
8708   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8709 }\r
8710 \r
8711 void NotifyFrontendLogin()\r
8712 {\r
8713         if (hwndConsole)\r
8714                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8715 }\r
8716 \r
8717 VOID\r
8718 ResetFrontEnd()\r
8719 {\r
8720   fromX = fromY = -1;\r
8721   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8722     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8723     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8724     dragInfo.lastpos = dragInfo.pos;\r
8725     dragInfo.start.x = dragInfo.start.y = -1;\r
8726     dragInfo.from = dragInfo.start;\r
8727     ReleaseCapture();\r
8728     DrawPosition(TRUE, NULL);\r
8729   }\r
8730   TagsPopDown();\r
8731 }\r
8732 \r
8733 \r
8734 VOID\r
8735 CommentPopUp(char *title, char *str)\r
8736 {\r
8737   HWND hwnd = GetActiveWindow();\r
8738   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8739   SAY(str);\r
8740   SetActiveWindow(hwnd);\r
8741 }\r
8742 \r
8743 VOID\r
8744 CommentPopDown(void)\r
8745 {\r
8746   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8747   if (commentDialog) {\r
8748     ShowWindow(commentDialog, SW_HIDE);\r
8749   }\r
8750   commentUp = FALSE;\r
8751 }\r
8752 \r
8753 VOID\r
8754 EditCommentPopUp(int index, char *title, char *str)\r
8755 {\r
8756   EitherCommentPopUp(index, title, str, TRUE);\r
8757 }\r
8758 \r
8759 \r
8760 int\r
8761 Roar()\r
8762 {\r
8763   MyPlaySound(&sounds[(int)SoundRoar]);\r
8764   return 1;\r
8765 }\r
8766 \r
8767 VOID\r
8768 RingBell()\r
8769 {\r
8770   MyPlaySound(&sounds[(int)SoundMove]);\r
8771 }\r
8772 \r
8773 VOID PlayIcsWinSound()\r
8774 {\r
8775   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8776 }\r
8777 \r
8778 VOID PlayIcsLossSound()\r
8779 {\r
8780   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8781 }\r
8782 \r
8783 VOID PlayIcsDrawSound()\r
8784 {\r
8785   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8786 }\r
8787 \r
8788 VOID PlayIcsUnfinishedSound()\r
8789 {\r
8790   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8791 }\r
8792 \r
8793 VOID\r
8794 PlayAlarmSound()\r
8795 {\r
8796   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8797 }\r
8798 \r
8799 VOID\r
8800 PlayTellSound()\r
8801 {\r
8802   MyPlaySound(&textAttribs[ColorTell].sound);\r
8803 }\r
8804 \r
8805 \r
8806 VOID\r
8807 EchoOn()\r
8808 {\r
8809   HWND hInput;\r
8810   consoleEcho = TRUE;\r
8811   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8812   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8813   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8814 }\r
8815 \r
8816 \r
8817 VOID\r
8818 EchoOff()\r
8819 {\r
8820   CHARFORMAT cf;\r
8821   HWND hInput;\r
8822   consoleEcho = FALSE;\r
8823   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8824   /* This works OK: set text and background both to the same color */\r
8825   cf = consoleCF;\r
8826   cf.crTextColor = COLOR_ECHOOFF;\r
8827   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8828   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8829 }\r
8830 \r
8831 /* No Raw()...? */\r
8832 \r
8833 void Colorize(ColorClass cc, int continuation)\r
8834 {\r
8835   currentColorClass = cc;\r
8836   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8837   consoleCF.crTextColor = textAttribs[cc].color;\r
8838   consoleCF.dwEffects = textAttribs[cc].effects;\r
8839   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8840 }\r
8841 \r
8842 char *\r
8843 UserName()\r
8844 {\r
8845   static char buf[MSG_SIZ];\r
8846   DWORD bufsiz = MSG_SIZ;\r
8847 \r
8848   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8849         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8850   }\r
8851   if (!GetUserName(buf, &bufsiz)) {\r
8852     /*DisplayError("Error getting user name", GetLastError());*/\r
8853     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8854   }\r
8855   return buf;\r
8856 }\r
8857 \r
8858 char *\r
8859 HostName()\r
8860 {\r
8861   static char buf[MSG_SIZ];\r
8862   DWORD bufsiz = MSG_SIZ;\r
8863 \r
8864   if (!GetComputerName(buf, &bufsiz)) {\r
8865     /*DisplayError("Error getting host name", GetLastError());*/\r
8866     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8867   }\r
8868   return buf;\r
8869 }\r
8870 \r
8871 \r
8872 int\r
8873 ClockTimerRunning()\r
8874 {\r
8875   return clockTimerEvent != 0;\r
8876 }\r
8877 \r
8878 int\r
8879 StopClockTimer()\r
8880 {\r
8881   if (clockTimerEvent == 0) return FALSE;\r
8882   KillTimer(hwndMain, clockTimerEvent);\r
8883   clockTimerEvent = 0;\r
8884   return TRUE;\r
8885 }\r
8886 \r
8887 void\r
8888 StartClockTimer(long millisec)\r
8889 {\r
8890   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8891                              (UINT) millisec, NULL);\r
8892 }\r
8893 \r
8894 void\r
8895 DisplayWhiteClock(long timeRemaining, int highlight)\r
8896 {\r
8897   HDC hdc;\r
8898   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8899 \r
8900   if(appData.noGUI) return;\r
8901   hdc = GetDC(hwndMain);\r
8902   if (!IsIconic(hwndMain)) {\r
8903     DisplayAClock(hdc, timeRemaining, highlight, \r
8904                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8905   }\r
8906   if (highlight && iconCurrent == iconBlack) {\r
8907     iconCurrent = iconWhite;\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 void\r
8919 DisplayBlackClock(long timeRemaining, int highlight)\r
8920 {\r
8921   HDC hdc;\r
8922   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8923 \r
8924 \r
8925   if(appData.noGUI) return;\r
8926   hdc = GetDC(hwndMain);\r
8927   if (!IsIconic(hwndMain)) {\r
8928     DisplayAClock(hdc, timeRemaining, highlight, \r
8929                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8930   }\r
8931   if (highlight && iconCurrent == iconWhite) {\r
8932     iconCurrent = iconBlack;\r
8933     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8934     if (IsIconic(hwndMain)) {\r
8935       DrawIcon(hdc, 2, 2, iconCurrent);\r
8936     }\r
8937   }\r
8938   (void) ReleaseDC(hwndMain, hdc);\r
8939   if (hwndConsole)\r
8940     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8941 }\r
8942 \r
8943 \r
8944 int\r
8945 LoadGameTimerRunning()\r
8946 {\r
8947   return loadGameTimerEvent != 0;\r
8948 }\r
8949 \r
8950 int\r
8951 StopLoadGameTimer()\r
8952 {\r
8953   if (loadGameTimerEvent == 0) return FALSE;\r
8954   KillTimer(hwndMain, loadGameTimerEvent);\r
8955   loadGameTimerEvent = 0;\r
8956   return TRUE;\r
8957 }\r
8958 \r
8959 void\r
8960 StartLoadGameTimer(long millisec)\r
8961 {\r
8962   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8963                                 (UINT) millisec, NULL);\r
8964 }\r
8965 \r
8966 void\r
8967 AutoSaveGame()\r
8968 {\r
8969   char *defName;\r
8970   FILE *f;\r
8971   char fileTitle[MSG_SIZ];\r
8972 \r
8973   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8974   f = OpenFileDialog(hwndMain, "a", defName,\r
8975                      appData.oldSaveStyle ? "gam" : "pgn",\r
8976                      GAME_FILT, \r
8977                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8978   if (f != NULL) {\r
8979     SaveGame(f, 0, "");\r
8980     fclose(f);\r
8981   }\r
8982 }\r
8983 \r
8984 \r
8985 void\r
8986 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8987 {\r
8988   if (delayedTimerEvent != 0) {\r
8989     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8990       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8991     }\r
8992     KillTimer(hwndMain, delayedTimerEvent);\r
8993     delayedTimerEvent = 0;\r
8994     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8995     delayedTimerCallback();\r
8996   }\r
8997   delayedTimerCallback = cb;\r
8998   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8999                                 (UINT) millisec, NULL);\r
9000 }\r
9001 \r
9002 DelayedEventCallback\r
9003 GetDelayedEvent()\r
9004 {\r
9005   if (delayedTimerEvent) {\r
9006     return delayedTimerCallback;\r
9007   } else {\r
9008     return NULL;\r
9009   }\r
9010 }\r
9011 \r
9012 void\r
9013 CancelDelayedEvent()\r
9014 {\r
9015   if (delayedTimerEvent) {\r
9016     KillTimer(hwndMain, delayedTimerEvent);\r
9017     delayedTimerEvent = 0;\r
9018   }\r
9019 }\r
9020 \r
9021 DWORD GetWin32Priority(int nice)\r
9022 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9023 /*\r
9024 REALTIME_PRIORITY_CLASS     0x00000100\r
9025 HIGH_PRIORITY_CLASS         0x00000080\r
9026 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9027 NORMAL_PRIORITY_CLASS       0x00000020\r
9028 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9029 IDLE_PRIORITY_CLASS         0x00000040\r
9030 */\r
9031         if (nice < -15) return 0x00000080;\r
9032         if (nice < 0)   return 0x00008000;\r
9033         if (nice == 0)  return 0x00000020;\r
9034         if (nice < 15)  return 0x00004000;\r
9035         return 0x00000040;\r
9036 }\r
9037 \r
9038 void RunCommand(char *cmdLine)\r
9039 {\r
9040   /* Now create the child process. */\r
9041   STARTUPINFO siStartInfo;\r
9042   PROCESS_INFORMATION piProcInfo;\r
9043 \r
9044   siStartInfo.cb = sizeof(STARTUPINFO);\r
9045   siStartInfo.lpReserved = NULL;\r
9046   siStartInfo.lpDesktop = NULL;\r
9047   siStartInfo.lpTitle = NULL;\r
9048   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9049   siStartInfo.cbReserved2 = 0;\r
9050   siStartInfo.lpReserved2 = NULL;\r
9051   siStartInfo.hStdInput = NULL;\r
9052   siStartInfo.hStdOutput = NULL;\r
9053   siStartInfo.hStdError = NULL;\r
9054 \r
9055   CreateProcess(NULL,\r
9056                 cmdLine,           /* command line */\r
9057                 NULL,      /* process security attributes */\r
9058                 NULL,      /* primary thread security attrs */\r
9059                 TRUE,      /* handles are inherited */\r
9060                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9061                 NULL,      /* use parent's environment */\r
9062                 NULL,\r
9063                 &siStartInfo, /* STARTUPINFO pointer */\r
9064                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9065 \r
9066   CloseHandle(piProcInfo.hThread);\r
9067 }\r
9068 \r
9069 /* Start a child process running the given program.\r
9070    The process's standard output can be read from "from", and its\r
9071    standard input can be written to "to".\r
9072    Exit with fatal error if anything goes wrong.\r
9073    Returns an opaque pointer that can be used to destroy the process\r
9074    later.\r
9075 */\r
9076 int\r
9077 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9078 {\r
9079 #define BUFSIZE 4096\r
9080 \r
9081   HANDLE hChildStdinRd, hChildStdinWr,\r
9082     hChildStdoutRd, hChildStdoutWr;\r
9083   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9084   SECURITY_ATTRIBUTES saAttr;\r
9085   BOOL fSuccess;\r
9086   PROCESS_INFORMATION piProcInfo;\r
9087   STARTUPINFO siStartInfo;\r
9088   ChildProc *cp;\r
9089   char buf[MSG_SIZ];\r
9090   DWORD err;\r
9091 \r
9092   if (appData.debugMode) {\r
9093     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9094   }\r
9095 \r
9096   *pr = NoProc;\r
9097 \r
9098   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9099   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9100   saAttr.bInheritHandle = TRUE;\r
9101   saAttr.lpSecurityDescriptor = NULL;\r
9102 \r
9103   /*\r
9104    * The steps for redirecting child's STDOUT:\r
9105    *     1. Create anonymous pipe to be STDOUT for child.\r
9106    *     2. Create a noninheritable duplicate of read handle,\r
9107    *         and close the inheritable read handle.\r
9108    */\r
9109 \r
9110   /* Create a pipe for the child's STDOUT. */\r
9111   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9112     return GetLastError();\r
9113   }\r
9114 \r
9115   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9116   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9117                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9118                              FALSE,     /* not inherited */\r
9119                              DUPLICATE_SAME_ACCESS);\r
9120   if (! fSuccess) {\r
9121     return GetLastError();\r
9122   }\r
9123   CloseHandle(hChildStdoutRd);\r
9124 \r
9125   /*\r
9126    * The steps for redirecting child's STDIN:\r
9127    *     1. Create anonymous pipe to be STDIN for child.\r
9128    *     2. Create a noninheritable duplicate of write handle,\r
9129    *         and close the inheritable write handle.\r
9130    */\r
9131 \r
9132   /* Create a pipe for the child's STDIN. */\r
9133   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9134     return GetLastError();\r
9135   }\r
9136 \r
9137   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9138   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9139                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9140                              FALSE,     /* not inherited */\r
9141                              DUPLICATE_SAME_ACCESS);\r
9142   if (! fSuccess) {\r
9143     return GetLastError();\r
9144   }\r
9145   CloseHandle(hChildStdinWr);\r
9146 \r
9147   /* Arrange to (1) look in dir for the child .exe file, and\r
9148    * (2) have dir be the child's working directory.  Interpret\r
9149    * dir relative to the directory WinBoard loaded from. */\r
9150   GetCurrentDirectory(MSG_SIZ, buf);\r
9151   SetCurrentDirectory(installDir);\r
9152   SetCurrentDirectory(dir);\r
9153 \r
9154   /* Now create the child process. */\r
9155 \r
9156   siStartInfo.cb = sizeof(STARTUPINFO);\r
9157   siStartInfo.lpReserved = NULL;\r
9158   siStartInfo.lpDesktop = NULL;\r
9159   siStartInfo.lpTitle = NULL;\r
9160   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9161   siStartInfo.cbReserved2 = 0;\r
9162   siStartInfo.lpReserved2 = NULL;\r
9163   siStartInfo.hStdInput = hChildStdinRd;\r
9164   siStartInfo.hStdOutput = hChildStdoutWr;\r
9165   siStartInfo.hStdError = hChildStdoutWr;\r
9166 \r
9167   fSuccess = CreateProcess(NULL,\r
9168                            cmdLine,        /* command line */\r
9169                            NULL,           /* process security attributes */\r
9170                            NULL,           /* primary thread security attrs */\r
9171                            TRUE,           /* handles are inherited */\r
9172                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9173                            NULL,           /* use parent's environment */\r
9174                            NULL,\r
9175                            &siStartInfo, /* STARTUPINFO pointer */\r
9176                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9177 \r
9178   err = GetLastError();\r
9179   SetCurrentDirectory(buf); /* return to prev directory */\r
9180   if (! fSuccess) {\r
9181     return err;\r
9182   }\r
9183 \r
9184   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9185     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9186     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9187   }\r
9188 \r
9189   /* Close the handles we don't need in the parent */\r
9190   CloseHandle(piProcInfo.hThread);\r
9191   CloseHandle(hChildStdinRd);\r
9192   CloseHandle(hChildStdoutWr);\r
9193 \r
9194   /* Prepare return value */\r
9195   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9196   cp->kind = CPReal;\r
9197   cp->hProcess = piProcInfo.hProcess;\r
9198   cp->pid = piProcInfo.dwProcessId;\r
9199   cp->hFrom = hChildStdoutRdDup;\r
9200   cp->hTo = hChildStdinWrDup;\r
9201 \r
9202   *pr = (void *) cp;\r
9203 \r
9204   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9205      2000 where engines sometimes don't see the initial command(s)\r
9206      from WinBoard and hang.  I don't understand how that can happen,\r
9207      but the Sleep is harmless, so I've put it in.  Others have also\r
9208      reported what may be the same problem, so hopefully this will fix\r
9209      it for them too.  */\r
9210   Sleep(500);\r
9211 \r
9212   return NO_ERROR;\r
9213 }\r
9214 \r
9215 \r
9216 void\r
9217 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9218 {\r
9219   ChildProc *cp; int result;\r
9220 \r
9221   cp = (ChildProc *) pr;\r
9222   if (cp == NULL) return;\r
9223 \r
9224   switch (cp->kind) {\r
9225   case CPReal:\r
9226     /* TerminateProcess is considered harmful, so... */\r
9227     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9228     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9229     /* The following doesn't work because the chess program\r
9230        doesn't "have the same console" as WinBoard.  Maybe\r
9231        we could arrange for this even though neither WinBoard\r
9232        nor the chess program uses a console for stdio? */\r
9233     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9234 \r
9235     /* [AS] Special termination modes for misbehaving programs... */\r
9236     if( signal & 8 ) { \r
9237         result = TerminateProcess( cp->hProcess, 0 );\r
9238 \r
9239         if ( appData.debugMode) {\r
9240             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9241         }\r
9242     }\r
9243     else if( signal & 4 ) {\r
9244         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9245 \r
9246         if( dw != WAIT_OBJECT_0 ) {\r
9247             result = TerminateProcess( cp->hProcess, 0 );\r
9248 \r
9249             if ( appData.debugMode) {\r
9250                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9251             }\r
9252 \r
9253         }\r
9254     }\r
9255 \r
9256     CloseHandle(cp->hProcess);\r
9257     break;\r
9258 \r
9259   case CPComm:\r
9260     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9261     break;\r
9262 \r
9263   case CPSock:\r
9264     closesocket(cp->sock);\r
9265     WSACleanup();\r
9266     break;\r
9267 \r
9268   case CPRcmd:\r
9269     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9270     closesocket(cp->sock);\r
9271     closesocket(cp->sock2);\r
9272     WSACleanup();\r
9273     break;\r
9274   }\r
9275   free(cp);\r
9276 }\r
9277 \r
9278 void\r
9279 InterruptChildProcess(ProcRef pr)\r
9280 {\r
9281   ChildProc *cp;\r
9282 \r
9283   cp = (ChildProc *) pr;\r
9284   if (cp == NULL) return;\r
9285   switch (cp->kind) {\r
9286   case CPReal:\r
9287     /* The following doesn't work because the chess program\r
9288        doesn't "have the same console" as WinBoard.  Maybe\r
9289        we could arrange for this even though neither WinBoard\r
9290        nor the chess program uses a console for stdio */\r
9291     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9292     break;\r
9293 \r
9294   case CPComm:\r
9295   case CPSock:\r
9296     /* Can't interrupt */\r
9297     break;\r
9298 \r
9299   case CPRcmd:\r
9300     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9301     break;\r
9302   }\r
9303 }\r
9304 \r
9305 \r
9306 int\r
9307 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9308 {\r
9309   char cmdLine[MSG_SIZ];\r
9310 \r
9311   if (port[0] == NULLCHAR) {\r
9312     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9313   } else {\r
9314     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9315   }\r
9316   return StartChildProcess(cmdLine, "", pr);\r
9317 }\r
9318 \r
9319 \r
9320 /* Code to open TCP sockets */\r
9321 \r
9322 int\r
9323 OpenTCP(char *host, char *port, ProcRef *pr)\r
9324 {\r
9325   ChildProc *cp;\r
9326   int err;\r
9327   SOCKET s;\r
9328 \r
9329   struct sockaddr_in sa, mysa;\r
9330   struct hostent FAR *hp;\r
9331   unsigned short uport;\r
9332   WORD wVersionRequested;\r
9333   WSADATA wsaData;\r
9334 \r
9335   /* Initialize socket DLL */\r
9336   wVersionRequested = MAKEWORD(1, 1);\r
9337   err = WSAStartup(wVersionRequested, &wsaData);\r
9338   if (err != 0) return err;\r
9339 \r
9340   /* Make socket */\r
9341   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9342     err = WSAGetLastError();\r
9343     WSACleanup();\r
9344     return err;\r
9345   }\r
9346 \r
9347   /* Bind local address using (mostly) don't-care values.\r
9348    */\r
9349   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9350   mysa.sin_family = AF_INET;\r
9351   mysa.sin_addr.s_addr = INADDR_ANY;\r
9352   uport = (unsigned short) 0;\r
9353   mysa.sin_port = htons(uport);\r
9354   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9355       == SOCKET_ERROR) {\r
9356     err = WSAGetLastError();\r
9357     WSACleanup();\r
9358     return err;\r
9359   }\r
9360 \r
9361   /* Resolve remote host name */\r
9362   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9363   if (!(hp = gethostbyname(host))) {\r
9364     unsigned int b0, b1, b2, b3;\r
9365 \r
9366     err = WSAGetLastError();\r
9367 \r
9368     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9369       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9370       hp->h_addrtype = AF_INET;\r
9371       hp->h_length = 4;\r
9372       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9373       hp->h_addr_list[0] = (char *) malloc(4);\r
9374       hp->h_addr_list[0][0] = (char) b0;\r
9375       hp->h_addr_list[0][1] = (char) b1;\r
9376       hp->h_addr_list[0][2] = (char) b2;\r
9377       hp->h_addr_list[0][3] = (char) b3;\r
9378     } else {\r
9379       WSACleanup();\r
9380       return err;\r
9381     }\r
9382   }\r
9383   sa.sin_family = hp->h_addrtype;\r
9384   uport = (unsigned short) atoi(port);\r
9385   sa.sin_port = htons(uport);\r
9386   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9387 \r
9388   /* Make connection */\r
9389   if (connect(s, (struct sockaddr *) &sa,\r
9390               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9391     err = WSAGetLastError();\r
9392     WSACleanup();\r
9393     return err;\r
9394   }\r
9395 \r
9396   /* Prepare return value */\r
9397   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9398   cp->kind = CPSock;\r
9399   cp->sock = s;\r
9400   *pr = (ProcRef *) cp;\r
9401 \r
9402   return NO_ERROR;\r
9403 }\r
9404 \r
9405 int\r
9406 OpenCommPort(char *name, ProcRef *pr)\r
9407 {\r
9408   HANDLE h;\r
9409   COMMTIMEOUTS ct;\r
9410   ChildProc *cp;\r
9411   char fullname[MSG_SIZ];\r
9412 \r
9413   if (*name != '\\')\r
9414     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9415   else\r
9416     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9417 \r
9418   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9419                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9420   if (h == (HANDLE) -1) {\r
9421     return GetLastError();\r
9422   }\r
9423   hCommPort = h;\r
9424 \r
9425   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9426 \r
9427   /* Accumulate characters until a 100ms pause, then parse */\r
9428   ct.ReadIntervalTimeout = 100;\r
9429   ct.ReadTotalTimeoutMultiplier = 0;\r
9430   ct.ReadTotalTimeoutConstant = 0;\r
9431   ct.WriteTotalTimeoutMultiplier = 0;\r
9432   ct.WriteTotalTimeoutConstant = 0;\r
9433   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9434 \r
9435   /* Prepare return value */\r
9436   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9437   cp->kind = CPComm;\r
9438   cp->hFrom = h;\r
9439   cp->hTo = h;\r
9440   *pr = (ProcRef *) cp;\r
9441 \r
9442   return NO_ERROR;\r
9443 }\r
9444 \r
9445 int\r
9446 OpenLoopback(ProcRef *pr)\r
9447 {\r
9448   DisplayFatalError(_("Not implemented"), 0, 1);\r
9449   return NO_ERROR;\r
9450 }\r
9451 \r
9452 \r
9453 int\r
9454 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9455 {\r
9456   ChildProc *cp;\r
9457   int err;\r
9458   SOCKET s, s2, s3;\r
9459   struct sockaddr_in sa, mysa;\r
9460   struct hostent FAR *hp;\r
9461   unsigned short uport;\r
9462   WORD wVersionRequested;\r
9463   WSADATA wsaData;\r
9464   int fromPort;\r
9465   char stderrPortStr[MSG_SIZ];\r
9466 \r
9467   /* Initialize socket DLL */\r
9468   wVersionRequested = MAKEWORD(1, 1);\r
9469   err = WSAStartup(wVersionRequested, &wsaData);\r
9470   if (err != 0) return err;\r
9471 \r
9472   /* Resolve remote host name */\r
9473   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9474   if (!(hp = gethostbyname(host))) {\r
9475     unsigned int b0, b1, b2, b3;\r
9476 \r
9477     err = WSAGetLastError();\r
9478 \r
9479     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9480       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9481       hp->h_addrtype = AF_INET;\r
9482       hp->h_length = 4;\r
9483       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9484       hp->h_addr_list[0] = (char *) malloc(4);\r
9485       hp->h_addr_list[0][0] = (char) b0;\r
9486       hp->h_addr_list[0][1] = (char) b1;\r
9487       hp->h_addr_list[0][2] = (char) b2;\r
9488       hp->h_addr_list[0][3] = (char) b3;\r
9489     } else {\r
9490       WSACleanup();\r
9491       return err;\r
9492     }\r
9493   }\r
9494   sa.sin_family = hp->h_addrtype;\r
9495   uport = (unsigned short) 514;\r
9496   sa.sin_port = htons(uport);\r
9497   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9498 \r
9499   /* Bind local socket to unused "privileged" port address\r
9500    */\r
9501   s = INVALID_SOCKET;\r
9502   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9503   mysa.sin_family = AF_INET;\r
9504   mysa.sin_addr.s_addr = INADDR_ANY;\r
9505   for (fromPort = 1023;; fromPort--) {\r
9506     if (fromPort < 0) {\r
9507       WSACleanup();\r
9508       return WSAEADDRINUSE;\r
9509     }\r
9510     if (s == INVALID_SOCKET) {\r
9511       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9512         err = WSAGetLastError();\r
9513         WSACleanup();\r
9514         return err;\r
9515       }\r
9516     }\r
9517     uport = (unsigned short) fromPort;\r
9518     mysa.sin_port = htons(uport);\r
9519     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9520         == SOCKET_ERROR) {\r
9521       err = WSAGetLastError();\r
9522       if (err == WSAEADDRINUSE) continue;\r
9523       WSACleanup();\r
9524       return err;\r
9525     }\r
9526     if (connect(s, (struct sockaddr *) &sa,\r
9527       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9528       err = WSAGetLastError();\r
9529       if (err == WSAEADDRINUSE) {\r
9530         closesocket(s);\r
9531         s = -1;\r
9532         continue;\r
9533       }\r
9534       WSACleanup();\r
9535       return err;\r
9536     }\r
9537     break;\r
9538   }\r
9539 \r
9540   /* Bind stderr local socket to unused "privileged" port address\r
9541    */\r
9542   s2 = INVALID_SOCKET;\r
9543   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9544   mysa.sin_family = AF_INET;\r
9545   mysa.sin_addr.s_addr = INADDR_ANY;\r
9546   for (fromPort = 1023;; fromPort--) {\r
9547     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9548     if (fromPort < 0) {\r
9549       (void) closesocket(s);\r
9550       WSACleanup();\r
9551       return WSAEADDRINUSE;\r
9552     }\r
9553     if (s2 == INVALID_SOCKET) {\r
9554       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9555         err = WSAGetLastError();\r
9556         closesocket(s);\r
9557         WSACleanup();\r
9558         return err;\r
9559       }\r
9560     }\r
9561     uport = (unsigned short) fromPort;\r
9562     mysa.sin_port = htons(uport);\r
9563     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9564         == SOCKET_ERROR) {\r
9565       err = WSAGetLastError();\r
9566       if (err == WSAEADDRINUSE) continue;\r
9567       (void) closesocket(s);\r
9568       WSACleanup();\r
9569       return err;\r
9570     }\r
9571     if (listen(s2, 1) == SOCKET_ERROR) {\r
9572       err = WSAGetLastError();\r
9573       if (err == WSAEADDRINUSE) {\r
9574         closesocket(s2);\r
9575         s2 = INVALID_SOCKET;\r
9576         continue;\r
9577       }\r
9578       (void) closesocket(s);\r
9579       (void) closesocket(s2);\r
9580       WSACleanup();\r
9581       return err;\r
9582     }\r
9583     break;\r
9584   }\r
9585   prevStderrPort = fromPort; // remember port used\r
9586   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9587 \r
9588   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9589     err = WSAGetLastError();\r
9590     (void) closesocket(s);\r
9591     (void) closesocket(s2);\r
9592     WSACleanup();\r
9593     return err;\r
9594   }\r
9595 \r
9596   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9597     err = WSAGetLastError();\r
9598     (void) closesocket(s);\r
9599     (void) closesocket(s2);\r
9600     WSACleanup();\r
9601     return err;\r
9602   }\r
9603   if (*user == NULLCHAR) user = UserName();\r
9604   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9605     err = WSAGetLastError();\r
9606     (void) closesocket(s);\r
9607     (void) closesocket(s2);\r
9608     WSACleanup();\r
9609     return err;\r
9610   }\r
9611   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9612     err = WSAGetLastError();\r
9613     (void) closesocket(s);\r
9614     (void) closesocket(s2);\r
9615     WSACleanup();\r
9616     return err;\r
9617   }\r
9618 \r
9619   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9620     err = WSAGetLastError();\r
9621     (void) closesocket(s);\r
9622     (void) closesocket(s2);\r
9623     WSACleanup();\r
9624     return err;\r
9625   }\r
9626   (void) closesocket(s2);  /* Stop listening */\r
9627 \r
9628   /* Prepare return value */\r
9629   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9630   cp->kind = CPRcmd;\r
9631   cp->sock = s;\r
9632   cp->sock2 = s3;\r
9633   *pr = (ProcRef *) cp;\r
9634 \r
9635   return NO_ERROR;\r
9636 }\r
9637 \r
9638 \r
9639 InputSourceRef\r
9640 AddInputSource(ProcRef pr, int lineByLine,\r
9641                InputCallback func, VOIDSTAR closure)\r
9642 {\r
9643   InputSource *is, *is2 = NULL;\r
9644   ChildProc *cp = (ChildProc *) pr;\r
9645 \r
9646   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9647   is->lineByLine = lineByLine;\r
9648   is->func = func;\r
9649   is->closure = closure;\r
9650   is->second = NULL;\r
9651   is->next = is->buf;\r
9652   if (pr == NoProc) {\r
9653     is->kind = CPReal;\r
9654     consoleInputSource = is;\r
9655   } else {\r
9656     is->kind = cp->kind;\r
9657     /* \r
9658         [AS] Try to avoid a race condition if the thread is given control too early:\r
9659         we create all threads suspended so that the is->hThread variable can be\r
9660         safely assigned, then let the threads start with ResumeThread.\r
9661     */\r
9662     switch (cp->kind) {\r
9663     case CPReal:\r
9664       is->hFile = cp->hFrom;\r
9665       cp->hFrom = NULL; /* now owned by InputThread */\r
9666       is->hThread =\r
9667         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9668                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9669       break;\r
9670 \r
9671     case CPComm:\r
9672       is->hFile = cp->hFrom;\r
9673       cp->hFrom = NULL; /* now owned by InputThread */\r
9674       is->hThread =\r
9675         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9676                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9677       break;\r
9678 \r
9679     case CPSock:\r
9680       is->sock = cp->sock;\r
9681       is->hThread =\r
9682         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9683                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9684       break;\r
9685 \r
9686     case CPRcmd:\r
9687       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9688       *is2 = *is;\r
9689       is->sock = cp->sock;\r
9690       is->second = is2;\r
9691       is2->sock = cp->sock2;\r
9692       is2->second = is2;\r
9693       is->hThread =\r
9694         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9695                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9696       is2->hThread =\r
9697         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9698                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9699       break;\r
9700     }\r
9701 \r
9702     if( is->hThread != NULL ) {\r
9703         ResumeThread( is->hThread );\r
9704     }\r
9705 \r
9706     if( is2 != NULL && is2->hThread != NULL ) {\r
9707         ResumeThread( is2->hThread );\r
9708     }\r
9709   }\r
9710 \r
9711   return (InputSourceRef) is;\r
9712 }\r
9713 \r
9714 void\r
9715 RemoveInputSource(InputSourceRef isr)\r
9716 {\r
9717   InputSource *is;\r
9718 \r
9719   is = (InputSource *) isr;\r
9720   is->hThread = NULL;  /* tell thread to stop */\r
9721   CloseHandle(is->hThread);\r
9722   if (is->second != NULL) {\r
9723     is->second->hThread = NULL;\r
9724     CloseHandle(is->second->hThread);\r
9725   }\r
9726 }\r
9727 \r
9728 int no_wrap(char *message, int count)\r
9729 {\r
9730     ConsoleOutput(message, count, FALSE);\r
9731     return count;\r
9732 }\r
9733 \r
9734 int\r
9735 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9736 {\r
9737   DWORD dOutCount;\r
9738   int outCount = SOCKET_ERROR;\r
9739   ChildProc *cp = (ChildProc *) pr;\r
9740   static OVERLAPPED ovl;\r
9741   static int line = 0;\r
9742 \r
9743   if (pr == NoProc)\r
9744   {\r
9745     if (appData.noJoin || !appData.useInternalWrap)\r
9746       return no_wrap(message, count);\r
9747     else\r
9748     {\r
9749       int width = get_term_width();\r
9750       int len = wrap(NULL, message, count, width, &line);\r
9751       char *msg = malloc(len);\r
9752       int dbgchk;\r
9753 \r
9754       if (!msg)\r
9755         return no_wrap(message, count);\r
9756       else\r
9757       {\r
9758         dbgchk = wrap(msg, message, count, width, &line);\r
9759         if (dbgchk != len && appData.debugMode)\r
9760             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9761         ConsoleOutput(msg, len, FALSE);\r
9762         free(msg);\r
9763         return len;\r
9764       }\r
9765     }\r
9766   }\r
9767 \r
9768   if (ovl.hEvent == NULL) {\r
9769     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9770   }\r
9771   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9772 \r
9773   switch (cp->kind) {\r
9774   case CPSock:\r
9775   case CPRcmd:\r
9776     outCount = send(cp->sock, message, count, 0);\r
9777     if (outCount == SOCKET_ERROR) {\r
9778       *outError = WSAGetLastError();\r
9779     } else {\r
9780       *outError = NO_ERROR;\r
9781     }\r
9782     break;\r
9783 \r
9784   case CPReal:\r
9785     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9786                   &dOutCount, NULL)) {\r
9787       *outError = NO_ERROR;\r
9788       outCount = (int) dOutCount;\r
9789     } else {\r
9790       *outError = GetLastError();\r
9791     }\r
9792     break;\r
9793 \r
9794   case CPComm:\r
9795     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9796                             &dOutCount, &ovl);\r
9797     if (*outError == NO_ERROR) {\r
9798       outCount = (int) dOutCount;\r
9799     }\r
9800     break;\r
9801   }\r
9802   return outCount;\r
9803 }\r
9804 \r
9805 void\r
9806 DoSleep(int n)\r
9807 {\r
9808     if(n != 0) Sleep(n);\r
9809 }\r
9810 \r
9811 int\r
9812 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9813                        long msdelay)\r
9814 {\r
9815   /* Ignore delay, not implemented for WinBoard */\r
9816   return OutputToProcess(pr, message, count, outError);\r
9817 }\r
9818 \r
9819 \r
9820 void\r
9821 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9822                         char *buf, int count, int error)\r
9823 {\r
9824   DisplayFatalError(_("Not implemented"), 0, 1);\r
9825 }\r
9826 \r
9827 /* see wgamelist.c for Game List functions */\r
9828 /* see wedittags.c for Edit Tags functions */\r
9829 \r
9830 \r
9831 int\r
9832 ICSInitScript()\r
9833 {\r
9834   FILE *f;\r
9835   char buf[MSG_SIZ];\r
9836   char *dummy;\r
9837 \r
9838   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9839     f = fopen(buf, "r");\r
9840     if (f != NULL) {\r
9841       ProcessICSInitScript(f);\r
9842       fclose(f);\r
9843       return TRUE;\r
9844     }\r
9845   }\r
9846   return FALSE;\r
9847 }\r
9848 \r
9849 \r
9850 VOID\r
9851 StartAnalysisClock()\r
9852 {\r
9853   if (analysisTimerEvent) return;\r
9854   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9855                                         (UINT) 2000, NULL);\r
9856 }\r
9857 \r
9858 VOID\r
9859 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9860 {\r
9861   highlightInfo.sq[0].x = fromX;\r
9862   highlightInfo.sq[0].y = fromY;\r
9863   highlightInfo.sq[1].x = toX;\r
9864   highlightInfo.sq[1].y = toY;\r
9865 }\r
9866 \r
9867 VOID\r
9868 ClearHighlights()\r
9869 {\r
9870   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9871     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9872 }\r
9873 \r
9874 VOID\r
9875 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9876 {\r
9877   premoveHighlightInfo.sq[0].x = fromX;\r
9878   premoveHighlightInfo.sq[0].y = fromY;\r
9879   premoveHighlightInfo.sq[1].x = toX;\r
9880   premoveHighlightInfo.sq[1].y = toY;\r
9881 }\r
9882 \r
9883 VOID\r
9884 ClearPremoveHighlights()\r
9885 {\r
9886   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9887     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9888 }\r
9889 \r
9890 VOID\r
9891 ShutDownFrontEnd()\r
9892 {\r
9893   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9894   DeleteClipboardTempFiles();\r
9895 }\r
9896 \r
9897 void\r
9898 BoardToTop()\r
9899 {\r
9900     if (IsIconic(hwndMain))\r
9901       ShowWindow(hwndMain, SW_RESTORE);\r
9902 \r
9903     SetActiveWindow(hwndMain);\r
9904 }\r
9905 \r
9906 /*\r
9907  * Prototypes for animation support routines\r
9908  */\r
9909 static void ScreenSquare(int column, int row, POINT * pt);\r
9910 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9911      POINT frames[], int * nFrames);\r
9912 \r
9913 \r
9914 #define kFactor 4\r
9915 \r
9916 void\r
9917 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9918 {       // [HGM] atomic: animate blast wave\r
9919         int i;\r
9920 \r
9921         explodeInfo.fromX = fromX;\r
9922         explodeInfo.fromY = fromY;\r
9923         explodeInfo.toX = toX;\r
9924         explodeInfo.toY = toY;\r
9925         for(i=1; i<4*kFactor; i++) {\r
9926             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9927             DrawPosition(FALSE, board);\r
9928             Sleep(appData.animSpeed);\r
9929         }\r
9930         explodeInfo.radius = 0;\r
9931         DrawPosition(TRUE, board);\r
9932 }\r
9933 \r
9934 void\r
9935 AnimateMove(board, fromX, fromY, toX, toY)\r
9936      Board board;\r
9937      int fromX;\r
9938      int fromY;\r
9939      int toX;\r
9940      int toY;\r
9941 {\r
9942   ChessSquare piece;\r
9943   int x = toX, y = toY;\r
9944   POINT start, finish, mid;\r
9945   POINT frames[kFactor * 2 + 1];\r
9946   int nFrames, n;\r
9947 \r
9948   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9949 \r
9950   if (!appData.animate) return;\r
9951   if (doingSizing) return;\r
9952   if (fromY < 0 || fromX < 0) return;\r
9953   piece = board[fromY][fromX];\r
9954   if (piece >= EmptySquare) return;\r
9955 \r
9956   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
9957 \r
9958 again:\r
9959 \r
9960   ScreenSquare(fromX, fromY, &start);\r
9961   ScreenSquare(toX, toY, &finish);\r
9962 \r
9963   /* All moves except knight jumps move in straight line */\r
9964   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9965     mid.x = start.x + (finish.x - start.x) / 2;\r
9966     mid.y = start.y + (finish.y - start.y) / 2;\r
9967   } else {\r
9968     /* Knight: make straight movement then diagonal */\r
9969     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9970        mid.x = start.x + (finish.x - start.x) / 2;\r
9971        mid.y = start.y;\r
9972      } else {\r
9973        mid.x = start.x;\r
9974        mid.y = start.y + (finish.y - start.y) / 2;\r
9975      }\r
9976   }\r
9977   \r
9978   /* Don't use as many frames for very short moves */\r
9979   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9980     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9981   else\r
9982     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9983 \r
9984   animInfo.from.x = fromX;\r
9985   animInfo.from.y = fromY;\r
9986   animInfo.to.x = toX;\r
9987   animInfo.to.y = toY;\r
9988   animInfo.lastpos = start;\r
9989   animInfo.piece = piece;\r
9990   for (n = 0; n < nFrames; n++) {\r
9991     animInfo.pos = frames[n];\r
9992     DrawPosition(FALSE, NULL);\r
9993     animInfo.lastpos = animInfo.pos;\r
9994     Sleep(appData.animSpeed);\r
9995   }\r
9996   animInfo.pos = finish;\r
9997   DrawPosition(FALSE, NULL);\r
9998 \r
9999   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10000 \r
10001   animInfo.piece = EmptySquare;\r
10002   Explode(board, fromX, fromY, toX, toY);\r
10003 }\r
10004 \r
10005 /*      Convert board position to corner of screen rect and color       */\r
10006 \r
10007 static void\r
10008 ScreenSquare(column, row, pt)\r
10009      int column; int row; POINT * pt;\r
10010 {\r
10011   if (flipView) {\r
10012     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10013     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10014   } else {\r
10015     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10016     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10017   }\r
10018 }\r
10019 \r
10020 /*      Generate a series of frame coords from start->mid->finish.\r
10021         The movement rate doubles until the half way point is\r
10022         reached, then halves back down to the final destination,\r
10023         which gives a nice slow in/out effect. The algorithmn\r
10024         may seem to generate too many intermediates for short\r
10025         moves, but remember that the purpose is to attract the\r
10026         viewers attention to the piece about to be moved and\r
10027         then to where it ends up. Too few frames would be less\r
10028         noticeable.                                             */\r
10029 \r
10030 static void\r
10031 Tween(start, mid, finish, factor, frames, nFrames)\r
10032      POINT * start; POINT * mid;\r
10033      POINT * finish; int factor;\r
10034      POINT frames[]; int * nFrames;\r
10035 {\r
10036   int n, fraction = 1, count = 0;\r
10037 \r
10038   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10039   for (n = 0; n < factor; n++)\r
10040     fraction *= 2;\r
10041   for (n = 0; n < factor; n++) {\r
10042     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10043     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10044     count ++;\r
10045     fraction = fraction / 2;\r
10046   }\r
10047   \r
10048   /* Midpoint */\r
10049   frames[count] = *mid;\r
10050   count ++;\r
10051   \r
10052   /* Slow out, stepping 1/2, then 1/4, ... */\r
10053   fraction = 2;\r
10054   for (n = 0; n < factor; n++) {\r
10055     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10056     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10057     count ++;\r
10058     fraction = fraction * 2;\r
10059   }\r
10060   *nFrames = count;\r
10061 }\r
10062 \r
10063 void\r
10064 SettingsPopUp(ChessProgramState *cps)\r
10065 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10066       EngineOptionsPopup(savedHwnd, cps);\r
10067 }\r
10068 \r
10069 int flock(int fid, int code)\r
10070 {\r
10071     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10072     OVERLAPPED ov;\r
10073     ov.hEvent = NULL;\r
10074     ov.Offset = 0;\r
10075     ov.OffsetHigh = 0;\r
10076     switch(code) {\r
10077       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10078 \r
10079       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10080       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10081       default: return -1;\r
10082     }\r
10083     return 0;\r
10084 }\r
10085 \r
10086 char *\r
10087 Col2Text (int n)\r
10088 {\r
10089     static int i=0;\r
10090     static char col[8][20];\r
10091     COLORREF color = *(COLORREF *) colorVariable[n];\r
10092     i = i+1 & 7;\r
10093     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10094     return col[i];\r
10095 }\r
10096 \r
10097 void\r
10098 ActivateTheme (int new)\r
10099 {   // Redo initialization of features depending on options that can occur in themes\r
10100    InitTextures();\r
10101    if(new) InitDrawingColors();\r
10102    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10103    InitDrawingSizes(boardSize, 0);\r
10104    InvalidateRect(hwndMain, NULL, TRUE);\r
10105 }\r