3738ddc0180a754d0e5f6f4c082c692e62df7c3a
[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, IDOK, IDCANCEL }, \r
264 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
265   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
266 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
267 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
268   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
269 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
270 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
271   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
272 { ABOUTBOX2, IDC_ChessBoard }, \r
273 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
274   OPT_GameListClose, IDC_GameListDoFilter }, \r
275 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
276 { DLG_Error, IDOK }, \r
277 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
278   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
279 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
280 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
281   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
282   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
283 { DLG_IndexNumber, IDC_Index }, \r
284 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
285 { DLG_TypeInName, IDOK, IDCANCEL }, \r
286 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
287   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
288 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
289   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
290   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
291   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
292   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
293   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
294   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
295 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
296   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
297   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
298   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
299   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
300   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
301   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
302   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
303   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
304 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
305   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
306   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
307   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
308   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
309   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
310   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
311   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
312 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
313   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
314   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
315   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
316   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
317   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
318   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
319   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
320   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
321 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
322   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
323   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
324   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
325   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
326 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
327 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
328   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
329 { DLG_MoveHistory }, \r
330 { DLG_EvalGraph }, \r
331 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
332 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
333 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
334   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
335   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
336   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
337 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
338   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
339   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
340 { 0 }\r
341 };\r
342 \r
343 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
344 static int lastChecked;\r
345 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
346 extern int tinyLayout;\r
347 extern char * menuBarText[][10];\r
348 \r
349 void\r
350 LoadLanguageFile(char *name)\r
351 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
352     FILE *f;\r
353     int i=0, j=0, n=0, k;\r
354     char buf[MSG_SIZ];\r
355 \r
356     if(!name || name[0] == NULLCHAR) return;\r
357       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
358     appData.language = oldLanguage;\r
359     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
360     if((f = fopen(buf, "r")) == NULL) return;\r
361     while((k = fgetc(f)) != EOF) {\r
362         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
363         languageBuf[i] = k;\r
364         if(k == '\n') {\r
365             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
366                 char *p;\r
367                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
368                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
369                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
370                         english[j] = languageBuf + n + 1; *p = 0;\r
371                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
372 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
373                     }\r
374                 }\r
375             }\r
376             n = i + 1;\r
377         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
378             switch(k) {\r
379               case 'n': k = '\n'; break;\r
380               case 'r': k = '\r'; break;\r
381               case 't': k = '\t'; break;\r
382             }\r
383             languageBuf[--i] = k;\r
384         }\r
385         i++;\r
386     }\r
387     fclose(f);\r
388     barbaric = (j != 0);\r
389     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
390 }\r
391 \r
392 char *\r
393 T_(char *s)\r
394 {   // return the translation of the given string\r
395     // efficiency can be improved a lot...\r
396     int i=0;\r
397     static char buf[MSG_SIZ];\r
398 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
399     if(!barbaric) return s;\r
400     if(!s) return ""; // sanity\r
401     while(english[i]) {\r
402         if(!strcmp(s, english[i])) return foreign[i];\r
403         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
404             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
405             return buf;\r
406         }\r
407         i++;\r
408     }\r
409     return s;\r
410 }\r
411 \r
412 void\r
413 Translate(HWND hDlg, int dialogID)\r
414 {   // translate all text items in the given dialog\r
415     int i=0, j, k;\r
416     char buf[MSG_SIZ], *s;\r
417     if(!barbaric) return;\r
418     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
419     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
420     GetWindowText( hDlg, buf, MSG_SIZ );\r
421     s = T_(buf);\r
422     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
423     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
424         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
425         if(strlen(buf) == 0) continue;\r
426         s = T_(buf);\r
427         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
428     }\r
429 }\r
430 \r
431 HMENU\r
432 TranslateOneMenu(int i, HMENU subMenu)\r
433 {\r
434     int j;\r
435     static MENUITEMINFO info;\r
436 \r
437     info.cbSize = sizeof(MENUITEMINFO);\r
438     info.fMask = MIIM_STATE | MIIM_TYPE;\r
439           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
440             char buf[MSG_SIZ];\r
441             info.dwTypeData = buf;\r
442             info.cch = sizeof(buf);\r
443             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
444             if(i < 10) {\r
445                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
446                 else menuText[i][j] = strdup(buf); // remember original on first change\r
447             }\r
448             if(buf[0] == NULLCHAR) continue;\r
449             info.dwTypeData = T_(buf);\r
450             info.cch = strlen(buf)+1;\r
451             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
452           }\r
453     return subMenu;\r
454 }\r
455 \r
456 void\r
457 TranslateMenus(int addLanguage)\r
458 {\r
459     int i;\r
460     WIN32_FIND_DATA fileData;\r
461     HANDLE hFind;\r
462 #define IDM_English 1970\r
463     if(1) {\r
464         HMENU mainMenu = GetMenu(hwndMain);\r
465         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
466           HMENU subMenu = GetSubMenu(mainMenu, i);\r
467           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
468                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
469           TranslateOneMenu(i, subMenu);\r
470         }\r
471         DrawMenuBar(hwndMain);\r
472     }\r
473 \r
474     if(!addLanguage) return;\r
475     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
476         HMENU mainMenu = GetMenu(hwndMain);\r
477         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
478         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
479         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
480         i = 0; lastChecked = IDM_English;\r
481         do {\r
482             char *p, *q = fileData.cFileName;\r
483             int checkFlag = MF_UNCHECKED;\r
484             languageFile[i] = strdup(q);\r
485             if(barbaric && !strcmp(oldLanguage, q)) {\r
486                 checkFlag = MF_CHECKED;\r
487                 lastChecked = IDM_English + i + 1;\r
488                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
489             }\r
490             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
491             p = strstr(fileData.cFileName, ".lng");\r
492             if(p) *p = 0;\r
493             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
494         } while(FindNextFile(hFind, &fileData));\r
495         FindClose(hFind);\r
496     }\r
497 }\r
498 \r
499 #endif\r
500 \r
501 #define IDM_RecentEngines 3000\r
502 \r
503 void\r
504 RecentEngineMenu (char *s)\r
505 {\r
506     if(appData.icsActive) return;\r
507     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
508         HMENU mainMenu = GetMenu(hwndMain);\r
509         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
510         int i=IDM_RecentEngines;\r
511         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
512         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
513         while(*s) {\r
514           char *p = strchr(s, '\n');\r
515           if(p == NULL) return; // malformed!\r
516           *p = NULLCHAR;\r
517           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
518           *p = '\n';\r
519           s = p+1;\r
520         }\r
521     }\r
522 }\r
523 \r
524 \r
525 typedef struct {\r
526   char *name;\r
527   int squareSize;\r
528   int lineGap;\r
529   int smallLayout;\r
530   int tinyLayout;\r
531   int cliWidth, cliHeight;\r
532 } SizeInfo;\r
533 \r
534 SizeInfo sizeInfo[] = \r
535 {\r
536   { "tiny",     21, 0, 1, 1, 0, 0 },\r
537   { "teeny",    25, 1, 1, 1, 0, 0 },\r
538   { "dinky",    29, 1, 1, 1, 0, 0 },\r
539   { "petite",   33, 1, 1, 1, 0, 0 },\r
540   { "slim",     37, 2, 1, 0, 0, 0 },\r
541   { "small",    40, 2, 1, 0, 0, 0 },\r
542   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
543   { "middling", 49, 2, 0, 0, 0, 0 },\r
544   { "average",  54, 2, 0, 0, 0, 0 },\r
545   { "moderate", 58, 3, 0, 0, 0, 0 },\r
546   { "medium",   64, 3, 0, 0, 0, 0 },\r
547   { "bulky",    72, 3, 0, 0, 0, 0 },\r
548   { "large",    80, 3, 0, 0, 0, 0 },\r
549   { "big",      87, 3, 0, 0, 0, 0 },\r
550   { "huge",     95, 3, 0, 0, 0, 0 },\r
551   { "giant",    108, 3, 0, 0, 0, 0 },\r
552   { "colossal", 116, 4, 0, 0, 0, 0 },\r
553   { "titanic",  129, 4, 0, 0, 0, 0 },\r
554   { NULL, 0, 0, 0, 0, 0, 0 }\r
555 };\r
556 \r
557 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
558 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
559 {\r
560   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
561   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
574   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
575   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
576   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
577   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
578 };\r
579 \r
580 MyFont *font[NUM_SIZES][NUM_FONTS];\r
581 \r
582 typedef struct {\r
583   char *label;\r
584   int id;\r
585   HWND hwnd;\r
586   WNDPROC wndproc;\r
587 } MyButtonDesc;\r
588 \r
589 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
590 #define N_BUTTONS 5\r
591 \r
592 MyButtonDesc buttonDesc[N_BUTTONS] =\r
593 {\r
594   {"<<", IDM_ToStart, NULL, NULL},\r
595   {"<", IDM_Backward, NULL, NULL},\r
596   {"P", IDM_Pause, NULL, NULL},\r
597   {">", IDM_Forward, NULL, NULL},\r
598   {">>", IDM_ToEnd, NULL, NULL},\r
599 };\r
600 \r
601 int tinyLayout = 0, smallLayout = 0;\r
602 #define MENU_BAR_ITEMS 9\r
603 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
604   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
605   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
606 };\r
607 \r
608 \r
609 MySound sounds[(int)NSoundClasses];\r
610 MyTextAttribs textAttribs[(int)NColorClasses];\r
611 \r
612 MyColorizeAttribs colorizeAttribs[] = {\r
613   { (COLORREF)0, 0, N_("Shout Text") },\r
614   { (COLORREF)0, 0, N_("SShout/CShout") },\r
615   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
616   { (COLORREF)0, 0, N_("Channel Text") },\r
617   { (COLORREF)0, 0, N_("Kibitz Text") },\r
618   { (COLORREF)0, 0, N_("Tell Text") },\r
619   { (COLORREF)0, 0, N_("Challenge Text") },\r
620   { (COLORREF)0, 0, N_("Request Text") },\r
621   { (COLORREF)0, 0, N_("Seek Text") },\r
622   { (COLORREF)0, 0, N_("Normal Text") },\r
623   { (COLORREF)0, 0, N_("None") }\r
624 };\r
625 \r
626 \r
627 \r
628 static char *commentTitle;\r
629 static char *commentText;\r
630 static int commentIndex;\r
631 static Boolean editComment = FALSE;\r
632 \r
633 \r
634 char errorTitle[MSG_SIZ];\r
635 char errorMessage[2*MSG_SIZ];\r
636 HWND errorDialog = NULL;\r
637 BOOLEAN moveErrorMessageUp = FALSE;\r
638 BOOLEAN consoleEcho = TRUE;\r
639 CHARFORMAT consoleCF;\r
640 COLORREF consoleBackgroundColor;\r
641 \r
642 char *programVersion;\r
643 \r
644 #define CPReal 1\r
645 #define CPComm 2\r
646 #define CPSock 3\r
647 #define CPRcmd 4\r
648 typedef int CPKind;\r
649 \r
650 typedef struct {\r
651   CPKind kind;\r
652   HANDLE hProcess;\r
653   DWORD pid;\r
654   HANDLE hTo;\r
655   HANDLE hFrom;\r
656   SOCKET sock;\r
657   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
658 } ChildProc;\r
659 \r
660 #define INPUT_SOURCE_BUF_SIZE 4096\r
661 \r
662 typedef struct _InputSource {\r
663   CPKind kind;\r
664   HANDLE hFile;\r
665   SOCKET sock;\r
666   int lineByLine;\r
667   HANDLE hThread;\r
668   DWORD id;\r
669   char buf[INPUT_SOURCE_BUF_SIZE];\r
670   char *next;\r
671   DWORD count;\r
672   int error;\r
673   InputCallback func;\r
674   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
675   VOIDSTAR closure;\r
676 } InputSource;\r
677 \r
678 InputSource *consoleInputSource;\r
679 \r
680 DCB dcb;\r
681 \r
682 /* forward */\r
683 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
684 VOID ConsoleCreate();\r
685 LRESULT CALLBACK\r
686   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
687 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
688 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
689 VOID ParseCommSettings(char *arg, DCB *dcb);\r
690 LRESULT CALLBACK\r
691   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
692 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
693 void ParseIcsTextMenu(char *icsTextMenuString);\r
694 VOID PopUpNameDialog(char firstchar);\r
695 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
696 \r
697 /* [AS] */\r
698 int NewGameFRC();\r
699 int GameListOptions();\r
700 \r
701 int dummy; // [HGM] for obsolete args\r
702 \r
703 HWND hwndMain = NULL;        /* root window*/\r
704 HWND hwndConsole = NULL;\r
705 HWND commentDialog = NULL;\r
706 HWND moveHistoryDialog = NULL;\r
707 HWND evalGraphDialog = NULL;\r
708 HWND engineOutputDialog = NULL;\r
709 HWND gameListDialog = NULL;\r
710 HWND editTagsDialog = NULL;\r
711 \r
712 int commentUp = FALSE;\r
713 \r
714 WindowPlacement wpMain;\r
715 WindowPlacement wpConsole;\r
716 WindowPlacement wpComment;\r
717 WindowPlacement wpMoveHistory;\r
718 WindowPlacement wpEvalGraph;\r
719 WindowPlacement wpEngineOutput;\r
720 WindowPlacement wpGameList;\r
721 WindowPlacement wpTags;\r
722 \r
723 VOID EngineOptionsPopup(); // [HGM] settings\r
724 \r
725 VOID GothicPopUp(char *title, VariantClass variant);\r
726 /*\r
727  * Setting "frozen" should disable all user input other than deleting\r
728  * the window.  We do this while engines are initializing themselves.\r
729  */\r
730 static int frozen = 0;\r
731 static int oldMenuItemState[MENU_BAR_ITEMS];\r
732 void FreezeUI()\r
733 {\r
734   HMENU hmenu;\r
735   int i;\r
736 \r
737   if (frozen) return;\r
738   frozen = 1;\r
739   hmenu = GetMenu(hwndMain);\r
740   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
741     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
742   }\r
743   DrawMenuBar(hwndMain);\r
744 }\r
745 \r
746 /* Undo a FreezeUI */\r
747 void ThawUI()\r
748 {\r
749   HMENU hmenu;\r
750   int i;\r
751 \r
752   if (!frozen) return;\r
753   frozen = 0;\r
754   hmenu = GetMenu(hwndMain);\r
755   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
756     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
757   }\r
758   DrawMenuBar(hwndMain);\r
759 }\r
760 \r
761 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
762 \r
763 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
764 #ifdef JAWS\r
765 #include "jaws.c"\r
766 #else\r
767 #define JAWS_INIT\r
768 #define JAWS_ARGS\r
769 #define JAWS_ALT_INTERCEPT\r
770 #define JAWS_KBUP_NAVIGATION\r
771 #define JAWS_KBDOWN_NAVIGATION\r
772 #define JAWS_MENU_ITEMS\r
773 #define JAWS_SILENCE\r
774 #define JAWS_REPLAY\r
775 #define JAWS_ACCEL\r
776 #define JAWS_COPYRIGHT\r
777 #define JAWS_DELETE(X) X\r
778 #define SAYMACHINEMOVE()\r
779 #define SAY(X)\r
780 #endif\r
781 \r
782 /*---------------------------------------------------------------------------*\\r
783  *\r
784  * WinMain\r
785  *\r
786 \*---------------------------------------------------------------------------*/\r
787 \r
788 int APIENTRY\r
789 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
790         LPSTR lpCmdLine, int nCmdShow)\r
791 {\r
792   MSG msg;\r
793   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
794 //  INITCOMMONCONTROLSEX ex;\r
795 \r
796   debugFP = stderr;\r
797 \r
798   LoadLibrary("RICHED32.DLL");\r
799   consoleCF.cbSize = sizeof(CHARFORMAT);\r
800 \r
801   if (!InitApplication(hInstance)) {\r
802     return (FALSE);\r
803   }\r
804   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
805     return (FALSE);\r
806   }\r
807 \r
808   JAWS_INIT\r
809   TranslateMenus(1);\r
810 \r
811 //  InitCommonControlsEx(&ex);\r
812   InitCommonControls();\r
813 \r
814   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
815   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
816   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
817 \r
818   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
819 \r
820   while (GetMessage(&msg, /* message structure */\r
821                     NULL, /* handle of window receiving the message */\r
822                     0,    /* lowest message to examine */\r
823                     0))   /* highest message to examine */\r
824     {\r
825 \r
826       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
827         // [HGM] navigate: switch between all windows with tab\r
828         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
829         int i, currentElement = 0;\r
830 \r
831         // first determine what element of the chain we come from (if any)\r
832         if(appData.icsActive) {\r
833             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
834             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
835         }\r
836         if(engineOutputDialog && EngineOutputIsUp()) {\r
837             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
838             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
839         }\r
840         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
841             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
842         }\r
843         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
844         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
845         if(msg.hwnd == e1)                 currentElement = 2; else\r
846         if(msg.hwnd == e2)                 currentElement = 3; else\r
847         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
848         if(msg.hwnd == mh)                currentElement = 4; else\r
849         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
850         if(msg.hwnd == hText)  currentElement = 5; else\r
851         if(msg.hwnd == hInput) currentElement = 6; else\r
852         for (i = 0; i < N_BUTTONS; i++) {\r
853             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
854         }\r
855 \r
856         // determine where to go to\r
857         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
858           do {\r
859             currentElement = (currentElement + direction) % 7;\r
860             switch(currentElement) {\r
861                 case 0:\r
862                   h = hwndMain; break; // passing this case always makes the loop exit\r
863                 case 1:\r
864                   h = buttonDesc[0].hwnd; break; // could be NULL\r
865                 case 2:\r
866                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
867                   h = e1; break;\r
868                 case 3:\r
869                   if(!EngineOutputIsUp()) continue;\r
870                   h = e2; break;\r
871                 case 4:\r
872                   if(!MoveHistoryIsUp()) continue;\r
873                   h = mh; break;\r
874 //              case 6: // input to eval graph does not seem to get here!\r
875 //                if(!EvalGraphIsUp()) continue;\r
876 //                h = evalGraphDialog; break;\r
877                 case 5:\r
878                   if(!appData.icsActive) continue;\r
879                   SAY("display");\r
880                   h = hText; break;\r
881                 case 6:\r
882                   if(!appData.icsActive) continue;\r
883                   SAY("input");\r
884                   h = hInput; break;\r
885             }\r
886           } while(h == 0);\r
887 \r
888           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
889           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
890           SetFocus(h);\r
891 \r
892           continue; // this message now has been processed\r
893         }\r
894       }\r
895 \r
896       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
897           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
898           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
899           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
900           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
901           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
902           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
903           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
904           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
905           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
906         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
907         for(i=0; i<MAX_CHAT; i++) \r
908             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
909                 done = 1; break;\r
910         }\r
911         if(done) continue; // [HGM] chat: end patch\r
912         TranslateMessage(&msg); /* Translates virtual key codes */\r
913         DispatchMessage(&msg);  /* Dispatches message to window */\r
914       }\r
915     }\r
916 \r
917 \r
918   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
919 }\r
920 \r
921 /*---------------------------------------------------------------------------*\\r
922  *\r
923  * Initialization functions\r
924  *\r
925 \*---------------------------------------------------------------------------*/\r
926 \r
927 void\r
928 SetUserLogo()\r
929 {   // update user logo if necessary\r
930     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
931 \r
932     if(appData.autoLogo) {\r
933           curName = UserName();\r
934           if(strcmp(curName, oldUserName)) {\r
935                 GetCurrentDirectory(MSG_SIZ, dir);\r
936                 SetCurrentDirectory(installDir);\r
937                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
938                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
939                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
940                 if(userLogo == NULL)\r
941                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
942                 SetCurrentDirectory(dir); /* return to prev directory */\r
943           }\r
944     }\r
945 }\r
946 \r
947 BOOL\r
948 InitApplication(HINSTANCE hInstance)\r
949 {\r
950   WNDCLASS wc;\r
951 \r
952   /* Fill in window class structure with parameters that describe the */\r
953   /* main window. */\r
954 \r
955   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
956   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
957   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
958   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
959   wc.hInstance     = hInstance;         /* Owner of this class */\r
960   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
961   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
962   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
963   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
964   wc.lpszClassName = szAppName;                 /* Name to register as */\r
965 \r
966   /* Register the window class and return success/failure code. */\r
967   if (!RegisterClass(&wc)) return FALSE;\r
968 \r
969   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
970   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
971   wc.cbClsExtra    = 0;\r
972   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
973   wc.hInstance     = hInstance;\r
974   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
975   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
976   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
977   wc.lpszMenuName  = NULL;\r
978   wc.lpszClassName = szConsoleName;\r
979 \r
980   if (!RegisterClass(&wc)) return FALSE;\r
981   return TRUE;\r
982 }\r
983 \r
984 \r
985 /* Set by InitInstance, used by EnsureOnScreen */\r
986 int screenHeight, screenWidth;\r
987 RECT screenGeometry;\r
988 \r
989 void\r
990 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
991 {\r
992 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
993   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
994   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
995   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
996   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
997   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
998 }\r
999 \r
1000 VOID\r
1001 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1002 {\r
1003   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1004   GetCurrentDirectory(MSG_SIZ, dir);\r
1005   SetCurrentDirectory(installDir);\r
1006   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1007       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1008 \r
1009       if (cps->programLogo == NULL && appData.debugMode) {\r
1010           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1011       }\r
1012   } else if(appData.autoLogo) {\r
1013       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1014         char *opponent = "";\r
1015         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1016         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1017         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1018         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1019             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1020             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1021         }\r
1022       } else\r
1023       if(appData.directory[n] && appData.directory[n][0]) {\r
1024         SetCurrentDirectory(appData.directory[n]);\r
1025         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1026       }\r
1027   }\r
1028   SetCurrentDirectory(dir); /* return to prev directory */\r
1029 }\r
1030 \r
1031 VOID\r
1032 InitTextures()\r
1033 {\r
1034   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1035   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1036   \r
1037   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1038       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1039       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1040       liteBackTextureMode = appData.liteBackTextureMode;\r
1041 \r
1042       if (liteBackTexture == NULL && appData.debugMode) {\r
1043           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1044       }\r
1045   }\r
1046   \r
1047   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1048       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1049       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1050       darkBackTextureMode = appData.darkBackTextureMode;\r
1051 \r
1052       if (darkBackTexture == NULL && appData.debugMode) {\r
1053           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1054       }\r
1055   }\r
1056 }\r
1057 \r
1058 #ifndef SM_CXVIRTUALSCREEN\r
1059 #define SM_CXVIRTUALSCREEN 78\r
1060 #endif\r
1061 #ifndef SM_CYVIRTUALSCREEN\r
1062 #define SM_CYVIRTUALSCREEN 79\r
1063 #endif\r
1064 #ifndef SM_XVIRTUALSCREEN \r
1065 #define SM_XVIRTUALSCREEN 76\r
1066 #endif\r
1067 #ifndef SM_YVIRTUALSCREEN \r
1068 #define SM_YVIRTUALSCREEN 77\r
1069 #endif\r
1070 \r
1071 VOID\r
1072 InitGeometry()\r
1073 {\r
1074   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1075   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1076   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1077   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1078   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1079   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1080   screenGeometry.right = screenGeometry.left + screenWidth;\r
1081   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1082 }\r
1083 \r
1084 BOOL\r
1085 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1086 {\r
1087   HWND hwnd; /* Main window handle. */\r
1088   int ibs;\r
1089   WINDOWPLACEMENT wp;\r
1090   char *filepart;\r
1091 \r
1092   hInst = hInstance;    /* Store instance handle in our global variable */\r
1093   programName = szAppName;\r
1094 \r
1095   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1096     *filepart = NULLCHAR;\r
1097     SetCurrentDirectory(installDir);\r
1098   } else {\r
1099     GetCurrentDirectory(MSG_SIZ, installDir);\r
1100   }\r
1101   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1102   InitGeometry();\r
1103   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1104   /* xboard, and older WinBoards, controlled the move sound with the\r
1105      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1106      always turn the option on (so that the backend will call us),\r
1107      then let the user turn the sound off by setting it to silence if\r
1108      desired.  To accommodate old winboard.ini files saved by old\r
1109      versions of WinBoard, we also turn off the sound if the option\r
1110      was initially set to false. [HGM] taken out of InitAppData */\r
1111   if (!appData.ringBellAfterMoves) {\r
1112     sounds[(int)SoundMove].name = strdup("");\r
1113     appData.ringBellAfterMoves = TRUE;\r
1114   }\r
1115   if (appData.debugMode) {\r
1116     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1117     setbuf(debugFP, NULL);\r
1118   }\r
1119 \r
1120   LoadLanguageFile(appData.language);\r
1121 \r
1122   InitBackEnd1();\r
1123 \r
1124 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1125 //  InitEngineUCI( installDir, &second );\r
1126 \r
1127   /* Create a main window for this application instance. */\r
1128   hwnd = CreateWindow(szAppName, szTitle,\r
1129                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1130                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1131                       NULL, NULL, hInstance, NULL);\r
1132   hwndMain = hwnd;\r
1133 \r
1134   /* If window could not be created, return "failure" */\r
1135   if (!hwnd) {\r
1136     return (FALSE);\r
1137   }\r
1138 \r
1139   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1140   LoadLogo(&first, 0, FALSE);\r
1141   LoadLogo(&second, 1, appData.icsActive);\r
1142 \r
1143   SetUserLogo();\r
1144 \r
1145   iconWhite = LoadIcon(hInstance, "icon_white");\r
1146   iconBlack = LoadIcon(hInstance, "icon_black");\r
1147   iconCurrent = iconWhite;\r
1148   InitDrawingColors();\r
1149 \r
1150   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1151   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1152     /* Compute window size for each board size, and use the largest\r
1153        size that fits on this screen as the default. */\r
1154     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1155     if (boardSize == (BoardSize)-1 &&\r
1156         winH <= screenHeight\r
1157            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1158         && winW <= screenWidth) {\r
1159       boardSize = (BoardSize)ibs;\r
1160     }\r
1161   }\r
1162 \r
1163   InitDrawingSizes(boardSize, 0);\r
1164   RecentEngineMenu(appData.recentEngineList);\r
1165   InitMenuChecks();\r
1166   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1167 \r
1168   /* [AS] Load textures if specified */\r
1169   InitTextures();\r
1170 \r
1171   mysrandom( (unsigned) time(NULL) );\r
1172 \r
1173   /* [AS] Restore layout */\r
1174   if( wpMoveHistory.visible ) {\r
1175       MoveHistoryPopUp();\r
1176   }\r
1177 \r
1178   if( wpEvalGraph.visible ) {\r
1179       EvalGraphPopUp();\r
1180   }\r
1181 \r
1182   if( wpEngineOutput.visible ) {\r
1183       EngineOutputPopUp();\r
1184   }\r
1185 \r
1186   /* Make the window visible; update its client area; and return "success" */\r
1187   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1188   wp.length = sizeof(WINDOWPLACEMENT);\r
1189   wp.flags = 0;\r
1190   wp.showCmd = nCmdShow;\r
1191   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1192   wp.rcNormalPosition.left = wpMain.x;\r
1193   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1194   wp.rcNormalPosition.top = wpMain.y;\r
1195   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1196   SetWindowPlacement(hwndMain, &wp);\r
1197 \r
1198   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1199 \r
1200   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1201                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1202 \r
1203   if (hwndConsole) {\r
1204 #if AOT_CONSOLE\r
1205     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1206                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1207 #endif\r
1208     ShowWindow(hwndConsole, nCmdShow);\r
1209     SetActiveWindow(hwndConsole);\r
1210   }\r
1211   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1212   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1213 \r
1214   return TRUE;\r
1215 \r
1216 }\r
1217 \r
1218 VOID\r
1219 InitMenuChecks()\r
1220 {\r
1221   HMENU hmenu = GetMenu(hwndMain);\r
1222 \r
1223   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1224                         MF_BYCOMMAND|((appData.icsActive &&\r
1225                                        *appData.icsCommPort != NULLCHAR) ?\r
1226                                       MF_ENABLED : MF_GRAYED));\r
1227   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1228                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1229                                      MF_CHECKED : MF_UNCHECKED));\r
1230 }\r
1231 \r
1232 //---------------------------------------------------------------------------------------------------------\r
1233 \r
1234 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1235 #define XBOARD FALSE\r
1236 \r
1237 #define OPTCHAR "/"\r
1238 #define SEPCHAR "="\r
1239 #define TOPLEVEL 0\r
1240 \r
1241 #include "args.h"\r
1242 \r
1243 // front-end part of option handling\r
1244 \r
1245 VOID\r
1246 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1247 {\r
1248   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1249   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1250   DeleteDC(hdc);\r
1251   lf->lfWidth = 0;\r
1252   lf->lfEscapement = 0;\r
1253   lf->lfOrientation = 0;\r
1254   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1255   lf->lfItalic = mfp->italic;\r
1256   lf->lfUnderline = mfp->underline;\r
1257   lf->lfStrikeOut = mfp->strikeout;\r
1258   lf->lfCharSet = mfp->charset;\r
1259   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1260   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1261   lf->lfQuality = DEFAULT_QUALITY;\r
1262   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1263     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1264 }\r
1265 \r
1266 void\r
1267 CreateFontInMF(MyFont *mf)\r
1268\r
1269   LFfromMFP(&mf->lf, &mf->mfp);\r
1270   if (mf->hf) DeleteObject(mf->hf);\r
1271   mf->hf = CreateFontIndirect(&mf->lf);\r
1272 }\r
1273 \r
1274 // [HGM] This platform-dependent table provides the location for storing the color info\r
1275 void *\r
1276 colorVariable[] = {\r
1277   &whitePieceColor, \r
1278   &blackPieceColor, \r
1279   &lightSquareColor,\r
1280   &darkSquareColor, \r
1281   &highlightSquareColor,\r
1282   &premoveHighlightColor,\r
1283   NULL,\r
1284   &consoleBackgroundColor,\r
1285   &appData.fontForeColorWhite,\r
1286   &appData.fontBackColorWhite,\r
1287   &appData.fontForeColorBlack,\r
1288   &appData.fontBackColorBlack,\r
1289   &appData.evalHistColorWhite,\r
1290   &appData.evalHistColorBlack,\r
1291   &appData.highlightArrowColor,\r
1292 };\r
1293 \r
1294 /* Command line font name parser.  NULL name means do nothing.\r
1295    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1296    For backward compatibility, syntax without the colon is also\r
1297    accepted, but font names with digits in them won't work in that case.\r
1298 */\r
1299 VOID\r
1300 ParseFontName(char *name, MyFontParams *mfp)\r
1301 {\r
1302   char *p, *q;\r
1303   if (name == NULL) return;\r
1304   p = name;\r
1305   q = strchr(p, ':');\r
1306   if (q) {\r
1307     if (q - p >= sizeof(mfp->faceName))\r
1308       ExitArgError(_("Font name too long:"), name, TRUE);\r
1309     memcpy(mfp->faceName, p, q - p);\r
1310     mfp->faceName[q - p] = NULLCHAR;\r
1311     p = q + 1;\r
1312   } else {\r
1313     q = mfp->faceName;\r
1314 \r
1315     while (*p && !isdigit(*p)) {\r
1316       *q++ = *p++;\r
1317       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1318         ExitArgError(_("Font name too long:"), name, TRUE);\r
1319     }\r
1320     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1321     *q = NULLCHAR;\r
1322   }\r
1323   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1324   mfp->pointSize = (float) atof(p);\r
1325   mfp->bold = (strchr(p, 'b') != NULL);\r
1326   mfp->italic = (strchr(p, 'i') != NULL);\r
1327   mfp->underline = (strchr(p, 'u') != NULL);\r
1328   mfp->strikeout = (strchr(p, 's') != NULL);\r
1329   mfp->charset = DEFAULT_CHARSET;\r
1330   q = strchr(p, 'c');\r
1331   if (q)\r
1332     mfp->charset = (BYTE) atoi(q+1);\r
1333 }\r
1334 \r
1335 void\r
1336 ParseFont(char *name, int number)\r
1337 { // wrapper to shield back-end from 'font'\r
1338   ParseFontName(name, &font[boardSize][number]->mfp);\r
1339 }\r
1340 \r
1341 void\r
1342 SetFontDefaults()\r
1343 { // in WB  we have a 2D array of fonts; this initializes their description\r
1344   int i, j;\r
1345   /* Point font array elements to structures and\r
1346      parse default font names */\r
1347   for (i=0; i<NUM_FONTS; i++) {\r
1348     for (j=0; j<NUM_SIZES; j++) {\r
1349       font[j][i] = &fontRec[j][i];\r
1350       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1351     }\r
1352   }\r
1353 }\r
1354 \r
1355 void\r
1356 CreateFonts()\r
1357 { // here we create the actual fonts from the selected descriptions\r
1358   int i, j;\r
1359   for (i=0; i<NUM_FONTS; i++) {\r
1360     for (j=0; j<NUM_SIZES; j++) {\r
1361       CreateFontInMF(font[j][i]);\r
1362     }\r
1363   }\r
1364 }\r
1365 /* Color name parser.\r
1366    X version accepts X color names, but this one\r
1367    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1368 COLORREF\r
1369 ParseColorName(char *name)\r
1370 {\r
1371   int red, green, blue, count;\r
1372   char buf[MSG_SIZ];\r
1373 \r
1374   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1375   if (count != 3) {\r
1376     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1377       &red, &green, &blue);\r
1378   }\r
1379   if (count != 3) {\r
1380     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1381     DisplayError(buf, 0);\r
1382     return RGB(0, 0, 0);\r
1383   }\r
1384   return PALETTERGB(red, green, blue);\r
1385 }\r
1386 \r
1387 void\r
1388 ParseColor(int n, char *name)\r
1389 { // for WinBoard the color is an int, which needs to be derived from the string\r
1390   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1391 }\r
1392 \r
1393 void\r
1394 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1395 {\r
1396   char *e = argValue;\r
1397   int eff = 0;\r
1398 \r
1399   while (*e) {\r
1400     if (*e == 'b')      eff |= CFE_BOLD;\r
1401     else if (*e == 'i') eff |= CFE_ITALIC;\r
1402     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1403     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1404     else if (*e == '#' || isdigit(*e)) break;\r
1405     e++;\r
1406   }\r
1407   *effects = eff;\r
1408   *color   = ParseColorName(e);\r
1409 }\r
1410 \r
1411 void\r
1412 ParseTextAttribs(ColorClass cc, char *s)\r
1413 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1414     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1415     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1416 }\r
1417 \r
1418 void\r
1419 ParseBoardSize(void *addr, char *name)\r
1420 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1421   BoardSize bs = SizeTiny;\r
1422   while (sizeInfo[bs].name != NULL) {\r
1423     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1424         *(BoardSize *)addr = bs;\r
1425         return;\r
1426     }\r
1427     bs++;\r
1428   }\r
1429   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1430 }\r
1431 \r
1432 void\r
1433 LoadAllSounds()\r
1434 { // [HGM] import name from appData first\r
1435   ColorClass cc;\r
1436   SoundClass sc;\r
1437   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1438     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1439     textAttribs[cc].sound.data = NULL;\r
1440     MyLoadSound(&textAttribs[cc].sound);\r
1441   }\r
1442   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1443     textAttribs[cc].sound.name = strdup("");\r
1444     textAttribs[cc].sound.data = NULL;\r
1445   }\r
1446   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1447     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1448     sounds[sc].data = NULL;\r
1449     MyLoadSound(&sounds[sc]);\r
1450   }\r
1451 }\r
1452 \r
1453 void\r
1454 SetCommPortDefaults()\r
1455 {\r
1456    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1457   dcb.DCBlength = sizeof(DCB);\r
1458   dcb.BaudRate = 9600;\r
1459   dcb.fBinary = TRUE;\r
1460   dcb.fParity = FALSE;\r
1461   dcb.fOutxCtsFlow = FALSE;\r
1462   dcb.fOutxDsrFlow = FALSE;\r
1463   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1464   dcb.fDsrSensitivity = FALSE;\r
1465   dcb.fTXContinueOnXoff = TRUE;\r
1466   dcb.fOutX = FALSE;\r
1467   dcb.fInX = FALSE;\r
1468   dcb.fNull = FALSE;\r
1469   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1470   dcb.fAbortOnError = FALSE;\r
1471   dcb.ByteSize = 7;\r
1472   dcb.Parity = SPACEPARITY;\r
1473   dcb.StopBits = ONESTOPBIT;\r
1474 }\r
1475 \r
1476 // [HGM] args: these three cases taken out to stay in front-end\r
1477 void\r
1478 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1479 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1480         // while the curent board size determines the element. This system should be ported to XBoard.\r
1481         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1482         int bs;\r
1483         for (bs=0; bs<NUM_SIZES; bs++) {\r
1484           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1485           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1486           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1487             ad->argName, mfp->faceName, mfp->pointSize,\r
1488             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1489             mfp->bold ? "b" : "",\r
1490             mfp->italic ? "i" : "",\r
1491             mfp->underline ? "u" : "",\r
1492             mfp->strikeout ? "s" : "",\r
1493             (int)mfp->charset);\r
1494         }\r
1495       }\r
1496 \r
1497 void\r
1498 ExportSounds()\r
1499 { // [HGM] copy the names from the internal WB variables to appData\r
1500   ColorClass cc;\r
1501   SoundClass sc;\r
1502   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1503     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1504   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1505     (&appData.soundMove)[sc] = sounds[sc].name;\r
1506 }\r
1507 \r
1508 void\r
1509 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1510 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1511         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1512         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1513           (ta->effects & CFE_BOLD) ? "b" : "",\r
1514           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1515           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1516           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1517           (ta->effects) ? " " : "",\r
1518           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1519       }\r
1520 \r
1521 void\r
1522 SaveColor(FILE *f, ArgDescriptor *ad)\r
1523 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1524         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1525         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1526           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1527 }\r
1528 \r
1529 void\r
1530 SaveBoardSize(FILE *f, char *name, void *addr)\r
1531 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1532   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1533 }\r
1534 \r
1535 void\r
1536 ParseCommPortSettings(char *s)\r
1537 { // wrapper to keep dcb from back-end\r
1538   ParseCommSettings(s, &dcb);\r
1539 }\r
1540 \r
1541 void\r
1542 GetWindowCoords()\r
1543 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1544   GetActualPlacement(hwndMain, &wpMain);\r
1545   GetActualPlacement(hwndConsole, &wpConsole);\r
1546   GetActualPlacement(commentDialog, &wpComment);\r
1547   GetActualPlacement(editTagsDialog, &wpTags);\r
1548   GetActualPlacement(gameListDialog, &wpGameList);\r
1549   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1550   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1551   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1552 }\r
1553 \r
1554 void\r
1555 PrintCommPortSettings(FILE *f, char *name)\r
1556 { // wrapper to shield back-end from DCB\r
1557       PrintCommSettings(f, name, &dcb);\r
1558 }\r
1559 \r
1560 int\r
1561 MySearchPath(char *installDir, char *name, char *fullname)\r
1562 {\r
1563   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1564   if(name[0]== '%') {\r
1565     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1566     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1567       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1568       *strchr(buf, '%') = 0;\r
1569       strcat(fullname, getenv(buf));\r
1570       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1571     }\r
1572     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1573     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1574     return (int) strlen(fullname);\r
1575   }\r
1576   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1577 }\r
1578 \r
1579 int\r
1580 MyGetFullPathName(char *name, char *fullname)\r
1581 {\r
1582   char *dummy;\r
1583   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1584 }\r
1585 \r
1586 int\r
1587 MainWindowUp()\r
1588 { // [HGM] args: allows testing if main window is realized from back-end\r
1589   return hwndMain != NULL;\r
1590 }\r
1591 \r
1592 void\r
1593 PopUpStartupDialog()\r
1594 {\r
1595     FARPROC lpProc;\r
1596     \r
1597     LoadLanguageFile(appData.language);\r
1598     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1599     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1600     FreeProcInstance(lpProc);\r
1601 }\r
1602 \r
1603 /*---------------------------------------------------------------------------*\\r
1604  *\r
1605  * GDI board drawing routines\r
1606  *\r
1607 \*---------------------------------------------------------------------------*/\r
1608 \r
1609 /* [AS] Draw square using background texture */\r
1610 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1611 {\r
1612     XFORM   x;\r
1613 \r
1614     if( mode == 0 ) {\r
1615         return; /* Should never happen! */\r
1616     }\r
1617 \r
1618     SetGraphicsMode( dst, GM_ADVANCED );\r
1619 \r
1620     switch( mode ) {\r
1621     case 1:\r
1622         /* Identity */\r
1623         break;\r
1624     case 2:\r
1625         /* X reflection */\r
1626         x.eM11 = -1.0;\r
1627         x.eM12 = 0;\r
1628         x.eM21 = 0;\r
1629         x.eM22 = 1.0;\r
1630         x.eDx = (FLOAT) dw + dx - 1;\r
1631         x.eDy = 0;\r
1632         dx = 0;\r
1633         SetWorldTransform( dst, &x );\r
1634         break;\r
1635     case 3:\r
1636         /* Y reflection */\r
1637         x.eM11 = 1.0;\r
1638         x.eM12 = 0;\r
1639         x.eM21 = 0;\r
1640         x.eM22 = -1.0;\r
1641         x.eDx = 0;\r
1642         x.eDy = (FLOAT) dh + dy - 1;\r
1643         dy = 0;\r
1644         SetWorldTransform( dst, &x );\r
1645         break;\r
1646     case 4:\r
1647         /* X/Y flip */\r
1648         x.eM11 = 0;\r
1649         x.eM12 = 1.0;\r
1650         x.eM21 = 1.0;\r
1651         x.eM22 = 0;\r
1652         x.eDx = (FLOAT) dx;\r
1653         x.eDy = (FLOAT) dy;\r
1654         dx = 0;\r
1655         dy = 0;\r
1656         SetWorldTransform( dst, &x );\r
1657         break;\r
1658     }\r
1659 \r
1660     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1661 \r
1662     x.eM11 = 1.0;\r
1663     x.eM12 = 0;\r
1664     x.eM21 = 0;\r
1665     x.eM22 = 1.0;\r
1666     x.eDx = 0;\r
1667     x.eDy = 0;\r
1668     SetWorldTransform( dst, &x );\r
1669 \r
1670     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1671 }\r
1672 \r
1673 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1674 enum {\r
1675     PM_WP = (int) WhitePawn, \r
1676     PM_WN = (int) WhiteKnight, \r
1677     PM_WB = (int) WhiteBishop, \r
1678     PM_WR = (int) WhiteRook, \r
1679     PM_WQ = (int) WhiteQueen, \r
1680     PM_WF = (int) WhiteFerz, \r
1681     PM_WW = (int) WhiteWazir, \r
1682     PM_WE = (int) WhiteAlfil, \r
1683     PM_WM = (int) WhiteMan, \r
1684     PM_WO = (int) WhiteCannon, \r
1685     PM_WU = (int) WhiteUnicorn, \r
1686     PM_WH = (int) WhiteNightrider, \r
1687     PM_WA = (int) WhiteAngel, \r
1688     PM_WC = (int) WhiteMarshall, \r
1689     PM_WAB = (int) WhiteCardinal, \r
1690     PM_WD = (int) WhiteDragon, \r
1691     PM_WL = (int) WhiteLance, \r
1692     PM_WS = (int) WhiteCobra, \r
1693     PM_WV = (int) WhiteFalcon, \r
1694     PM_WSG = (int) WhiteSilver, \r
1695     PM_WG = (int) WhiteGrasshopper, \r
1696     PM_WK = (int) WhiteKing,\r
1697     PM_BP = (int) BlackPawn, \r
1698     PM_BN = (int) BlackKnight, \r
1699     PM_BB = (int) BlackBishop, \r
1700     PM_BR = (int) BlackRook, \r
1701     PM_BQ = (int) BlackQueen, \r
1702     PM_BF = (int) BlackFerz, \r
1703     PM_BW = (int) BlackWazir, \r
1704     PM_BE = (int) BlackAlfil, \r
1705     PM_BM = (int) BlackMan,\r
1706     PM_BO = (int) BlackCannon, \r
1707     PM_BU = (int) BlackUnicorn, \r
1708     PM_BH = (int) BlackNightrider, \r
1709     PM_BA = (int) BlackAngel, \r
1710     PM_BC = (int) BlackMarshall, \r
1711     PM_BG = (int) BlackGrasshopper, \r
1712     PM_BAB = (int) BlackCardinal,\r
1713     PM_BD = (int) BlackDragon,\r
1714     PM_BL = (int) BlackLance,\r
1715     PM_BS = (int) BlackCobra,\r
1716     PM_BV = (int) BlackFalcon,\r
1717     PM_BSG = (int) BlackSilver,\r
1718     PM_BK = (int) BlackKing\r
1719 };\r
1720 \r
1721 static HFONT hPieceFont = NULL;\r
1722 static HBITMAP hPieceMask[(int) EmptySquare];\r
1723 static HBITMAP hPieceFace[(int) EmptySquare];\r
1724 static int fontBitmapSquareSize = 0;\r
1725 static char pieceToFontChar[(int) EmptySquare] =\r
1726                               { 'p', 'n', 'b', 'r', 'q', \r
1727                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1728                       'k', 'o', 'm', 'v', 't', 'w', \r
1729                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1730                                                               'l' };\r
1731 \r
1732 extern BOOL SetCharTable( char *table, const char * map );\r
1733 /* [HGM] moved to backend.c */\r
1734 \r
1735 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1736 {\r
1737     HBRUSH hbrush;\r
1738     BYTE r1 = GetRValue( color );\r
1739     BYTE g1 = GetGValue( color );\r
1740     BYTE b1 = GetBValue( color );\r
1741     BYTE r2 = r1 / 2;\r
1742     BYTE g2 = g1 / 2;\r
1743     BYTE b2 = b1 / 2;\r
1744     RECT rc;\r
1745 \r
1746     /* Create a uniform background first */\r
1747     hbrush = CreateSolidBrush( color );\r
1748     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1749     FillRect( hdc, &rc, hbrush );\r
1750     DeleteObject( hbrush );\r
1751     \r
1752     if( mode == 1 ) {\r
1753         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1754         int steps = squareSize / 2;\r
1755         int i;\r
1756 \r
1757         for( i=0; i<steps; i++ ) {\r
1758             BYTE r = r1 - (r1-r2) * i / steps;\r
1759             BYTE g = g1 - (g1-g2) * i / steps;\r
1760             BYTE b = b1 - (b1-b2) * i / steps;\r
1761 \r
1762             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1763             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1764             FillRect( hdc, &rc, hbrush );\r
1765             DeleteObject(hbrush);\r
1766         }\r
1767     }\r
1768     else if( mode == 2 ) {\r
1769         /* Diagonal gradient, good more or less for every piece */\r
1770         POINT triangle[3];\r
1771         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1772         HBRUSH hbrush_old;\r
1773         int steps = squareSize;\r
1774         int i;\r
1775 \r
1776         triangle[0].x = squareSize - steps;\r
1777         triangle[0].y = squareSize;\r
1778         triangle[1].x = squareSize;\r
1779         triangle[1].y = squareSize;\r
1780         triangle[2].x = squareSize;\r
1781         triangle[2].y = squareSize - steps;\r
1782 \r
1783         for( i=0; i<steps; i++ ) {\r
1784             BYTE r = r1 - (r1-r2) * i / steps;\r
1785             BYTE g = g1 - (g1-g2) * i / steps;\r
1786             BYTE b = b1 - (b1-b2) * i / steps;\r
1787 \r
1788             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1789             hbrush_old = SelectObject( hdc, hbrush );\r
1790             Polygon( hdc, triangle, 3 );\r
1791             SelectObject( hdc, hbrush_old );\r
1792             DeleteObject(hbrush);\r
1793             triangle[0].x++;\r
1794             triangle[2].y++;\r
1795         }\r
1796 \r
1797         SelectObject( hdc, hpen );\r
1798     }\r
1799 }\r
1800 \r
1801 /*\r
1802     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1803     seems to work ok. The main problem here is to find the "inside" of a chess\r
1804     piece: follow the steps as explained below.\r
1805 */\r
1806 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1807 {\r
1808     HBITMAP hbm;\r
1809     HBITMAP hbm_old;\r
1810     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1811     RECT rc;\r
1812     SIZE sz;\r
1813 \r
1814     POINT pt;\r
1815     int backColor = whitePieceColor; \r
1816     int foreColor = blackPieceColor;\r
1817     \r
1818     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1819         backColor = appData.fontBackColorWhite;\r
1820         foreColor = appData.fontForeColorWhite;\r
1821     }\r
1822     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1823         backColor = appData.fontBackColorBlack;\r
1824         foreColor = appData.fontForeColorBlack;\r
1825     }\r
1826 \r
1827     /* Mask */\r
1828     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1829 \r
1830     hbm_old = SelectObject( hdc, hbm );\r
1831 \r
1832     rc.left = 0;\r
1833     rc.top = 0;\r
1834     rc.right = squareSize;\r
1835     rc.bottom = squareSize;\r
1836 \r
1837     /* Step 1: background is now black */\r
1838     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1839 \r
1840     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1841 \r
1842     pt.x = (squareSize - sz.cx) / 2;\r
1843     pt.y = (squareSize - sz.cy) / 2;\r
1844 \r
1845     SetBkMode( hdc, TRANSPARENT );\r
1846     SetTextColor( hdc, chroma );\r
1847     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1848     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1849 \r
1850     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1851     /* Step 3: the area outside the piece is filled with white */\r
1852 //    FloodFill( hdc, 0, 0, chroma );\r
1853     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1854     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1855     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1856     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1857     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1858     /* \r
1859         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1860         but if the start point is not inside the piece we're lost!\r
1861         There should be a better way to do this... if we could create a region or path\r
1862         from the fill operation we would be fine for example.\r
1863     */\r
1864 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1865     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1866 \r
1867     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1868         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1869         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1870 \r
1871         SelectObject( dc2, bm2 );\r
1872         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1873         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1874         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1875         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1876         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1877 \r
1878         DeleteDC( dc2 );\r
1879         DeleteObject( bm2 );\r
1880     }\r
1881 \r
1882     SetTextColor( hdc, 0 );\r
1883     /* \r
1884         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1885         draw the piece again in black for safety.\r
1886     */\r
1887     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1888 \r
1889     SelectObject( hdc, hbm_old );\r
1890 \r
1891     if( hPieceMask[index] != NULL ) {\r
1892         DeleteObject( hPieceMask[index] );\r
1893     }\r
1894 \r
1895     hPieceMask[index] = hbm;\r
1896 \r
1897     /* Face */\r
1898     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1899 \r
1900     SelectObject( hdc, hbm );\r
1901 \r
1902     {\r
1903         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1904         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1905         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1906 \r
1907         SelectObject( dc1, hPieceMask[index] );\r
1908         SelectObject( dc2, bm2 );\r
1909         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1910         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1911         \r
1912         /* \r
1913             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1914             the piece background and deletes (makes transparent) the rest.\r
1915             Thanks to that mask, we are free to paint the background with the greates\r
1916             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1917             We use this, to make gradients and give the pieces a "roundish" look.\r
1918         */\r
1919         SetPieceBackground( hdc, backColor, 2 );\r
1920         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1921 \r
1922         DeleteDC( dc2 );\r
1923         DeleteDC( dc1 );\r
1924         DeleteObject( bm2 );\r
1925     }\r
1926 \r
1927     SetTextColor( hdc, foreColor );\r
1928     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1929 \r
1930     SelectObject( hdc, hbm_old );\r
1931 \r
1932     if( hPieceFace[index] != NULL ) {\r
1933         DeleteObject( hPieceFace[index] );\r
1934     }\r
1935 \r
1936     hPieceFace[index] = hbm;\r
1937 }\r
1938 \r
1939 static int TranslatePieceToFontPiece( int piece )\r
1940 {\r
1941     switch( piece ) {\r
1942     case BlackPawn:\r
1943         return PM_BP;\r
1944     case BlackKnight:\r
1945         return PM_BN;\r
1946     case BlackBishop:\r
1947         return PM_BB;\r
1948     case BlackRook:\r
1949         return PM_BR;\r
1950     case BlackQueen:\r
1951         return PM_BQ;\r
1952     case BlackKing:\r
1953         return PM_BK;\r
1954     case WhitePawn:\r
1955         return PM_WP;\r
1956     case WhiteKnight:\r
1957         return PM_WN;\r
1958     case WhiteBishop:\r
1959         return PM_WB;\r
1960     case WhiteRook:\r
1961         return PM_WR;\r
1962     case WhiteQueen:\r
1963         return PM_WQ;\r
1964     case WhiteKing:\r
1965         return PM_WK;\r
1966 \r
1967     case BlackAngel:\r
1968         return PM_BA;\r
1969     case BlackMarshall:\r
1970         return PM_BC;\r
1971     case BlackFerz:\r
1972         return PM_BF;\r
1973     case BlackNightrider:\r
1974         return PM_BH;\r
1975     case BlackAlfil:\r
1976         return PM_BE;\r
1977     case BlackWazir:\r
1978         return PM_BW;\r
1979     case BlackUnicorn:\r
1980         return PM_BU;\r
1981     case BlackCannon:\r
1982         return PM_BO;\r
1983     case BlackGrasshopper:\r
1984         return PM_BG;\r
1985     case BlackMan:\r
1986         return PM_BM;\r
1987     case BlackSilver:\r
1988         return PM_BSG;\r
1989     case BlackLance:\r
1990         return PM_BL;\r
1991     case BlackFalcon:\r
1992         return PM_BV;\r
1993     case BlackCobra:\r
1994         return PM_BS;\r
1995     case BlackCardinal:\r
1996         return PM_BAB;\r
1997     case BlackDragon:\r
1998         return PM_BD;\r
1999 \r
2000     case WhiteAngel:\r
2001         return PM_WA;\r
2002     case WhiteMarshall:\r
2003         return PM_WC;\r
2004     case WhiteFerz:\r
2005         return PM_WF;\r
2006     case WhiteNightrider:\r
2007         return PM_WH;\r
2008     case WhiteAlfil:\r
2009         return PM_WE;\r
2010     case WhiteWazir:\r
2011         return PM_WW;\r
2012     case WhiteUnicorn:\r
2013         return PM_WU;\r
2014     case WhiteCannon:\r
2015         return PM_WO;\r
2016     case WhiteGrasshopper:\r
2017         return PM_WG;\r
2018     case WhiteMan:\r
2019         return PM_WM;\r
2020     case WhiteSilver:\r
2021         return PM_WSG;\r
2022     case WhiteLance:\r
2023         return PM_WL;\r
2024     case WhiteFalcon:\r
2025         return PM_WV;\r
2026     case WhiteCobra:\r
2027         return PM_WS;\r
2028     case WhiteCardinal:\r
2029         return PM_WAB;\r
2030     case WhiteDragon:\r
2031         return PM_WD;\r
2032     }\r
2033 \r
2034     return 0;\r
2035 }\r
2036 \r
2037 void CreatePiecesFromFont()\r
2038 {\r
2039     LOGFONT lf;\r
2040     HDC hdc_window = NULL;\r
2041     HDC hdc = NULL;\r
2042     HFONT hfont_old;\r
2043     int fontHeight;\r
2044     int i;\r
2045 \r
2046     if( fontBitmapSquareSize < 0 ) {\r
2047         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2048         return;\r
2049     }\r
2050 \r
2051     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2052             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2053         fontBitmapSquareSize = -1;\r
2054         return;\r
2055     }\r
2056 \r
2057     if( fontBitmapSquareSize != squareSize ) {\r
2058         hdc_window = GetDC( hwndMain );\r
2059         hdc = CreateCompatibleDC( hdc_window );\r
2060 \r
2061         if( hPieceFont != NULL ) {\r
2062             DeleteObject( hPieceFont );\r
2063         }\r
2064         else {\r
2065             for( i=0; i<=(int)BlackKing; i++ ) {\r
2066                 hPieceMask[i] = NULL;\r
2067                 hPieceFace[i] = NULL;\r
2068             }\r
2069         }\r
2070 \r
2071         fontHeight = 75;\r
2072 \r
2073         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2074             fontHeight = appData.fontPieceSize;\r
2075         }\r
2076 \r
2077         fontHeight = (fontHeight * squareSize) / 100;\r
2078 \r
2079         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2080         lf.lfWidth = 0;\r
2081         lf.lfEscapement = 0;\r
2082         lf.lfOrientation = 0;\r
2083         lf.lfWeight = FW_NORMAL;\r
2084         lf.lfItalic = 0;\r
2085         lf.lfUnderline = 0;\r
2086         lf.lfStrikeOut = 0;\r
2087         lf.lfCharSet = DEFAULT_CHARSET;\r
2088         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2089         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2090         lf.lfQuality = PROOF_QUALITY;\r
2091         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2092         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2093         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2094 \r
2095         hPieceFont = CreateFontIndirect( &lf );\r
2096 \r
2097         if( hPieceFont == NULL ) {\r
2098             fontBitmapSquareSize = -2;\r
2099         }\r
2100         else {\r
2101             /* Setup font-to-piece character table */\r
2102             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2103                 /* No (or wrong) global settings, try to detect the font */\r
2104                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2105                     /* Alpha */\r
2106                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2107                 }\r
2108                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2109                     /* DiagramTT* family */\r
2110                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2111                 }\r
2112                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2113                     /* Fairy symbols */\r
2114                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2115                 }\r
2116                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2117                     /* Good Companion (Some characters get warped as literal :-( */\r
2118                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2119                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2120                     SetCharTable(pieceToFontChar, s);\r
2121                 }\r
2122                 else {\r
2123                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2124                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2125                 }\r
2126             }\r
2127 \r
2128             /* Create bitmaps */\r
2129             hfont_old = SelectObject( hdc, hPieceFont );\r
2130             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2131                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2132                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2133 \r
2134             SelectObject( hdc, hfont_old );\r
2135 \r
2136             fontBitmapSquareSize = squareSize;\r
2137         }\r
2138     }\r
2139 \r
2140     if( hdc != NULL ) {\r
2141         DeleteDC( hdc );\r
2142     }\r
2143 \r
2144     if( hdc_window != NULL ) {\r
2145         ReleaseDC( hwndMain, hdc_window );\r
2146     }\r
2147 }\r
2148 \r
2149 HBITMAP\r
2150 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2151 {\r
2152   char name[128], buf[MSG_SIZ];\r
2153 \r
2154     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2155   if(appData.pieceDirectory[0]) {\r
2156     HBITMAP res;\r
2157     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2158     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2159     if(res) return res;\r
2160   }\r
2161   if (gameInfo.event &&\r
2162       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2163       strcmp(name, "k80s") == 0) {\r
2164     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2165   }\r
2166   return LoadBitmap(hinst, name);\r
2167 }\r
2168 \r
2169 \r
2170 /* Insert a color into the program's logical palette\r
2171    structure.  This code assumes the given color is\r
2172    the result of the RGB or PALETTERGB macro, and it\r
2173    knows how those macros work (which is documented).\r
2174 */\r
2175 VOID\r
2176 InsertInPalette(COLORREF color)\r
2177 {\r
2178   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2179 \r
2180   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2181     DisplayFatalError(_("Too many colors"), 0, 1);\r
2182     pLogPal->palNumEntries--;\r
2183     return;\r
2184   }\r
2185 \r
2186   pe->peFlags = (char) 0;\r
2187   pe->peRed = (char) (0xFF & color);\r
2188   pe->peGreen = (char) (0xFF & (color >> 8));\r
2189   pe->peBlue = (char) (0xFF & (color >> 16));\r
2190   return;\r
2191 }\r
2192 \r
2193 \r
2194 VOID\r
2195 InitDrawingColors()\r
2196 {\r
2197   int i;\r
2198   if (pLogPal == NULL) {\r
2199     /* Allocate enough memory for a logical palette with\r
2200      * PALETTESIZE entries and set the size and version fields\r
2201      * of the logical palette structure.\r
2202      */\r
2203     pLogPal = (NPLOGPALETTE)\r
2204       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2205                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2206     pLogPal->palVersion    = 0x300;\r
2207   }\r
2208   pLogPal->palNumEntries = 0;\r
2209 \r
2210   InsertInPalette(lightSquareColor);\r
2211   InsertInPalette(darkSquareColor);\r
2212   InsertInPalette(whitePieceColor);\r
2213   InsertInPalette(blackPieceColor);\r
2214   InsertInPalette(highlightSquareColor);\r
2215   InsertInPalette(premoveHighlightColor);\r
2216 \r
2217   /*  create a logical color palette according the information\r
2218    *  in the LOGPALETTE structure.\r
2219    */\r
2220   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2221 \r
2222   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2223   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2224   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2225   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2226   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2227   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2228   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2229     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2230 \r
2231    /* [AS] Force rendering of the font-based pieces */\r
2232   if( fontBitmapSquareSize > 0 ) {\r
2233     fontBitmapSquareSize = 0;\r
2234   }\r
2235 }\r
2236 \r
2237 \r
2238 int\r
2239 BoardWidth(int boardSize, int n)\r
2240 { /* [HGM] argument n added to allow different width and height */\r
2241   int lineGap = sizeInfo[boardSize].lineGap;\r
2242 \r
2243   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2244       lineGap = appData.overrideLineGap;\r
2245   }\r
2246 \r
2247   return (n + 1) * lineGap +\r
2248           n * sizeInfo[boardSize].squareSize;\r
2249 }\r
2250 \r
2251 /* Respond to board resize by dragging edge */\r
2252 VOID\r
2253 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2254 {\r
2255   BoardSize newSize = NUM_SIZES - 1;\r
2256   static int recurse = 0;\r
2257   if (IsIconic(hwndMain)) return;\r
2258   if (recurse > 0) return;\r
2259   recurse++;\r
2260   while (newSize > 0) {\r
2261         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2262         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2263            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2264     newSize--;\r
2265   } \r
2266   boardSize = newSize;\r
2267   InitDrawingSizes(boardSize, flags);\r
2268   recurse--;\r
2269 }\r
2270 \r
2271 \r
2272 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2273 \r
2274 VOID\r
2275 InitDrawingSizes(BoardSize boardSize, int flags)\r
2276 {\r
2277   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2278   ChessSquare piece;\r
2279   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2280   HDC hdc;\r
2281   SIZE clockSize, messageSize;\r
2282   HFONT oldFont;\r
2283   char buf[MSG_SIZ];\r
2284   char *str;\r
2285   HMENU hmenu = GetMenu(hwndMain);\r
2286   RECT crect, wrect, oldRect;\r
2287   int offby;\r
2288   LOGBRUSH logbrush;\r
2289   VariantClass v = gameInfo.variant;\r
2290 \r
2291   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2292   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2293 \r
2294   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2295   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2296   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2297   oldBoardSize = boardSize;\r
2298 \r
2299   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2300   { // correct board size to one where built-in pieces exist\r
2301     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2302        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2303 \r
2304       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2305       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2306       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2307       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2308       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2309                                    boardSize = SizeMiddling;\r
2310     }\r
2311   }\r
2312   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2313 \r
2314   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2315   oldRect.top = wpMain.y;\r
2316   oldRect.right = wpMain.x + wpMain.width;\r
2317   oldRect.bottom = wpMain.y + wpMain.height;\r
2318 \r
2319   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2320   smallLayout = sizeInfo[boardSize].smallLayout;\r
2321   squareSize = sizeInfo[boardSize].squareSize;\r
2322   lineGap = sizeInfo[boardSize].lineGap;\r
2323   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2324   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2325 \r
2326   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2327       lineGap = appData.overrideLineGap;\r
2328   }\r
2329 \r
2330   if (tinyLayout != oldTinyLayout) {\r
2331     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2332     if (tinyLayout) {\r
2333       style &= ~WS_SYSMENU;\r
2334       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2335                  "&Minimize\tCtrl+F4");\r
2336     } else {\r
2337       style |= WS_SYSMENU;\r
2338       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2339     }\r
2340     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2341 \r
2342     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2343       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2344         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2345     }\r
2346     DrawMenuBar(hwndMain);\r
2347   }\r
2348 \r
2349   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2350   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2351 \r
2352   /* Get text area sizes */\r
2353   hdc = GetDC(hwndMain);\r
2354   if (appData.clockMode) {\r
2355     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2356   } else {\r
2357     snprintf(buf, MSG_SIZ, _("White"));\r
2358   }\r
2359   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2360   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2361   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2362   str = _("We only care about the height here");\r
2363   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2364   SelectObject(hdc, oldFont);\r
2365   ReleaseDC(hwndMain, hdc);\r
2366 \r
2367   /* Compute where everything goes */\r
2368   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2369         /* [HGM] logo: if either logo is on, reserve space for it */\r
2370         logoHeight =  2*clockSize.cy;\r
2371         leftLogoRect.left   = OUTER_MARGIN;\r
2372         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2373         leftLogoRect.top    = OUTER_MARGIN;\r
2374         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2375 \r
2376         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2377         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2378         rightLogoRect.top    = OUTER_MARGIN;\r
2379         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2380 \r
2381 \r
2382     whiteRect.left = leftLogoRect.right;\r
2383     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2384     whiteRect.top = OUTER_MARGIN;\r
2385     whiteRect.bottom = whiteRect.top + logoHeight;\r
2386 \r
2387     blackRect.right = rightLogoRect.left;\r
2388     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2389     blackRect.top = whiteRect.top;\r
2390     blackRect.bottom = whiteRect.bottom;\r
2391   } else {\r
2392     whiteRect.left = OUTER_MARGIN;\r
2393     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2394     whiteRect.top = OUTER_MARGIN;\r
2395     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2396 \r
2397     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2398     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2399     blackRect.top = whiteRect.top;\r
2400     blackRect.bottom = whiteRect.bottom;\r
2401 \r
2402     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2403   }\r
2404 \r
2405   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2406   if (appData.showButtonBar) {\r
2407     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2408       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2409   } else {\r
2410     messageRect.right = OUTER_MARGIN + boardWidth;\r
2411   }\r
2412   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2413   messageRect.bottom = messageRect.top + messageSize.cy;\r
2414 \r
2415   boardRect.left = OUTER_MARGIN;\r
2416   boardRect.right = boardRect.left + boardWidth;\r
2417   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2418   boardRect.bottom = boardRect.top + boardHeight;\r
2419 \r
2420   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2421   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2422   oldTinyLayout = tinyLayout;\r
2423   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2424   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2425     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2426   winW *= 1 + twoBoards;\r
2427   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2428   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2429   wpMain.height = winH; //       without disturbing window attachments\r
2430   GetWindowRect(hwndMain, &wrect);\r
2431   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2432                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2433 \r
2434   // [HGM] placement: let attached windows follow size change.\r
2435   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2436   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2437   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2438   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2439   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2440 \r
2441   /* compensate if menu bar wrapped */\r
2442   GetClientRect(hwndMain, &crect);\r
2443   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2444   wpMain.height += offby;\r
2445   switch (flags) {\r
2446   case WMSZ_TOPLEFT:\r
2447     SetWindowPos(hwndMain, NULL, \r
2448                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2449                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2450     break;\r
2451 \r
2452   case WMSZ_TOPRIGHT:\r
2453   case WMSZ_TOP:\r
2454     SetWindowPos(hwndMain, NULL, \r
2455                  wrect.left, wrect.bottom - wpMain.height, \r
2456                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2457     break;\r
2458 \r
2459   case WMSZ_BOTTOMLEFT:\r
2460   case WMSZ_LEFT:\r
2461     SetWindowPos(hwndMain, NULL, \r
2462                  wrect.right - wpMain.width, wrect.top, \r
2463                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2464     break;\r
2465 \r
2466   case WMSZ_BOTTOMRIGHT:\r
2467   case WMSZ_BOTTOM:\r
2468   case WMSZ_RIGHT:\r
2469   default:\r
2470     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2471                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2472     break;\r
2473   }\r
2474 \r
2475   hwndPause = NULL;\r
2476   for (i = 0; i < N_BUTTONS; i++) {\r
2477     if (buttonDesc[i].hwnd != NULL) {\r
2478       DestroyWindow(buttonDesc[i].hwnd);\r
2479       buttonDesc[i].hwnd = NULL;\r
2480     }\r
2481     if (appData.showButtonBar) {\r
2482       buttonDesc[i].hwnd =\r
2483         CreateWindow("BUTTON", buttonDesc[i].label,\r
2484                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2485                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2486                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2487                      (HMENU) buttonDesc[i].id,\r
2488                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2489       if (tinyLayout) {\r
2490         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2491                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2492                     MAKELPARAM(FALSE, 0));\r
2493       }\r
2494       if (buttonDesc[i].id == IDM_Pause)\r
2495         hwndPause = buttonDesc[i].hwnd;\r
2496       buttonDesc[i].wndproc = (WNDPROC)\r
2497         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2498     }\r
2499   }\r
2500   if (gridPen != NULL) DeleteObject(gridPen);\r
2501   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2502   if (premovePen != NULL) DeleteObject(premovePen);\r
2503   if (lineGap != 0) {\r
2504     logbrush.lbStyle = BS_SOLID;\r
2505     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2506     gridPen =\r
2507       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2508                    lineGap, &logbrush, 0, NULL);\r
2509     logbrush.lbColor = highlightSquareColor;\r
2510     highlightPen =\r
2511       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2512                    lineGap, &logbrush, 0, NULL);\r
2513 \r
2514     logbrush.lbColor = premoveHighlightColor; \r
2515     premovePen =\r
2516       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2517                    lineGap, &logbrush, 0, NULL);\r
2518 \r
2519     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2520     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2521       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2522       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2523         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2524       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2525         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2526       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2527     }\r
2528     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2529       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2530       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2531         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2532         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2533       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2534         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2535       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2536     }\r
2537   }\r
2538 \r
2539   /* [HGM] Licensing requirement */\r
2540 #ifdef GOTHIC\r
2541   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2542 #endif\r
2543 #ifdef FALCON\r
2544   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2545 #endif\r
2546   GothicPopUp( "", VariantNormal);\r
2547 \r
2548 \r
2549 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2550 \r
2551   /* Load piece bitmaps for this board size */\r
2552   for (i=0; i<=2; i++) {\r
2553     for (piece = WhitePawn;\r
2554          (int) piece < (int) BlackPawn;\r
2555          piece = (ChessSquare) ((int) piece + 1)) {\r
2556       if (pieceBitmap[i][piece] != NULL)\r
2557         DeleteObject(pieceBitmap[i][piece]);\r
2558     }\r
2559   }\r
2560 \r
2561   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2562   // Orthodox Chess pieces\r
2563   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2564   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2565   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2566   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2567   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2568   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2569   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2570   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2571   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2572   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2573   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2574   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2575   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2576   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2577   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2578   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2579     // in Shogi, Hijack the unused Queen for Lance\r
2580     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2581     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2582     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2583   } else {\r
2584     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2585     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2586     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2587   }\r
2588 \r
2589   if(squareSize <= 72 && squareSize >= 33) { \r
2590     /* A & C are available in most sizes now */\r
2591     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2592       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2593       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2594       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2595       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2596       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2597       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2598       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2599       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2600       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2601       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2602       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2603       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2604     } else { // Smirf-like\r
2605       if(gameInfo.variant == VariantSChess) {\r
2606         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2607         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2608         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2609       } else {\r
2610         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2611         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2612         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2613       }\r
2614     }\r
2615     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2616       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2617       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2618       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2619     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2620       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2621       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2622       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2623     } else { // WinBoard standard\r
2624       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2625       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2626       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2627     }\r
2628   }\r
2629 \r
2630 \r
2631   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2632     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2633     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2634     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2635     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2636     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2637     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2638     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2639     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2640     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2641     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2642     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2643     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2644     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2645     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2646     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2647     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2648     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2649     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2650     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2651     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2652     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2653     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2654     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2655     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2656     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2657     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2658     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2659     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2660     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2661     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2662     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2663     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2664     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2665 \r
2666     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2667       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2668       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2669       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2670       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2671       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2672       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2673       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2674       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2675       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2676       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2677       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2678       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2679     } else {\r
2680       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2681       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2682       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2683       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2684       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2685       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2686       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2687       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2688       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2689       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2690       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2691       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2692     }\r
2693 \r
2694   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2695     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2696     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2697     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2698     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2699     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2700     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2701     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2702     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2703     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2704     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2705     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2706     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2707     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2708     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2709   }\r
2710 \r
2711 \r
2712   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2713   /* special Shogi support in this size */\r
2714   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2715       for (piece = WhitePawn;\r
2716            (int) piece < (int) BlackPawn;\r
2717            piece = (ChessSquare) ((int) piece + 1)) {\r
2718         if (pieceBitmap[i][piece] != NULL)\r
2719           DeleteObject(pieceBitmap[i][piece]);\r
2720       }\r
2721     }\r
2722   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2723   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2724   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2725   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2726   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2727   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2728   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2729   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2730   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2731   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2732   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2733   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2734   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2735   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2736   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2737   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2738   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2739   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2740   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2741   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2742   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2743   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2744   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2745   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2746   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2747   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2748   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2749   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2750   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2751   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2752   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2753   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2754   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2755   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2756   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2757   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2758   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2759   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2760   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2761   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2762   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2763   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2764   minorSize = 0;\r
2765   }\r
2766 }\r
2767 \r
2768 HBITMAP\r
2769 PieceBitmap(ChessSquare p, int kind)\r
2770 {\r
2771   if ((int) p >= (int) BlackPawn)\r
2772     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2773 \r
2774   return pieceBitmap[kind][(int) p];\r
2775 }\r
2776 \r
2777 /***************************************************************/\r
2778 \r
2779 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2780 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2781 /*\r
2782 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2783 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2784 */\r
2785 \r
2786 VOID\r
2787 SquareToPos(int row, int column, int * x, int * y)\r
2788 {\r
2789   if (flipView) {\r
2790     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2791     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2792   } else {\r
2793     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2794     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2795   }\r
2796 }\r
2797 \r
2798 VOID\r
2799 DrawCoordsOnDC(HDC hdc)\r
2800 {\r
2801   static char files[] = "0123456789012345678901221098765432109876543210";\r
2802   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2803   char str[2] = { NULLCHAR, NULLCHAR };\r
2804   int oldMode, oldAlign, x, y, start, i;\r
2805   HFONT oldFont;\r
2806   HBRUSH oldBrush;\r
2807 \r
2808   if (!appData.showCoords)\r
2809     return;\r
2810 \r
2811   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2812 \r
2813   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2814   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2815   oldAlign = GetTextAlign(hdc);\r
2816   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2817 \r
2818   y = boardRect.top + lineGap;\r
2819   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2820 \r
2821   if(border) {\r
2822     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2823     x += border - lineGap - 4; y += squareSize - 6;\r
2824   } else\r
2825   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2826   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2827     str[0] = files[start + i];\r
2828     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2829     y += squareSize + lineGap;\r
2830   }\r
2831 \r
2832   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2833 \r
2834   if(border) {\r
2835     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2836     x += -border + 4; y += border - squareSize + 6;\r
2837   } else\r
2838   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2839   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2840     str[0] = ranks[start + i];\r
2841     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2842     x += squareSize + lineGap;\r
2843   }    \r
2844 \r
2845   SelectObject(hdc, oldBrush);\r
2846   SetBkMode(hdc, oldMode);\r
2847   SetTextAlign(hdc, oldAlign);\r
2848   SelectObject(hdc, oldFont);\r
2849 }\r
2850 \r
2851 VOID\r
2852 DrawGridOnDC(HDC hdc)\r
2853 {\r
2854   HPEN oldPen;\r
2855  \r
2856   if (lineGap != 0) {\r
2857     oldPen = SelectObject(hdc, gridPen);\r
2858     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2859     SelectObject(hdc, oldPen);\r
2860   }\r
2861 }\r
2862 \r
2863 #define HIGHLIGHT_PEN 0\r
2864 #define PREMOVE_PEN   1\r
2865 \r
2866 VOID\r
2867 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2868 {\r
2869   int x1, y1;\r
2870   HPEN oldPen, hPen;\r
2871   if (lineGap == 0) return;\r
2872   if (flipView) {\r
2873     x1 = boardRect.left +\r
2874       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2875     y1 = boardRect.top +\r
2876       lineGap/2 + y * (squareSize + lineGap) + border;\r
2877   } else {\r
2878     x1 = boardRect.left +\r
2879       lineGap/2 + x * (squareSize + lineGap) + border;\r
2880     y1 = boardRect.top +\r
2881       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2882   }\r
2883   hPen = pen ? premovePen : highlightPen;\r
2884   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2885   MoveToEx(hdc, x1, y1, NULL);\r
2886   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2887   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2888   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2889   LineTo(hdc, x1, y1);\r
2890   SelectObject(hdc, oldPen);\r
2891 }\r
2892 \r
2893 VOID\r
2894 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2895 {\r
2896   int i;\r
2897   for (i=0; i<2; i++) {\r
2898     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2899       DrawHighlightOnDC(hdc, TRUE,\r
2900                         h->sq[i].x, h->sq[i].y,\r
2901                         pen);\r
2902   }\r
2903 }\r
2904 \r
2905 /* Note: sqcolor is used only in monoMode */\r
2906 /* Note that this code is largely duplicated in woptions.c,\r
2907    function DrawSampleSquare, so that needs to be updated too */\r
2908 VOID\r
2909 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2910 {\r
2911   HBITMAP oldBitmap;\r
2912   HBRUSH oldBrush;\r
2913   int tmpSize;\r
2914 \r
2915   if (appData.blindfold) return;\r
2916 \r
2917   /* [AS] Use font-based pieces if needed */\r
2918   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2919     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2920     CreatePiecesFromFont();\r
2921 \r
2922     if( fontBitmapSquareSize == squareSize ) {\r
2923         int index = TranslatePieceToFontPiece(piece);\r
2924 \r
2925         SelectObject( tmphdc, hPieceMask[ index ] );\r
2926 \r
2927       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2928         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2929       else\r
2930         BitBlt( hdc,\r
2931             x, y,\r
2932             squareSize, squareSize,\r
2933             tmphdc,\r
2934             0, 0,\r
2935             SRCAND );\r
2936 \r
2937         SelectObject( tmphdc, hPieceFace[ index ] );\r
2938 \r
2939       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2940         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2941       else\r
2942         BitBlt( hdc,\r
2943             x, y,\r
2944             squareSize, squareSize,\r
2945             tmphdc,\r
2946             0, 0,\r
2947             SRCPAINT );\r
2948 \r
2949         return;\r
2950     }\r
2951   }\r
2952 \r
2953   if (appData.monoMode) {\r
2954     SelectObject(tmphdc, PieceBitmap(piece, \r
2955       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2956     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2957            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2958   } else {\r
2959     HBRUSH xBrush = whitePieceBrush;\r
2960     tmpSize = squareSize;\r
2961     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2962     if(minorSize &&\r
2963         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2964          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2965       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2966       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2967       x += (squareSize - minorSize)>>1;\r
2968       y += squareSize - minorSize - 2;\r
2969       tmpSize = minorSize;\r
2970     }\r
2971     if (color || appData.allWhite ) {\r
2972       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2973       if( color )\r
2974               oldBrush = SelectObject(hdc, xBrush);\r
2975       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2976       if(appData.upsideDown && color==flipView)\r
2977         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2978       else\r
2979         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2980       /* Use black for outline of white pieces */\r
2981       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2982       if(appData.upsideDown && color==flipView)\r
2983         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2984       else\r
2985         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2986     } else if(appData.pieceDirectory[0]) {\r
2987       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2988       oldBrush = SelectObject(hdc, xBrush);\r
2989       if(appData.upsideDown && color==flipView)\r
2990         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2991       else\r
2992         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2993       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2994       if(appData.upsideDown && color==flipView)\r
2995         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2996       else\r
2997         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2998     } else {\r
2999       /* Use square color for details of black pieces */\r
3000       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3001       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3002       if(appData.upsideDown && !flipView)\r
3003         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3004       else\r
3005         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3006     }\r
3007     SelectObject(hdc, oldBrush);\r
3008     SelectObject(tmphdc, oldBitmap);\r
3009   }\r
3010 }\r
3011 \r
3012 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3013 int GetBackTextureMode( int algo )\r
3014 {\r
3015     int result = BACK_TEXTURE_MODE_DISABLED;\r
3016 \r
3017     switch( algo ) \r
3018     {\r
3019         case BACK_TEXTURE_MODE_PLAIN:\r
3020             result = 1; /* Always use identity map */\r
3021             break;\r
3022         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3023             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3024             break;\r
3025     }\r
3026 \r
3027     return result;\r
3028 }\r
3029 \r
3030 /* \r
3031     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3032     to handle redraws cleanly (as random numbers would always be different).\r
3033 */\r
3034 VOID RebuildTextureSquareInfo()\r
3035 {\r
3036     BITMAP bi;\r
3037     int lite_w = 0;\r
3038     int lite_h = 0;\r
3039     int dark_w = 0;\r
3040     int dark_h = 0;\r
3041     int row;\r
3042     int col;\r
3043 \r
3044     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3045 \r
3046     if( liteBackTexture != NULL ) {\r
3047         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3048             lite_w = bi.bmWidth;\r
3049             lite_h = bi.bmHeight;\r
3050         }\r
3051     }\r
3052 \r
3053     if( darkBackTexture != NULL ) {\r
3054         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3055             dark_w = bi.bmWidth;\r
3056             dark_h = bi.bmHeight;\r
3057         }\r
3058     }\r
3059 \r
3060     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3061         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3062             if( (col + row) & 1 ) {\r
3063                 /* Lite square */\r
3064                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3065                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3066                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3067                   else\r
3068                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3069                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3070                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3071                   else\r
3072                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3073                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3074                 }\r
3075             }\r
3076             else {\r
3077                 /* Dark square */\r
3078                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3079                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3080                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3081                   else\r
3082                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3083                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3084                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3085                   else\r
3086                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3087                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3088                 }\r
3089             }\r
3090         }\r
3091     }\r
3092 }\r
3093 \r
3094 /* [AS] Arrow highlighting support */\r
3095 \r
3096 static double A_WIDTH = 5; /* Width of arrow body */\r
3097 \r
3098 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3099 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3100 \r
3101 static double Sqr( double x )\r
3102 {\r
3103     return x*x;\r
3104 }\r
3105 \r
3106 static int Round( double x )\r
3107 {\r
3108     return (int) (x + 0.5);\r
3109 }\r
3110 \r
3111 /* Draw an arrow between two points using current settings */\r
3112 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3113 {\r
3114     POINT arrow[7];\r
3115     double dx, dy, j, k, x, y;\r
3116 \r
3117     if( d_x == s_x ) {\r
3118         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3119 \r
3120         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3121         arrow[0].y = s_y;\r
3122 \r
3123         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3124         arrow[1].y = d_y - h;\r
3125 \r
3126         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3127         arrow[2].y = d_y - h;\r
3128 \r
3129         arrow[3].x = d_x;\r
3130         arrow[3].y = d_y;\r
3131 \r
3132         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3133         arrow[5].y = d_y - h;\r
3134 \r
3135         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3136         arrow[4].y = d_y - h;\r
3137 \r
3138         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3139         arrow[6].y = s_y;\r
3140     }\r
3141     else if( d_y == s_y ) {\r
3142         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3143 \r
3144         arrow[0].x = s_x;\r
3145         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3146 \r
3147         arrow[1].x = d_x - w;\r
3148         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3149 \r
3150         arrow[2].x = d_x - w;\r
3151         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3152 \r
3153         arrow[3].x = d_x;\r
3154         arrow[3].y = d_y;\r
3155 \r
3156         arrow[5].x = d_x - w;\r
3157         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3158 \r
3159         arrow[4].x = d_x - w;\r
3160         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3161 \r
3162         arrow[6].x = s_x;\r
3163         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3164     }\r
3165     else {\r
3166         /* [AS] Needed a lot of paper for this! :-) */\r
3167         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3168         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3169   \r
3170         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3171 \r
3172         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3173 \r
3174         x = s_x;\r
3175         y = s_y;\r
3176 \r
3177         arrow[0].x = Round(x - j);\r
3178         arrow[0].y = Round(y + j*dx);\r
3179 \r
3180         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3181         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3182 \r
3183         if( d_x > s_x ) {\r
3184             x = (double) d_x - k;\r
3185             y = (double) d_y - k*dy;\r
3186         }\r
3187         else {\r
3188             x = (double) d_x + k;\r
3189             y = (double) d_y + k*dy;\r
3190         }\r
3191 \r
3192         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3193 \r
3194         arrow[6].x = Round(x - j);\r
3195         arrow[6].y = Round(y + j*dx);\r
3196 \r
3197         arrow[2].x = Round(arrow[6].x + 2*j);\r
3198         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3199 \r
3200         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3201         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3202 \r
3203         arrow[4].x = d_x;\r
3204         arrow[4].y = d_y;\r
3205 \r
3206         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3207         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3208     }\r
3209 \r
3210     Polygon( hdc, arrow, 7 );\r
3211 }\r
3212 \r
3213 /* [AS] Draw an arrow between two squares */\r
3214 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3215 {\r
3216     int s_x, s_y, d_x, d_y;\r
3217     HPEN hpen;\r
3218     HPEN holdpen;\r
3219     HBRUSH hbrush;\r
3220     HBRUSH holdbrush;\r
3221     LOGBRUSH stLB;\r
3222 \r
3223     if( s_col == d_col && s_row == d_row ) {\r
3224         return;\r
3225     }\r
3226 \r
3227     /* Get source and destination points */\r
3228     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3229     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3230 \r
3231     if( d_y > s_y ) {\r
3232         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3233     }\r
3234     else if( d_y < s_y ) {\r
3235         d_y += squareSize / 2 + squareSize / 4;\r
3236     }\r
3237     else {\r
3238         d_y += squareSize / 2;\r
3239     }\r
3240 \r
3241     if( d_x > s_x ) {\r
3242         d_x += squareSize / 2 - squareSize / 4;\r
3243     }\r
3244     else if( d_x < s_x ) {\r
3245         d_x += squareSize / 2 + squareSize / 4;\r
3246     }\r
3247     else {\r
3248         d_x += squareSize / 2;\r
3249     }\r
3250 \r
3251     s_x += squareSize / 2;\r
3252     s_y += squareSize / 2;\r
3253 \r
3254     /* Adjust width */\r
3255     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3256 \r
3257     /* Draw */\r
3258     stLB.lbStyle = BS_SOLID;\r
3259     stLB.lbColor = appData.highlightArrowColor;\r
3260     stLB.lbHatch = 0;\r
3261 \r
3262     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3263     holdpen = SelectObject( hdc, hpen );\r
3264     hbrush = CreateBrushIndirect( &stLB );\r
3265     holdbrush = SelectObject( hdc, hbrush );\r
3266 \r
3267     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3268 \r
3269     SelectObject( hdc, holdpen );\r
3270     SelectObject( hdc, holdbrush );\r
3271     DeleteObject( hpen );\r
3272     DeleteObject( hbrush );\r
3273 }\r
3274 \r
3275 BOOL HasHighlightInfo()\r
3276 {\r
3277     BOOL result = FALSE;\r
3278 \r
3279     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3280         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3281     {\r
3282         result = TRUE;\r
3283     }\r
3284 \r
3285     return result;\r
3286 \r
3287 \r
3288 \r
3289 }\r
3290 \r
3291 BOOL IsDrawArrowEnabled()\r
3292 {\r
3293     BOOL result = FALSE;\r
3294 \r
3295     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3296         result = TRUE;\r
3297     }\r
3298 \r
3299     return result;\r
3300 }\r
3301 \r
3302 VOID DrawArrowHighlight( HDC hdc )\r
3303 {\r
3304     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3305         DrawArrowBetweenSquares( hdc,\r
3306             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3307             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3308     }\r
3309 }\r
3310 \r
3311 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3312 {\r
3313     HRGN result = NULL;\r
3314 \r
3315     if( HasHighlightInfo() ) {\r
3316         int x1, y1, x2, y2;\r
3317         int sx, sy, dx, dy;\r
3318 \r
3319         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3320         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3321 \r
3322         sx = MIN( x1, x2 );\r
3323         sy = MIN( y1, y2 );\r
3324         dx = MAX( x1, x2 ) + squareSize;\r
3325         dy = MAX( y1, y2 ) + squareSize;\r
3326 \r
3327         result = CreateRectRgn( sx, sy, dx, dy );\r
3328     }\r
3329 \r
3330     return result;\r
3331 }\r
3332 \r
3333 /*\r
3334     Warning: this function modifies the behavior of several other functions. \r
3335     \r
3336     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3337     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3338     repaint is scattered all over the place, which is not good for features such as\r
3339     "arrow highlighting" that require a full repaint of the board.\r
3340 \r
3341     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3342     user interaction, when speed is not so important) but especially to avoid errors\r
3343     in the displayed graphics.\r
3344 \r
3345     In such patched places, I always try refer to this function so there is a single\r
3346     place to maintain knowledge.\r
3347     \r
3348     To restore the original behavior, just return FALSE unconditionally.\r
3349 */\r
3350 BOOL IsFullRepaintPreferrable()\r
3351 {\r
3352     BOOL result = FALSE;\r
3353 \r
3354     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3355         /* Arrow may appear on the board */\r
3356         result = TRUE;\r
3357     }\r
3358 \r
3359     return result;\r
3360 }\r
3361 \r
3362 /* \r
3363     This function is called by DrawPosition to know whether a full repaint must\r
3364     be forced or not.\r
3365 \r
3366     Only DrawPosition may directly call this function, which makes use of \r
3367     some state information. Other function should call DrawPosition specifying \r
3368     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3369 */\r
3370 BOOL DrawPositionNeedsFullRepaint()\r
3371 {\r
3372     BOOL result = FALSE;\r
3373 \r
3374     /* \r
3375         Probably a slightly better policy would be to trigger a full repaint\r
3376         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3377         but animation is fast enough that it's difficult to notice.\r
3378     */\r
3379     if( animInfo.piece == EmptySquare ) {\r
3380         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3381             result = TRUE;\r
3382         }\r
3383     }\r
3384 \r
3385     return result;\r
3386 }\r
3387 \r
3388 static HBITMAP borderBitmap;\r
3389 \r
3390 VOID\r
3391 DrawBackgroundOnDC(HDC hdc)\r
3392 {\r
3393   \r
3394   BITMAP bi;\r
3395   HDC tmphdc;\r
3396   HBITMAP hbm;\r
3397   static char oldBorder[MSG_SIZ];\r
3398   int w = 600, h = 600, mode;\r
3399 \r
3400   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3401     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3402     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3403   }\r
3404   if(borderBitmap == NULL) { // loading failed, use white\r
3405     FillRect( hdc, &boardRect, whitePieceBrush );\r
3406     return;\r
3407   }\r
3408   tmphdc = CreateCompatibleDC(hdc);\r
3409   hbm = SelectObject(tmphdc, borderBitmap);\r
3410   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3411             w = bi.bmWidth;\r
3412             h = bi.bmHeight;\r
3413   }\r
3414   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3415   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3416                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3417   SetStretchBltMode(hdc, mode);\r
3418   SelectObject(tmphdc, hbm);\r
3419   DeleteDC(tmphdc);\r
3420 }\r
3421 \r
3422 VOID\r
3423 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3424 {\r
3425   int row, column, x, y, square_color, piece_color;\r
3426   ChessSquare piece;\r
3427   HBRUSH oldBrush;\r
3428   HDC texture_hdc = NULL;\r
3429 \r
3430   /* [AS] Initialize background textures if needed */\r
3431   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3432       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3433       if( backTextureSquareSize != squareSize \r
3434        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3435           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3436           backTextureSquareSize = squareSize;\r
3437           RebuildTextureSquareInfo();\r
3438       }\r
3439 \r
3440       texture_hdc = CreateCompatibleDC( hdc );\r
3441   }\r
3442 \r
3443   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3444     for (column = 0; column < BOARD_WIDTH; column++) {\r
3445   \r
3446       SquareToPos(row, column, &x, &y);\r
3447 \r
3448       piece = board[row][column];\r
3449 \r
3450       square_color = ((column + row) % 2) == 1;\r
3451       if( gameInfo.variant == VariantXiangqi ) {\r
3452           square_color = !InPalace(row, column);\r
3453           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3454           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3455       }\r
3456       piece_color = (int) piece < (int) BlackPawn;\r
3457 \r
3458 \r
3459       /* [HGM] holdings file: light square or black */\r
3460       if(column == BOARD_LEFT-2) {\r
3461             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3462                 square_color = 1;\r
3463             else {\r
3464                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3465                 continue;\r
3466             }\r
3467       } else\r
3468       if(column == BOARD_RGHT + 1 ) {\r
3469             if( row < gameInfo.holdingsSize )\r
3470                 square_color = 1;\r
3471             else {\r
3472                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3473                 continue;\r
3474             }\r
3475       }\r
3476       if(column == BOARD_LEFT-1 ) /* left align */\r
3477             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3478       else if( column == BOARD_RGHT) /* right align */\r
3479             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3480       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3481       else\r
3482       if (appData.monoMode) {\r
3483         if (piece == EmptySquare) {\r
3484           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3485                  square_color ? WHITENESS : BLACKNESS);\r
3486         } else {\r
3487           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3488         }\r
3489       } \r
3490       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3491           /* [AS] Draw the square using a texture bitmap */\r
3492           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3493           int r = row, c = column; // [HGM] do not flip board in flipView\r
3494           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3495 \r
3496           DrawTile( x, y, \r
3497               squareSize, squareSize, \r
3498               hdc, \r
3499               texture_hdc,\r
3500               backTextureSquareInfo[r][c].mode,\r
3501               backTextureSquareInfo[r][c].x,\r
3502               backTextureSquareInfo[r][c].y );\r
3503 \r
3504           SelectObject( texture_hdc, hbm );\r
3505 \r
3506           if (piece != EmptySquare) {\r
3507               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3508           }\r
3509       }\r
3510       else {\r
3511         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3512 \r
3513         oldBrush = SelectObject(hdc, brush );\r
3514         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3515         SelectObject(hdc, oldBrush);\r
3516         if (piece != EmptySquare)\r
3517           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3518       }\r
3519     }\r
3520   }\r
3521 \r
3522   if( texture_hdc != NULL ) {\r
3523     DeleteDC( texture_hdc );\r
3524   }\r
3525 }\r
3526 \r
3527 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3528 void fputDW(FILE *f, int x)\r
3529 {\r
3530         fputc(x     & 255, f);\r
3531         fputc(x>>8  & 255, f);\r
3532         fputc(x>>16 & 255, f);\r
3533         fputc(x>>24 & 255, f);\r
3534 }\r
3535 \r
3536 #define MAX_CLIPS 200   /* more than enough */\r
3537 \r
3538 VOID\r
3539 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3540 {\r
3541 //  HBITMAP bufferBitmap;\r
3542   BITMAP bi;\r
3543 //  RECT Rect;\r
3544   HDC tmphdc;\r
3545   HBITMAP hbm;\r
3546   int w = 100, h = 50;\r
3547 \r
3548   if(logo == NULL) {\r
3549     if(!logoHeight) return;\r
3550     FillRect( hdc, &logoRect, whitePieceBrush );\r
3551   }\r
3552 //  GetClientRect(hwndMain, &Rect);\r
3553 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3554 //                                      Rect.bottom-Rect.top+1);\r
3555   tmphdc = CreateCompatibleDC(hdc);\r
3556   hbm = SelectObject(tmphdc, logo);\r
3557   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3558             w = bi.bmWidth;\r
3559             h = bi.bmHeight;\r
3560   }\r
3561   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3562                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3563   SelectObject(tmphdc, hbm);\r
3564   DeleteDC(tmphdc);\r
3565 }\r
3566 \r
3567 VOID\r
3568 DisplayLogos()\r
3569 {\r
3570   if(logoHeight) {\r
3571         HDC hdc = GetDC(hwndMain);\r
3572         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3573         if(appData.autoLogo) {\r
3574           \r
3575           switch(gameMode) { // pick logos based on game mode\r
3576             case IcsObserving:\r
3577                 whiteLogo = second.programLogo; // ICS logo\r
3578                 blackLogo = second.programLogo;\r
3579             default:\r
3580                 break;\r
3581             case IcsPlayingWhite:\r
3582                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3583                 blackLogo = second.programLogo; // ICS logo\r
3584                 break;\r
3585             case IcsPlayingBlack:\r
3586                 whiteLogo = second.programLogo; // ICS logo\r
3587                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3588                 break;\r
3589             case TwoMachinesPlay:\r
3590                 if(first.twoMachinesColor[0] == 'b') {\r
3591                     whiteLogo = second.programLogo;\r
3592                     blackLogo = first.programLogo;\r
3593                 }\r
3594                 break;\r
3595             case MachinePlaysWhite:\r
3596                 blackLogo = userLogo;\r
3597                 break;\r
3598             case MachinePlaysBlack:\r
3599                 whiteLogo = userLogo;\r
3600                 blackLogo = first.programLogo;\r
3601           }\r
3602         }\r
3603         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3604         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3605         ReleaseDC(hwndMain, hdc);\r
3606   }\r
3607 }\r
3608 \r
3609 void\r
3610 UpdateLogos(int display)\r
3611 { // called after loading new engine(s), in tourney or from menu\r
3612   LoadLogo(&first, 0, FALSE);\r
3613   LoadLogo(&second, 1, appData.icsActive);\r
3614   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3615   if(display) DisplayLogos();\r
3616 }\r
3617 \r
3618 static HDC hdcSeek;\r
3619 \r
3620 // [HGM] seekgraph\r
3621 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3622 {\r
3623     POINT stPt;\r
3624     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3625     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3626     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3627     SelectObject( hdcSeek, hp );\r
3628 }\r
3629 \r
3630 // front-end wrapper for drawing functions to do rectangles\r
3631 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3632 {\r
3633     HPEN hp;\r
3634     RECT rc;\r
3635 \r
3636     if (hdcSeek == NULL) {\r
3637     hdcSeek = GetDC(hwndMain);\r
3638       if (!appData.monoMode) {\r
3639         SelectPalette(hdcSeek, hPal, FALSE);\r
3640         RealizePalette(hdcSeek);\r
3641       }\r
3642     }\r
3643     hp = SelectObject( hdcSeek, gridPen );\r
3644     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3645     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3646     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3647     SelectObject( hdcSeek, hp );\r
3648 }\r
3649 \r
3650 // front-end wrapper for putting text in graph\r
3651 void DrawSeekText(char *buf, int x, int y)\r
3652 {\r
3653         SIZE stSize;\r
3654         SetBkMode( hdcSeek, TRANSPARENT );\r
3655         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3656         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3657 }\r
3658 \r
3659 void DrawSeekDot(int x, int y, int color)\r
3660 {\r
3661         int square = color & 0x80;\r
3662         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3663                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3664         color &= 0x7F;\r
3665         if(square)\r
3666             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3667                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3668         else\r
3669             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3670                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3671             SelectObject(hdcSeek, oldBrush);\r
3672 }\r
3673 \r
3674 void DrawSeekOpen()\r
3675 {\r
3676 }\r
3677 \r
3678 void DrawSeekClose()\r
3679 {\r
3680 }\r
3681 \r
3682 VOID\r
3683 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3684 {\r
3685   static Board lastReq[2], lastDrawn[2];\r
3686   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3687   static int lastDrawnFlipView = 0;\r
3688   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3689   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3690   HDC tmphdc;\r
3691   HDC hdcmem;\r
3692   HBITMAP bufferBitmap;\r
3693   HBITMAP oldBitmap;\r
3694   RECT Rect;\r
3695   HRGN clips[MAX_CLIPS];\r
3696   ChessSquare dragged_piece = EmptySquare;\r
3697   int nr = twoBoards*partnerUp;\r
3698 \r
3699   /* I'm undecided on this - this function figures out whether a full\r
3700    * repaint is necessary on its own, so there's no real reason to have the\r
3701    * caller tell it that.  I think this can safely be set to FALSE - but\r
3702    * if we trust the callers not to request full repaints unnessesarily, then\r
3703    * we could skip some clipping work.  In other words, only request a full\r
3704    * redraw when the majority of pieces have changed positions (ie. flip, \r
3705    * gamestart and similar)  --Hawk\r
3706    */\r
3707   Boolean fullrepaint = repaint;\r
3708 \r
3709   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3710 \r
3711   if( DrawPositionNeedsFullRepaint() ) {\r
3712       fullrepaint = TRUE;\r
3713   }\r
3714 \r
3715   if (board == NULL) {\r
3716     if (!lastReqValid[nr]) {\r
3717       return;\r
3718     }\r
3719     board = lastReq[nr];\r
3720   } else {\r
3721     CopyBoard(lastReq[nr], board);\r
3722     lastReqValid[nr] = 1;\r
3723   }\r
3724 \r
3725   if (doingSizing) {\r
3726     return;\r
3727   }\r
3728 \r
3729   if (IsIconic(hwndMain)) {\r
3730     return;\r
3731   }\r
3732 \r
3733   if (hdc == NULL) {\r
3734     hdc = GetDC(hwndMain);\r
3735     if (!appData.monoMode) {\r
3736       SelectPalette(hdc, hPal, FALSE);\r
3737       RealizePalette(hdc);\r
3738     }\r
3739     releaseDC = TRUE;\r
3740   } else {\r
3741     releaseDC = FALSE;\r
3742   }\r
3743 \r
3744   /* Create some work-DCs */\r
3745   hdcmem = CreateCompatibleDC(hdc);\r
3746   tmphdc = CreateCompatibleDC(hdc);\r
3747 \r
3748   /* If dragging is in progress, we temporarely remove the piece */\r
3749   /* [HGM] or temporarily decrease count if stacked              */\r
3750   /*       !! Moved to before board compare !!                   */\r
3751   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3752     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3753     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3754             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3755         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3756     } else \r
3757     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3758             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3759         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3760     } else \r
3761         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3762   }\r
3763 \r
3764   /* Figure out which squares need updating by comparing the \r
3765    * newest board with the last drawn board and checking if\r
3766    * flipping has changed.\r
3767    */\r
3768   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3769     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3770       for (column = 0; column < BOARD_WIDTH; column++) {\r
3771         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3772           SquareToPos(row, column, &x, &y);\r
3773           clips[num_clips++] =\r
3774             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3775         }\r
3776       }\r
3777     }\r
3778    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3779     for (i=0; i<2; i++) {\r
3780       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3781           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3782         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3783             lastDrawnHighlight.sq[i].y >= 0) {\r
3784           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3785                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3786           clips[num_clips++] =\r
3787             CreateRectRgn(x - lineGap, y - lineGap, \r
3788                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3789         }\r
3790         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3791           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3792           clips[num_clips++] =\r
3793             CreateRectRgn(x - lineGap, y - lineGap, \r
3794                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3795         }\r
3796       }\r
3797     }\r
3798     for (i=0; i<2; i++) {\r
3799       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3800           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3801         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3802             lastDrawnPremove.sq[i].y >= 0) {\r
3803           SquareToPos(lastDrawnPremove.sq[i].y,\r
3804                       lastDrawnPremove.sq[i].x, &x, &y);\r
3805           clips[num_clips++] =\r
3806             CreateRectRgn(x - lineGap, y - lineGap, \r
3807                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3808         }\r
3809         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3810             premoveHighlightInfo.sq[i].y >= 0) {\r
3811           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3812                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3813           clips[num_clips++] =\r
3814             CreateRectRgn(x - lineGap, y - lineGap, \r
3815                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3816         }\r
3817       }\r
3818     }\r
3819    } else { // nr == 1\r
3820         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3821         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3822         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3823         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3824       for (i=0; i<2; i++) {\r
3825         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3826             partnerHighlightInfo.sq[i].y >= 0) {\r
3827           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3828                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3829           clips[num_clips++] =\r
3830             CreateRectRgn(x - lineGap, y - lineGap, \r
3831                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3832         }\r
3833         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3834             oldPartnerHighlight.sq[i].y >= 0) {\r
3835           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3836                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3837           clips[num_clips++] =\r
3838             CreateRectRgn(x - lineGap, y - lineGap, \r
3839                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3840         }\r
3841       }\r
3842    }\r
3843   } else {\r
3844     fullrepaint = TRUE;\r
3845   }\r
3846 \r
3847   /* Create a buffer bitmap - this is the actual bitmap\r
3848    * being written to.  When all the work is done, we can\r
3849    * copy it to the real DC (the screen).  This avoids\r
3850    * the problems with flickering.\r
3851    */\r
3852   GetClientRect(hwndMain, &Rect);\r
3853   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3854                                         Rect.bottom-Rect.top+1);\r
3855   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3856   if (!appData.monoMode) {\r
3857     SelectPalette(hdcmem, hPal, FALSE);\r
3858   }\r
3859 \r
3860   /* Create clips for dragging */\r
3861   if (!fullrepaint) {\r
3862     if (dragInfo.from.x >= 0) {\r
3863       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3864       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3865     }\r
3866     if (dragInfo.start.x >= 0) {\r
3867       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3868       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3869     }\r
3870     if (dragInfo.pos.x >= 0) {\r
3871       x = dragInfo.pos.x - squareSize / 2;\r
3872       y = dragInfo.pos.y - squareSize / 2;\r
3873       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3874     }\r
3875     if (dragInfo.lastpos.x >= 0) {\r
3876       x = dragInfo.lastpos.x - squareSize / 2;\r
3877       y = dragInfo.lastpos.y - squareSize / 2;\r
3878       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3879     }\r
3880   }\r
3881 \r
3882   /* Are we animating a move?  \r
3883    * If so, \r
3884    *   - remove the piece from the board (temporarely)\r
3885    *   - calculate the clipping region\r
3886    */\r
3887   if (!fullrepaint) {\r
3888     if (animInfo.piece != EmptySquare) {\r
3889       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3890       x = boardRect.left + animInfo.lastpos.x;\r
3891       y = boardRect.top + animInfo.lastpos.y;\r
3892       x2 = boardRect.left + animInfo.pos.x;\r
3893       y2 = boardRect.top + animInfo.pos.y;\r
3894       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3895       /* Slight kludge.  The real problem is that after AnimateMove is\r
3896          done, the position on the screen does not match lastDrawn.\r
3897          This currently causes trouble only on e.p. captures in\r
3898          atomic, where the piece moves to an empty square and then\r
3899          explodes.  The old and new positions both had an empty square\r
3900          at the destination, but animation has drawn a piece there and\r
3901          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3902       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3903     }\r
3904   }\r
3905 \r
3906   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3907   if (num_clips == 0)\r
3908     fullrepaint = TRUE;\r
3909 \r
3910   /* Set clipping on the memory DC */\r
3911   if (!fullrepaint) {\r
3912     SelectClipRgn(hdcmem, clips[0]);\r
3913     for (x = 1; x < num_clips; x++) {\r
3914       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3915         abort();  // this should never ever happen!\r
3916     }\r
3917   }\r
3918 \r
3919   /* Do all the drawing to the memory DC */\r
3920   if(explodeInfo.radius) { // [HGM] atomic\r
3921         HBRUSH oldBrush;\r
3922         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3923         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3924         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3925         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3926         x += squareSize/2;\r
3927         y += squareSize/2;\r
3928         if(!fullrepaint) {\r
3929           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3930           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3931         }\r
3932         DrawGridOnDC(hdcmem);\r
3933         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3934         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3935         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3936         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3937         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3938         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3939         SelectObject(hdcmem, oldBrush);\r
3940   } else {\r
3941     if(border) DrawBackgroundOnDC(hdcmem);\r
3942     DrawGridOnDC(hdcmem);\r
3943     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3944         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3945         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3946     } else {\r
3947         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3948         oldPartnerHighlight = partnerHighlightInfo;\r
3949     }\r
3950     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3951   }\r
3952   if(nr == 0) // [HGM] dual: markers only on left board\r
3953   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3954     for (column = 0; column < BOARD_WIDTH; column++) {\r
3955         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3956             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3957             SquareToPos(row, column, &x, &y);\r
3958             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3959                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3960             SelectObject(hdcmem, oldBrush);\r
3961         }\r
3962     }\r
3963   }\r
3964 \r
3965   if( appData.highlightMoveWithArrow ) {\r
3966     DrawArrowHighlight(hdcmem);\r
3967   }\r
3968 \r
3969   DrawCoordsOnDC(hdcmem);\r
3970 \r
3971   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3972                  /* to make sure lastDrawn contains what is actually drawn */\r
3973 \r
3974   /* Put the dragged piece back into place and draw it (out of place!) */\r
3975     if (dragged_piece != EmptySquare) {\r
3976     /* [HGM] or restack */\r
3977     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3978                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3979     else\r
3980     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3981                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3982 \r
3983     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3984     x = dragInfo.pos.x - squareSize / 2;\r
3985     y = dragInfo.pos.y - squareSize / 2;\r
3986     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3987                   ((int) dragInfo.piece < (int) BlackPawn), \r
3988                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3989   }   \r
3990   \r
3991   /* Put the animated piece back into place and draw it */\r
3992   if (animInfo.piece != EmptySquare) {\r
3993     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3994     x = boardRect.left + animInfo.pos.x;\r
3995     y = boardRect.top + animInfo.pos.y;\r
3996     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3997                   ((int) animInfo.piece < (int) BlackPawn),\r
3998                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3999   }\r
4000 \r
4001   /* Release the bufferBitmap by selecting in the old bitmap \r
4002    * and delete the memory DC\r
4003    */\r
4004   SelectObject(hdcmem, oldBitmap);\r
4005   DeleteDC(hdcmem);\r
4006 \r
4007   /* Set clipping on the target DC */\r
4008   if (!fullrepaint) {\r
4009     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4010         RECT rect;\r
4011         GetRgnBox(clips[x], &rect);\r
4012         DeleteObject(clips[x]);\r
4013         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4014                           rect.right + wpMain.width/2, rect.bottom);\r
4015     }\r
4016     SelectClipRgn(hdc, clips[0]);\r
4017     for (x = 1; x < num_clips; x++) {\r
4018       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4019         abort();   // this should never ever happen!\r
4020     } \r
4021   }\r
4022 \r
4023   /* Copy the new bitmap onto the screen in one go.\r
4024    * This way we avoid any flickering\r
4025    */\r
4026   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4027   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4028          boardRect.right - boardRect.left,\r
4029          boardRect.bottom - boardRect.top,\r
4030          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4031   if(saveDiagFlag) { \r
4032     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4033     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4034 \r
4035     GetObject(bufferBitmap, sizeof(b), &b);\r
4036     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4037         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4038         bih.biWidth = b.bmWidth;\r
4039         bih.biHeight = b.bmHeight;\r
4040         bih.biPlanes = 1;\r
4041         bih.biBitCount = b.bmBitsPixel;\r
4042         bih.biCompression = 0;\r
4043         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4044         bih.biXPelsPerMeter = 0;\r
4045         bih.biYPelsPerMeter = 0;\r
4046         bih.biClrUsed = 0;\r
4047         bih.biClrImportant = 0;\r
4048 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4049 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4050         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4051 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4052 \r
4053         wb = b.bmWidthBytes;\r
4054         // count colors\r
4055         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4056                 int k = ((int*) pData)[i];\r
4057                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4058                 if(j >= 16) break;\r
4059                 color[j] = k;\r
4060                 if(j >= nrColors) nrColors = j+1;\r
4061         }\r
4062         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4063                 INT p = 0;\r
4064                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4065                     for(w=0; w<(wb>>2); w+=2) {\r
4066                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4067                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4068                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4069                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4070                         pData[p++] = m | j<<4;\r
4071                     }\r
4072                     while(p&3) pData[p++] = 0;\r
4073                 }\r
4074                 fac = 3;\r
4075                 wb = ((wb+31)>>5)<<2;\r
4076         }\r
4077         // write BITMAPFILEHEADER\r
4078         fprintf(diagFile, "BM");\r
4079         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4080         fputDW(diagFile, 0);\r
4081         fputDW(diagFile, 0x36 + (fac?64:0));\r
4082         // write BITMAPINFOHEADER\r
4083         fputDW(diagFile, 40);\r
4084         fputDW(diagFile, b.bmWidth);\r
4085         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4086         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4087         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4088         fputDW(diagFile, 0);\r
4089         fputDW(diagFile, 0);\r
4090         fputDW(diagFile, 0);\r
4091         fputDW(diagFile, 0);\r
4092         fputDW(diagFile, 0);\r
4093         fputDW(diagFile, 0);\r
4094         // write color table\r
4095         if(fac)\r
4096         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4097         // write bitmap data\r
4098         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4099                 fputc(pData[i], diagFile);\r
4100         free(pData);\r
4101      }\r
4102   }\r
4103 \r
4104   SelectObject(tmphdc, oldBitmap);\r
4105 \r
4106   /* Massive cleanup */\r
4107   for (x = 0; x < num_clips; x++)\r
4108     DeleteObject(clips[x]);\r
4109 \r
4110   DeleteDC(tmphdc);\r
4111   DeleteObject(bufferBitmap);\r
4112 \r
4113   if (releaseDC) \r
4114     ReleaseDC(hwndMain, hdc);\r
4115   \r
4116   if (lastDrawnFlipView != flipView && nr == 0) {\r
4117     if (flipView)\r
4118       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4119     else\r
4120       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4121   }\r
4122 \r
4123 /*  CopyBoard(lastDrawn, board);*/\r
4124   lastDrawnHighlight = highlightInfo;\r
4125   lastDrawnPremove   = premoveHighlightInfo;\r
4126   lastDrawnFlipView = flipView;\r
4127   lastDrawnValid[nr] = 1;\r
4128 }\r
4129 \r
4130 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4131 int\r
4132 SaveDiagram(f)\r
4133      FILE *f;\r
4134 {\r
4135     saveDiagFlag = 1; diagFile = f;\r
4136     HDCDrawPosition(NULL, TRUE, NULL);\r
4137     saveDiagFlag = 0;\r
4138 \r
4139     fclose(f);\r
4140     return TRUE;\r
4141 }\r
4142 \r
4143 \r
4144 /*---------------------------------------------------------------------------*\\r
4145 | CLIENT PAINT PROCEDURE\r
4146 |   This is the main event-handler for the WM_PAINT message.\r
4147 |\r
4148 \*---------------------------------------------------------------------------*/\r
4149 VOID\r
4150 PaintProc(HWND hwnd)\r
4151 {\r
4152   HDC         hdc;\r
4153   PAINTSTRUCT ps;\r
4154   HFONT       oldFont;\r
4155 \r
4156   if((hdc = BeginPaint(hwnd, &ps))) {\r
4157     if (IsIconic(hwnd)) {\r
4158       DrawIcon(hdc, 2, 2, iconCurrent);\r
4159     } else {\r
4160       if (!appData.monoMode) {\r
4161         SelectPalette(hdc, hPal, FALSE);\r
4162         RealizePalette(hdc);\r
4163       }\r
4164       HDCDrawPosition(hdc, 1, NULL);\r
4165       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4166         flipView = !flipView; partnerUp = !partnerUp;\r
4167         HDCDrawPosition(hdc, 1, NULL);\r
4168         flipView = !flipView; partnerUp = !partnerUp;\r
4169       }\r
4170       oldFont =\r
4171         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4172       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4173                  ETO_CLIPPED|ETO_OPAQUE,\r
4174                  &messageRect, messageText, strlen(messageText), NULL);\r
4175       SelectObject(hdc, oldFont);\r
4176       DisplayBothClocks();\r
4177       DisplayLogos();\r
4178     }\r
4179     EndPaint(hwnd,&ps);\r
4180   }\r
4181 \r
4182   return;\r
4183 }\r
4184 \r
4185 \r
4186 /*\r
4187  * If the user selects on a border boundary, return -1; if off the board,\r
4188  *   return -2.  Otherwise map the event coordinate to the square.\r
4189  * The offset boardRect.left or boardRect.top must already have been\r
4190  *   subtracted from x.\r
4191  */\r
4192 int EventToSquare(x, limit)\r
4193      int x, limit;\r
4194 {\r
4195   if (x <= border)\r
4196     return -2;\r
4197   if (x < lineGap + border)\r
4198     return -1;\r
4199   x -= lineGap + border;\r
4200   if ((x % (squareSize + lineGap)) >= squareSize)\r
4201     return -1;\r
4202   x /= (squareSize + lineGap);\r
4203     if (x >= limit)\r
4204     return -2;\r
4205   return x;\r
4206 }\r
4207 \r
4208 typedef struct {\r
4209   char piece;\r
4210   int command;\r
4211   char* name;\r
4212 } DropEnable;\r
4213 \r
4214 DropEnable dropEnables[] = {\r
4215   { 'P', DP_Pawn, N_("Pawn") },\r
4216   { 'N', DP_Knight, N_("Knight") },\r
4217   { 'B', DP_Bishop, N_("Bishop") },\r
4218   { 'R', DP_Rook, N_("Rook") },\r
4219   { 'Q', DP_Queen, N_("Queen") },\r
4220 };\r
4221 \r
4222 VOID\r
4223 SetupDropMenu(HMENU hmenu)\r
4224 {\r
4225   int i, count, enable;\r
4226   char *p;\r
4227   extern char white_holding[], black_holding[];\r
4228   char item[MSG_SIZ];\r
4229 \r
4230   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4231     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4232                dropEnables[i].piece);\r
4233     count = 0;\r
4234     while (p && *p++ == dropEnables[i].piece) count++;\r
4235       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4236     enable = count > 0 || !appData.testLegality\r
4237       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4238                       && !appData.icsActive);\r
4239     ModifyMenu(hmenu, dropEnables[i].command,\r
4240                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4241                dropEnables[i].command, item);\r
4242   }\r
4243 }\r
4244 \r
4245 void DragPieceBegin(int x, int y, Boolean instantly)\r
4246 {\r
4247       dragInfo.lastpos.x = boardRect.left + x;\r
4248       dragInfo.lastpos.y = boardRect.top + y;\r
4249       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4250       dragInfo.from.x = fromX;\r
4251       dragInfo.from.y = fromY;\r
4252       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4253       dragInfo.start = dragInfo.from;\r
4254       SetCapture(hwndMain);\r
4255 }\r
4256 \r
4257 void DragPieceEnd(int x, int y)\r
4258 {\r
4259     ReleaseCapture();\r
4260     dragInfo.start.x = dragInfo.start.y = -1;\r
4261     dragInfo.from = dragInfo.start;\r
4262     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4263 }\r
4264 \r
4265 void ChangeDragPiece(ChessSquare piece)\r
4266 {\r
4267     dragInfo.piece = piece;\r
4268 }\r
4269 \r
4270 /* Event handler for mouse messages */\r
4271 VOID\r
4272 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4273 {\r
4274   int x, y, menuNr;\r
4275   POINT pt;\r
4276   static int recursive = 0;\r
4277   HMENU hmenu;\r
4278   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4279 \r
4280   if (recursive) {\r
4281     if (message == WM_MBUTTONUP) {\r
4282       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4283          to the middle button: we simulate pressing the left button too!\r
4284          */\r
4285       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4286       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4287     }\r
4288     return;\r
4289   }\r
4290   recursive++;\r
4291   \r
4292   pt.x = LOWORD(lParam);\r
4293   pt.y = HIWORD(lParam);\r
4294   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4295   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4296   if (!flipView && y >= 0) {\r
4297     y = BOARD_HEIGHT - 1 - y;\r
4298   }\r
4299   if (flipView && x >= 0) {\r
4300     x = BOARD_WIDTH - 1 - x;\r
4301   }\r
4302 \r
4303   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4304   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4305 \r
4306   switch (message) {\r
4307   case WM_LBUTTONDOWN:\r
4308       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4309         ClockClick(flipClock); break;\r
4310       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4311         ClockClick(!flipClock); break;\r
4312       }\r
4313     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4314       dragInfo.start.x = dragInfo.start.y = -1;\r
4315       dragInfo.from = dragInfo.start;\r
4316     }\r
4317     if(fromX == -1 && frozen) { // not sure where this is for\r
4318                 fromX = fromY = -1; \r
4319       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4320       break;\r
4321     }\r
4322       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4323       DrawPosition(TRUE, NULL);\r
4324     break;\r
4325 \r
4326   case WM_LBUTTONUP:\r
4327       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4328       DrawPosition(TRUE, NULL);\r
4329     break;\r
4330 \r
4331   case WM_MOUSEMOVE:\r
4332     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4333     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4334     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4335     if ((appData.animateDragging || appData.highlightDragging)\r
4336         && (wParam & MK_LBUTTON || dragging == 2)\r
4337         && dragInfo.from.x >= 0) \r
4338     {\r
4339       BOOL full_repaint = FALSE;\r
4340 \r
4341       if (appData.animateDragging) {\r
4342         dragInfo.pos = pt;\r
4343       }\r
4344       if (appData.highlightDragging) {\r
4345         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4346         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4347             full_repaint = TRUE;\r
4348         }\r
4349       }\r
4350       \r
4351       DrawPosition( full_repaint, NULL);\r
4352       \r
4353       dragInfo.lastpos = dragInfo.pos;\r
4354     }\r
4355     break;\r
4356 \r
4357   case WM_MOUSEWHEEL: // [DM]\r
4358     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4359        /* Mouse Wheel is being rolled forward\r
4360         * Play moves forward\r
4361         */\r
4362        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4363                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4364        /* Mouse Wheel is being rolled backward\r
4365         * Play moves backward\r
4366         */\r
4367        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4368                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4369     }\r
4370     break;\r
4371 \r
4372   case WM_MBUTTONUP:\r
4373   case WM_RBUTTONUP:\r
4374     ReleaseCapture();\r
4375     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4376     break;\r
4377  \r
4378   case WM_MBUTTONDOWN:\r
4379   case WM_RBUTTONDOWN:\r
4380     ErrorPopDown();\r
4381     ReleaseCapture();\r
4382     fromX = fromY = -1;\r
4383     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4384     dragInfo.start.x = dragInfo.start.y = -1;\r
4385     dragInfo.from = dragInfo.start;\r
4386     dragInfo.lastpos = dragInfo.pos;\r
4387     if (appData.highlightDragging) {\r
4388       ClearHighlights();\r
4389     }\r
4390     if(y == -2) {\r
4391       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4392       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4393           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4394       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4395           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4396       }\r
4397       break;\r
4398     }\r
4399     DrawPosition(TRUE, NULL);\r
4400 \r
4401     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4402     switch (menuNr) {\r
4403     case 0:\r
4404       if (message == WM_MBUTTONDOWN) {\r
4405         buttonCount = 3;  /* even if system didn't think so */\r
4406         if (wParam & MK_SHIFT) \r
4407           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4408         else\r
4409           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4410       } else { /* message == WM_RBUTTONDOWN */\r
4411         /* Just have one menu, on the right button.  Windows users don't\r
4412            think to try the middle one, and sometimes other software steals\r
4413            it, or it doesn't really exist. */\r
4414         if(gameInfo.variant != VariantShogi)\r
4415             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4416         else\r
4417             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4418       }\r
4419       break;\r
4420     case 2:\r
4421       SetCapture(hwndMain);\r
4422       break;\r
4423     case 1:\r
4424       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4425       SetupDropMenu(hmenu);\r
4426       MenuPopup(hwnd, pt, hmenu, -1);\r
4427     default:\r
4428       break;\r
4429     }\r
4430     break;\r
4431   }\r
4432 \r
4433   recursive--;\r
4434 }\r
4435 \r
4436 /* Preprocess messages for buttons in main window */\r
4437 LRESULT CALLBACK\r
4438 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4439 {\r
4440   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4441   int i, dir;\r
4442 \r
4443   for (i=0; i<N_BUTTONS; i++) {\r
4444     if (buttonDesc[i].id == id) break;\r
4445   }\r
4446   if (i == N_BUTTONS) return 0;\r
4447   switch (message) {\r
4448   case WM_KEYDOWN:\r
4449     switch (wParam) {\r
4450     case VK_LEFT:\r
4451     case VK_RIGHT:\r
4452       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4453       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4454       return TRUE;\r
4455     }\r
4456     break;\r
4457   case WM_CHAR:\r
4458     switch (wParam) {\r
4459     case '\r':\r
4460       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4461       return TRUE;\r
4462     default:\r
4463       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4464         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4465         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4466         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4467         SetFocus(h);\r
4468         SendMessage(h, WM_CHAR, wParam, lParam);\r
4469         return TRUE;\r
4470       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4471         TypeInEvent((char)wParam);\r
4472       }\r
4473       break;\r
4474     }\r
4475     break;\r
4476   }\r
4477   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4478 }\r
4479 \r
4480 static int promoStyle;\r
4481 \r
4482 /* Process messages for Promotion dialog box */\r
4483 LRESULT CALLBACK\r
4484 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4485 {\r
4486   char promoChar;\r
4487 \r
4488   switch (message) {\r
4489   case WM_INITDIALOG: /* message: initialize dialog box */\r
4490     /* Center the dialog over the application window */\r
4491     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4492     Translate(hDlg, DLG_PromotionKing);\r
4493     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4494       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4495        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4496        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4497                SW_SHOW : SW_HIDE);\r
4498     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4499     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4500        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4501          PieceToChar(WhiteAngel) != '~') ||\r
4502         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4503          PieceToChar(BlackAngel) != '~')   ) ?\r
4504                SW_SHOW : SW_HIDE);\r
4505     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4506        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4507          PieceToChar(WhiteMarshall) != '~') ||\r
4508         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4509          PieceToChar(BlackMarshall) != '~')   ) ?\r
4510                SW_SHOW : SW_HIDE);\r
4511     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4512     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4513     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4514     if(promoStyle) {\r
4515         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4516         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4517         SetWindowText(hDlg, "Promote?");\r
4518     }\r
4519     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4520        gameInfo.variant == VariantSuper ?\r
4521                SW_SHOW : SW_HIDE);\r
4522     return TRUE;\r
4523 \r
4524   case WM_COMMAND: /* message: received a command */\r
4525     switch (LOWORD(wParam)) {\r
4526     case IDCANCEL:\r
4527       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4528       ClearHighlights();\r
4529       DrawPosition(FALSE, NULL);\r
4530       return TRUE;\r
4531     case PB_King:\r
4532       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4533       break;\r
4534     case PB_Queen:\r
4535       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4536       break;\r
4537     case PB_Rook:\r
4538       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4539       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4540       break;\r
4541     case PB_Bishop:\r
4542       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4543       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4544       break;\r
4545     case PB_Chancellor:\r
4546       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4547       break;\r
4548     case PB_Archbishop:\r
4549       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4550       break;\r
4551     case PB_Knight:\r
4552       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4553                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4554       break;\r
4555     default:\r
4556       return FALSE;\r
4557     }\r
4558     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4559     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4560     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4561     fromX = fromY = -1;\r
4562     if (!appData.highlightLastMove) {\r
4563       ClearHighlights();\r
4564       DrawPosition(FALSE, NULL);\r
4565     }\r
4566     return TRUE;\r
4567   }\r
4568   return FALSE;\r
4569 }\r
4570 \r
4571 /* Pop up promotion dialog */\r
4572 VOID\r
4573 PromotionPopup(HWND hwnd)\r
4574 {\r
4575   FARPROC lpProc;\r
4576 \r
4577   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4578   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4579     hwnd, (DLGPROC)lpProc);\r
4580   FreeProcInstance(lpProc);\r
4581 }\r
4582 \r
4583 void\r
4584 PromotionPopUp(char choice)\r
4585 {\r
4586   promoStyle = (choice == '+');\r
4587   DrawPosition(TRUE, NULL);\r
4588   PromotionPopup(hwndMain);\r
4589 }\r
4590 \r
4591 VOID\r
4592 LoadGameDialog(HWND hwnd, char* title)\r
4593 {\r
4594   UINT number = 0;\r
4595   FILE *f;\r
4596   char fileTitle[MSG_SIZ];\r
4597   f = OpenFileDialog(hwnd, "rb", "",\r
4598                      appData.oldSaveStyle ? "gam" : "pgn",\r
4599                      GAME_FILT,\r
4600                      title, &number, fileTitle, NULL);\r
4601   if (f != NULL) {\r
4602     cmailMsgLoaded = FALSE;\r
4603     if (number == 0) {\r
4604       int error = GameListBuild(f);\r
4605       if (error) {\r
4606         DisplayError(_("Cannot build game list"), error);\r
4607       } else if (!ListEmpty(&gameList) &&\r
4608                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4609         GameListPopUp(f, fileTitle);\r
4610         return;\r
4611       }\r
4612       GameListDestroy();\r
4613       number = 1;\r
4614     }\r
4615     LoadGame(f, number, fileTitle, FALSE);\r
4616   }\r
4617 }\r
4618 \r
4619 int get_term_width()\r
4620 {\r
4621     HDC hdc;\r
4622     TEXTMETRIC tm;\r
4623     RECT rc;\r
4624     HFONT hfont, hold_font;\r
4625     LOGFONT lf;\r
4626     HWND hText;\r
4627 \r
4628     if (hwndConsole)\r
4629         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4630     else\r
4631         return 79;\r
4632 \r
4633     // get the text metrics\r
4634     hdc = GetDC(hText);\r
4635     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4636     if (consoleCF.dwEffects & CFE_BOLD)\r
4637         lf.lfWeight = FW_BOLD;\r
4638     if (consoleCF.dwEffects & CFE_ITALIC)\r
4639         lf.lfItalic = TRUE;\r
4640     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4641         lf.lfStrikeOut = TRUE;\r
4642     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4643         lf.lfUnderline = TRUE;\r
4644     hfont = CreateFontIndirect(&lf);\r
4645     hold_font = SelectObject(hdc, hfont);\r
4646     GetTextMetrics(hdc, &tm);\r
4647     SelectObject(hdc, hold_font);\r
4648     DeleteObject(hfont);\r
4649     ReleaseDC(hText, hdc);\r
4650 \r
4651     // get the rectangle\r
4652     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4653 \r
4654     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4655 }\r
4656 \r
4657 void UpdateICSWidth(HWND hText)\r
4658 {\r
4659     LONG old_width, new_width;\r
4660 \r
4661     new_width = get_term_width(hText, FALSE);\r
4662     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4663     if (new_width != old_width)\r
4664     {\r
4665         ics_update_width(new_width);\r
4666         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4667     }\r
4668 }\r
4669 \r
4670 VOID\r
4671 ChangedConsoleFont()\r
4672 {\r
4673   CHARFORMAT cfmt;\r
4674   CHARRANGE tmpsel, sel;\r
4675   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4676   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4677   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4678   PARAFORMAT paraf;\r
4679 \r
4680   cfmt.cbSize = sizeof(CHARFORMAT);\r
4681   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4682     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4683                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4684   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4685    * size.  This was undocumented in the version of MSVC++ that I had\r
4686    * when I wrote the code, but is apparently documented now.\r
4687    */\r
4688   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4689   cfmt.bCharSet = f->lf.lfCharSet;\r
4690   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4691   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4692   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4693   /* Why are the following seemingly needed too? */\r
4694   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4695   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4696   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4697   tmpsel.cpMin = 0;\r
4698   tmpsel.cpMax = -1; /*999999?*/\r
4699   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4700   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4701   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4702    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4703    */\r
4704   paraf.cbSize = sizeof(paraf);\r
4705   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4706   paraf.dxStartIndent = 0;\r
4707   paraf.dxOffset = WRAP_INDENT;\r
4708   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4709   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4710   UpdateICSWidth(hText);\r
4711 }\r
4712 \r
4713 /*---------------------------------------------------------------------------*\\r
4714  *\r
4715  * Window Proc for main window\r
4716  *\r
4717 \*---------------------------------------------------------------------------*/\r
4718 \r
4719 /* Process messages for main window, etc. */\r
4720 LRESULT CALLBACK\r
4721 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4722 {\r
4723   FARPROC lpProc;\r
4724   int wmId;\r
4725   char *defName;\r
4726   FILE *f;\r
4727   UINT number;\r
4728   char fileTitle[MSG_SIZ];\r
4729   static SnapData sd;\r
4730   static int peek=0;\r
4731 \r
4732   switch (message) {\r
4733 \r
4734   case WM_PAINT: /* message: repaint portion of window */\r
4735     PaintProc(hwnd);\r
4736     break;\r
4737 \r
4738   case WM_ERASEBKGND:\r
4739     if (IsIconic(hwnd)) {\r
4740       /* Cheat; change the message */\r
4741       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4742     } else {\r
4743       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4744     }\r
4745     break;\r
4746 \r
4747   case WM_LBUTTONDOWN:\r
4748   case WM_MBUTTONDOWN:\r
4749   case WM_RBUTTONDOWN:\r
4750   case WM_LBUTTONUP:\r
4751   case WM_MBUTTONUP:\r
4752   case WM_RBUTTONUP:\r
4753   case WM_MOUSEMOVE:\r
4754   case WM_MOUSEWHEEL:\r
4755     MouseEvent(hwnd, message, wParam, lParam);\r
4756     break;\r
4757 \r
4758   case WM_KEYUP:\r
4759     if((char)wParam == '\b') {\r
4760       ForwardEvent(); peek = 0;\r
4761     }\r
4762 \r
4763     JAWS_KBUP_NAVIGATION\r
4764 \r
4765     break;\r
4766 \r
4767   case WM_KEYDOWN:\r
4768     if((char)wParam == '\b') {\r
4769       if(!peek) BackwardEvent(), peek = 1;\r
4770     }\r
4771 \r
4772     JAWS_KBDOWN_NAVIGATION\r
4773 \r
4774     break;\r
4775 \r
4776   case WM_CHAR:\r
4777     \r
4778     JAWS_ALT_INTERCEPT\r
4779 \r
4780     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4781         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4782         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4783         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4784         SetFocus(h);\r
4785         SendMessage(h, message, wParam, lParam);\r
4786     } else if(lParam != KF_REPEAT) {\r
4787         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4788                 TypeInEvent((char)wParam);\r
4789         } else if((char)wParam == 003) CopyGameToClipboard();\r
4790          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4791     }\r
4792 \r
4793     break;\r
4794 \r
4795   case WM_PALETTECHANGED:\r
4796     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4797       int nnew;\r
4798       HDC hdc = GetDC(hwndMain);\r
4799       SelectPalette(hdc, hPal, TRUE);\r
4800       nnew = RealizePalette(hdc);\r
4801       if (nnew > 0) {\r
4802         paletteChanged = TRUE;\r
4803 \r
4804         InvalidateRect(hwnd, &boardRect, FALSE);\r
4805       }\r
4806       ReleaseDC(hwnd, hdc);\r
4807     }\r
4808     break;\r
4809 \r
4810   case WM_QUERYNEWPALETTE:\r
4811     if (!appData.monoMode /*&& paletteChanged*/) {\r
4812       int nnew;\r
4813       HDC hdc = GetDC(hwndMain);\r
4814       paletteChanged = FALSE;\r
4815       SelectPalette(hdc, hPal, FALSE);\r
4816       nnew = RealizePalette(hdc);\r
4817       if (nnew > 0) {\r
4818         InvalidateRect(hwnd, &boardRect, FALSE);\r
4819       }\r
4820       ReleaseDC(hwnd, hdc);\r
4821       return TRUE;\r
4822     }\r
4823     return FALSE;\r
4824 \r
4825   case WM_COMMAND: /* message: command from application menu */\r
4826     wmId    = LOWORD(wParam);\r
4827 \r
4828     switch (wmId) {\r
4829     case IDM_NewGame:\r
4830       ResetGameEvent();\r
4831       SAY("new game enter a move to play against the computer with white");\r
4832       break;\r
4833 \r
4834     case IDM_NewGameFRC:\r
4835       if( NewGameFRC() == 0 ) {\r
4836         ResetGameEvent();\r
4837       }\r
4838       break;\r
4839 \r
4840     case IDM_NewVariant:\r
4841       NewVariantPopup(hwnd);\r
4842       break;\r
4843 \r
4844     case IDM_LoadGame:\r
4845       LoadGameDialog(hwnd, _("Load Game from File"));\r
4846       break;\r
4847 \r
4848     case IDM_LoadNextGame:\r
4849       ReloadGame(1);\r
4850       break;\r
4851 \r
4852     case IDM_LoadPrevGame:\r
4853       ReloadGame(-1);\r
4854       break;\r
4855 \r
4856     case IDM_ReloadGame:\r
4857       ReloadGame(0);\r
4858       break;\r
4859 \r
4860     case IDM_LoadPosition:\r
4861       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4862         Reset(FALSE, TRUE);\r
4863       }\r
4864       number = 1;\r
4865       f = OpenFileDialog(hwnd, "rb", "",\r
4866                          appData.oldSaveStyle ? "pos" : "fen",\r
4867                          POSITION_FILT,\r
4868                          _("Load Position from File"), &number, fileTitle, NULL);\r
4869       if (f != NULL) {\r
4870         LoadPosition(f, number, fileTitle);\r
4871       }\r
4872       break;\r
4873 \r
4874     case IDM_LoadNextPosition:\r
4875       ReloadPosition(1);\r
4876       break;\r
4877 \r
4878     case IDM_LoadPrevPosition:\r
4879       ReloadPosition(-1);\r
4880       break;\r
4881 \r
4882     case IDM_ReloadPosition:\r
4883       ReloadPosition(0);\r
4884       break;\r
4885 \r
4886     case IDM_SaveGame:\r
4887       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4888       f = OpenFileDialog(hwnd, "a", defName,\r
4889                          appData.oldSaveStyle ? "gam" : "pgn",\r
4890                          GAME_FILT,\r
4891                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4892       if (f != NULL) {\r
4893         SaveGame(f, 0, "");\r
4894       }\r
4895       break;\r
4896 \r
4897     case IDM_SavePosition:\r
4898       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4899       f = OpenFileDialog(hwnd, "a", defName,\r
4900                          appData.oldSaveStyle ? "pos" : "fen",\r
4901                          POSITION_FILT,\r
4902                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4903       if (f != NULL) {\r
4904         SavePosition(f, 0, "");\r
4905       }\r
4906       break;\r
4907 \r
4908     case IDM_SaveDiagram:\r
4909       defName = "diagram";\r
4910       f = OpenFileDialog(hwnd, "wb", defName,\r
4911                          "bmp",\r
4912                          DIAGRAM_FILT,\r
4913                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4914       if (f != NULL) {\r
4915         SaveDiagram(f);\r
4916       }\r
4917       break;\r
4918 \r
4919     case IDM_CreateBook:\r
4920       CreateBookEvent();\r
4921       break;\r
4922 \r
4923     case IDM_CopyGame:\r
4924       CopyGameToClipboard();\r
4925       break;\r
4926 \r
4927     case IDM_PasteGame:\r
4928       PasteGameFromClipboard();\r
4929       break;\r
4930 \r
4931     case IDM_CopyGameListToClipboard:\r
4932       CopyGameListToClipboard();\r
4933       break;\r
4934 \r
4935     /* [AS] Autodetect FEN or PGN data */\r
4936     case IDM_PasteAny:\r
4937       PasteGameOrFENFromClipboard();\r
4938       break;\r
4939 \r
4940     /* [AS] Move history */\r
4941     case IDM_ShowMoveHistory:\r
4942         if( MoveHistoryIsUp() ) {\r
4943             MoveHistoryPopDown();\r
4944         }\r
4945         else {\r
4946             MoveHistoryPopUp();\r
4947         }\r
4948         break;\r
4949 \r
4950     /* [AS] Eval graph */\r
4951     case IDM_ShowEvalGraph:\r
4952         if( EvalGraphIsUp() ) {\r
4953             EvalGraphPopDown();\r
4954         }\r
4955         else {\r
4956             EvalGraphPopUp();\r
4957             SetFocus(hwndMain);\r
4958         }\r
4959         break;\r
4960 \r
4961     /* [AS] Engine output */\r
4962     case IDM_ShowEngineOutput:\r
4963         if( EngineOutputIsUp() ) {\r
4964             EngineOutputPopDown();\r
4965         }\r
4966         else {\r
4967             EngineOutputPopUp();\r
4968         }\r
4969         break;\r
4970 \r
4971     /* [AS] User adjudication */\r
4972     case IDM_UserAdjudication_White:\r
4973         UserAdjudicationEvent( +1 );\r
4974         break;\r
4975 \r
4976     case IDM_UserAdjudication_Black:\r
4977         UserAdjudicationEvent( -1 );\r
4978         break;\r
4979 \r
4980     case IDM_UserAdjudication_Draw:\r
4981         UserAdjudicationEvent( 0 );\r
4982         break;\r
4983 \r
4984     /* [AS] Game list options dialog */\r
4985     case IDM_GameListOptions:\r
4986       GameListOptions();\r
4987       break;\r
4988 \r
4989     case IDM_NewChat:\r
4990       ChatPopUp(NULL);\r
4991       break;\r
4992 \r
4993     case IDM_CopyPosition:\r
4994       CopyFENToClipboard();\r
4995       break;\r
4996 \r
4997     case IDM_PastePosition:\r
4998       PasteFENFromClipboard();\r
4999       break;\r
5000 \r
5001     case IDM_MailMove:\r
5002       MailMoveEvent();\r
5003       break;\r
5004 \r
5005     case IDM_ReloadCMailMsg:\r
5006       Reset(TRUE, TRUE);\r
5007       ReloadCmailMsgEvent(FALSE);\r
5008       break;\r
5009 \r
5010     case IDM_Minimize:\r
5011       ShowWindow(hwnd, SW_MINIMIZE);\r
5012       break;\r
5013 \r
5014     case IDM_Exit:\r
5015       ExitEvent(0);\r
5016       break;\r
5017 \r
5018     case IDM_MachineWhite:\r
5019       MachineWhiteEvent();\r
5020       /*\r
5021        * refresh the tags dialog only if it's visible\r
5022        */\r
5023       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5024           char *tags;\r
5025           tags = PGNTags(&gameInfo);\r
5026           TagsPopUp(tags, CmailMsg());\r
5027           free(tags);\r
5028       }\r
5029       SAY("computer starts playing white");\r
5030       break;\r
5031 \r
5032     case IDM_MachineBlack:\r
5033       MachineBlackEvent();\r
5034       /*\r
5035        * refresh the tags dialog only if it's visible\r
5036        */\r
5037       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5038           char *tags;\r
5039           tags = PGNTags(&gameInfo);\r
5040           TagsPopUp(tags, CmailMsg());\r
5041           free(tags);\r
5042       }\r
5043       SAY("computer starts playing black");\r
5044       break;\r
5045 \r
5046     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5047       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5048       break;\r
5049 \r
5050     case IDM_TwoMachines:\r
5051       TwoMachinesEvent();\r
5052       /*\r
5053        * refresh the tags dialog only if it's visible\r
5054        */\r
5055       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5056           char *tags;\r
5057           tags = PGNTags(&gameInfo);\r
5058           TagsPopUp(tags, CmailMsg());\r
5059           free(tags);\r
5060       }\r
5061       SAY("computer starts playing both sides");\r
5062       break;\r
5063 \r
5064     case IDM_AnalysisMode:\r
5065       if(AnalyzeModeEvent()) {\r
5066         SAY("analyzing current position");\r
5067       }\r
5068       break;\r
5069 \r
5070     case IDM_AnalyzeFile:\r
5071       AnalyzeFileEvent();\r
5072       break;\r
5073 \r
5074     case IDM_IcsClient:\r
5075       IcsClientEvent();\r
5076       break;\r
5077 \r
5078     case IDM_EditGame:\r
5079     case IDM_EditGame2:\r
5080       EditGameEvent();\r
5081       SAY("edit game");\r
5082       break;\r
5083 \r
5084     case IDM_EditPosition:\r
5085     case IDM_EditPosition2:\r
5086       EditPositionEvent();\r
5087       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5088       break;\r
5089 \r
5090     case IDM_Training:\r
5091       TrainingEvent();\r
5092       break;\r
5093 \r
5094     case IDM_ShowGameList:\r
5095       ShowGameListProc();\r
5096       break;\r
5097 \r
5098     case IDM_EditProgs1:\r
5099       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5100       break;\r
5101 \r
5102     case IDM_LoadProg1:\r
5103      LoadEnginePopUp(hwndMain, 0);\r
5104       break;\r
5105 \r
5106     case IDM_LoadProg2:\r
5107      LoadEnginePopUp(hwndMain, 1);\r
5108       break;\r
5109 \r
5110     case IDM_EditServers:\r
5111       EditTagsPopUp(icsNames, &icsNames);\r
5112       break;\r
5113 \r
5114     case IDM_EditTags:\r
5115     case IDM_Tags:\r
5116       EditTagsProc();\r
5117       break;\r
5118 \r
5119     case IDM_EditBook:\r
5120       EditBookEvent();\r
5121       break;\r
5122 \r
5123     case IDM_EditComment:\r
5124     case IDM_Comment:\r
5125       if (commentUp && editComment) {\r
5126         CommentPopDown();\r
5127       } else {\r
5128         EditCommentEvent();\r
5129       }\r
5130       break;\r
5131 \r
5132     case IDM_Pause:\r
5133       PauseEvent();\r
5134       break;\r
5135 \r
5136     case IDM_Accept:\r
5137       AcceptEvent();\r
5138       break;\r
5139 \r
5140     case IDM_Decline:\r
5141       DeclineEvent();\r
5142       break;\r
5143 \r
5144     case IDM_Rematch:\r
5145 \r
5146       RematchEvent();\r
5147       break;\r
5148 \r
5149     case IDM_CallFlag:\r
5150       CallFlagEvent();\r
5151       break;\r
5152 \r
5153     case IDM_Draw:\r
5154       DrawEvent();\r
5155       break;\r
5156 \r
5157     case IDM_Adjourn:\r
5158       AdjournEvent();\r
5159       break;\r
5160 \r
5161     case IDM_Abort:\r
5162       AbortEvent();\r
5163       break;\r
5164 \r
5165     case IDM_Resign:\r
5166       ResignEvent();\r
5167       break;\r
5168 \r
5169     case IDM_StopObserving:\r
5170       StopObservingEvent();\r
5171       break;\r
5172 \r
5173     case IDM_StopExamining:\r
5174       StopExaminingEvent();\r
5175       break;\r
5176 \r
5177     case IDM_Upload:\r
5178       UploadGameEvent();\r
5179       break;\r
5180 \r
5181     case IDM_TypeInMove:\r
5182       TypeInEvent('\000');\r
5183       break;\r
5184 \r
5185     case IDM_TypeInName:\r
5186       PopUpNameDialog('\000');\r
5187       break;\r
5188 \r
5189     case IDM_Backward:\r
5190       BackwardEvent();\r
5191       SetFocus(hwndMain);\r
5192       break;\r
5193 \r
5194     JAWS_MENU_ITEMS\r
5195 \r
5196     case IDM_Forward:\r
5197       ForwardEvent();\r
5198       SetFocus(hwndMain);\r
5199       break;\r
5200 \r
5201     case IDM_ToStart:\r
5202       ToStartEvent();\r
5203       SetFocus(hwndMain);\r
5204       break;\r
5205 \r
5206     case IDM_ToEnd:\r
5207       ToEndEvent();\r
5208       SetFocus(hwndMain);\r
5209       break;\r
5210 \r
5211     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5212     case OPT_GameListPrev:\r
5213       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5214       break;\r
5215 \r
5216     case IDM_Revert:\r
5217       RevertEvent(FALSE);\r
5218       break;\r
5219 \r
5220     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5221       RevertEvent(TRUE);\r
5222       break;\r
5223 \r
5224     case IDM_TruncateGame:\r
5225       TruncateGameEvent();\r
5226       break;\r
5227 \r
5228     case IDM_MoveNow:\r
5229       MoveNowEvent();\r
5230       break;\r
5231 \r
5232     case IDM_RetractMove:\r
5233       RetractMoveEvent();\r
5234       break;\r
5235 \r
5236     case IDM_FlipView:\r
5237       flipView = !flipView;\r
5238       DrawPosition(FALSE, NULL);\r
5239       break;\r
5240 \r
5241     case IDM_FlipClock:\r
5242       flipClock = !flipClock;\r
5243       DisplayBothClocks();\r
5244       DisplayLogos();\r
5245       break;\r
5246 \r
5247     case IDM_MuteSounds:\r
5248       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5249       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5250                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5251       break;\r
5252 \r
5253     case IDM_GeneralOptions:\r
5254       GeneralOptionsPopup(hwnd);\r
5255       DrawPosition(TRUE, NULL);\r
5256       break;\r
5257 \r
5258     case IDM_BoardOptions:\r
5259       BoardOptionsPopup(hwnd);\r
5260       break;\r
5261 \r
5262     case IDM_ThemeOptions:\r
5263       ThemeOptionsPopup(hwnd);\r
5264       break;\r
5265 \r
5266     case IDM_EnginePlayOptions:\r
5267       EnginePlayOptionsPopup(hwnd);\r
5268       break;\r
5269 \r
5270     case IDM_Engine1Options:\r
5271       EngineOptionsPopup(hwnd, &first);\r
5272       break;\r
5273 \r
5274     case IDM_Engine2Options:\r
5275       savedHwnd = hwnd;\r
5276       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5277       EngineOptionsPopup(hwnd, &second);\r
5278       break;\r
5279 \r
5280     case IDM_OptionsUCI:\r
5281       UciOptionsPopup(hwnd);\r
5282       break;\r
5283 \r
5284     case IDM_Tourney:\r
5285       TourneyPopup(hwnd);\r
5286       break;\r
5287 \r
5288     case IDM_IcsOptions:\r
5289       IcsOptionsPopup(hwnd);\r
5290       break;\r
5291 \r
5292     case IDM_Fonts:\r
5293       FontsOptionsPopup(hwnd);\r
5294       break;\r
5295 \r
5296     case IDM_Sounds:\r
5297       SoundOptionsPopup(hwnd);\r
5298       break;\r
5299 \r
5300     case IDM_CommPort:\r
5301       CommPortOptionsPopup(hwnd);\r
5302       break;\r
5303 \r
5304     case IDM_LoadOptions:\r
5305       LoadOptionsPopup(hwnd);\r
5306       break;\r
5307 \r
5308     case IDM_SaveOptions:\r
5309       SaveOptionsPopup(hwnd);\r
5310       break;\r
5311 \r
5312     case IDM_TimeControl:\r
5313       TimeControlOptionsPopup(hwnd);\r
5314       break;\r
5315 \r
5316     case IDM_SaveSettings:\r
5317       SaveSettings(settingsFileName);\r
5318       break;\r
5319 \r
5320     case IDM_SaveSettingsOnExit:\r
5321       saveSettingsOnExit = !saveSettingsOnExit;\r
5322       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5323                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5324                                          MF_CHECKED : MF_UNCHECKED));\r
5325       break;\r
5326 \r
5327     case IDM_Hint:\r
5328       HintEvent();\r
5329       break;\r
5330 \r
5331     case IDM_Book:\r
5332       BookEvent();\r
5333       break;\r
5334 \r
5335     case IDM_AboutGame:\r
5336       AboutGameEvent();\r
5337       break;\r
5338 \r
5339     case IDM_Debug:\r
5340       appData.debugMode = !appData.debugMode;\r
5341       if (appData.debugMode) {\r
5342         char dir[MSG_SIZ];\r
5343         GetCurrentDirectory(MSG_SIZ, dir);\r
5344         SetCurrentDirectory(installDir);\r
5345         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5346         SetCurrentDirectory(dir);\r
5347         setbuf(debugFP, NULL);\r
5348       } else {\r
5349         fclose(debugFP);\r
5350         debugFP = NULL;\r
5351       }\r
5352       break;\r
5353 \r
5354     case IDM_HELPCONTENTS:\r
5355       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5356           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5357           MessageBox (GetFocus(),\r
5358                     _("Unable to activate help"),\r
5359                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5360       }\r
5361       break;\r
5362 \r
5363     case IDM_HELPSEARCH:\r
5364         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5365             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5366         MessageBox (GetFocus(),\r
5367                     _("Unable to activate help"),\r
5368                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5369       }\r
5370       break;\r
5371 \r
5372     case IDM_HELPHELP:\r
5373       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5374         MessageBox (GetFocus(),\r
5375                     _("Unable to activate help"),\r
5376                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5377       }\r
5378       break;\r
5379 \r
5380     case IDM_ABOUT:\r
5381       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5382       DialogBox(hInst, \r
5383         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5384         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5385       FreeProcInstance(lpProc);\r
5386       break;\r
5387 \r
5388     case IDM_DirectCommand1:\r
5389       AskQuestionEvent(_("Direct Command"),\r
5390                        _("Send to chess program:"), "", "1");\r
5391       break;\r
5392     case IDM_DirectCommand2:\r
5393       AskQuestionEvent(_("Direct Command"),\r
5394                        _("Send to second chess program:"), "", "2");\r
5395       break;\r
5396 \r
5397     case EP_WhitePawn:\r
5398       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5399       fromX = fromY = -1;\r
5400       break;\r
5401 \r
5402     case EP_WhiteKnight:\r
5403       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5404       fromX = fromY = -1;\r
5405       break;\r
5406 \r
5407     case EP_WhiteBishop:\r
5408       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5409       fromX = fromY = -1;\r
5410       break;\r
5411 \r
5412     case EP_WhiteRook:\r
5413       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5414       fromX = fromY = -1;\r
5415       break;\r
5416 \r
5417     case EP_WhiteQueen:\r
5418       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5419       fromX = fromY = -1;\r
5420       break;\r
5421 \r
5422     case EP_WhiteFerz:\r
5423       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5424       fromX = fromY = -1;\r
5425       break;\r
5426 \r
5427     case EP_WhiteWazir:\r
5428       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5429       fromX = fromY = -1;\r
5430       break;\r
5431 \r
5432     case EP_WhiteAlfil:\r
5433       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5434       fromX = fromY = -1;\r
5435       break;\r
5436 \r
5437     case EP_WhiteCannon:\r
5438       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5439       fromX = fromY = -1;\r
5440       break;\r
5441 \r
5442     case EP_WhiteCardinal:\r
5443       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5444       fromX = fromY = -1;\r
5445       break;\r
5446 \r
5447     case EP_WhiteMarshall:\r
5448       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5449       fromX = fromY = -1;\r
5450       break;\r
5451 \r
5452     case EP_WhiteKing:\r
5453       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5454       fromX = fromY = -1;\r
5455       break;\r
5456 \r
5457     case EP_BlackPawn:\r
5458       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5459       fromX = fromY = -1;\r
5460       break;\r
5461 \r
5462     case EP_BlackKnight:\r
5463       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5464       fromX = fromY = -1;\r
5465       break;\r
5466 \r
5467     case EP_BlackBishop:\r
5468       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5469       fromX = fromY = -1;\r
5470       break;\r
5471 \r
5472     case EP_BlackRook:\r
5473       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5474       fromX = fromY = -1;\r
5475       break;\r
5476 \r
5477     case EP_BlackQueen:\r
5478       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5479       fromX = fromY = -1;\r
5480       break;\r
5481 \r
5482     case EP_BlackFerz:\r
5483       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5484       fromX = fromY = -1;\r
5485       break;\r
5486 \r
5487     case EP_BlackWazir:\r
5488       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5489       fromX = fromY = -1;\r
5490       break;\r
5491 \r
5492     case EP_BlackAlfil:\r
5493       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5494       fromX = fromY = -1;\r
5495       break;\r
5496 \r
5497     case EP_BlackCannon:\r
5498       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5499       fromX = fromY = -1;\r
5500       break;\r
5501 \r
5502     case EP_BlackCardinal:\r
5503       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5504       fromX = fromY = -1;\r
5505       break;\r
5506 \r
5507     case EP_BlackMarshall:\r
5508       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5509       fromX = fromY = -1;\r
5510       break;\r
5511 \r
5512     case EP_BlackKing:\r
5513       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5514       fromX = fromY = -1;\r
5515       break;\r
5516 \r
5517     case EP_EmptySquare:\r
5518       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5519       fromX = fromY = -1;\r
5520       break;\r
5521 \r
5522     case EP_ClearBoard:\r
5523       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5524       fromX = fromY = -1;\r
5525       break;\r
5526 \r
5527     case EP_White:\r
5528       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5529       fromX = fromY = -1;\r
5530       break;\r
5531 \r
5532     case EP_Black:\r
5533       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5534       fromX = fromY = -1;\r
5535       break;\r
5536 \r
5537     case EP_Promote:\r
5538       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5539       fromX = fromY = -1;\r
5540       break;\r
5541 \r
5542     case EP_Demote:\r
5543       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5544       fromX = fromY = -1;\r
5545       break;\r
5546 \r
5547     case DP_Pawn:\r
5548       DropMenuEvent(WhitePawn, fromX, fromY);\r
5549       fromX = fromY = -1;\r
5550       break;\r
5551 \r
5552     case DP_Knight:\r
5553       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5554       fromX = fromY = -1;\r
5555       break;\r
5556 \r
5557     case DP_Bishop:\r
5558       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5559       fromX = fromY = -1;\r
5560       break;\r
5561 \r
5562     case DP_Rook:\r
5563       DropMenuEvent(WhiteRook, fromX, fromY);\r
5564       fromX = fromY = -1;\r
5565       break;\r
5566 \r
5567     case DP_Queen:\r
5568       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5569       fromX = fromY = -1;\r
5570       break;\r
5571 \r
5572     case IDM_English:\r
5573       barbaric = 0; appData.language = "";\r
5574       TranslateMenus(0);\r
5575       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5576       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5577       lastChecked = wmId;\r
5578       break;\r
5579 \r
5580     default:\r
5581       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5582           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5583       else\r
5584       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5585           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5586           TranslateMenus(0);\r
5587           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5588           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5589           lastChecked = wmId;\r
5590           break;\r
5591       }\r
5592       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5593     }\r
5594     break;\r
5595 \r
5596   case WM_TIMER:\r
5597     switch (wParam) {\r
5598     case CLOCK_TIMER_ID:\r
5599       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5600       clockTimerEvent = 0;\r
5601       DecrementClocks(); /* call into back end */\r
5602       break;\r
5603     case LOAD_GAME_TIMER_ID:\r
5604       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5605       loadGameTimerEvent = 0;\r
5606       AutoPlayGameLoop(); /* call into back end */\r
5607       break;\r
5608     case ANALYSIS_TIMER_ID:\r
5609       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5610                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5611         AnalysisPeriodicEvent(0);\r
5612       } else {\r
5613         KillTimer(hwnd, analysisTimerEvent);\r
5614         analysisTimerEvent = 0;\r
5615       }\r
5616       break;\r
5617     case DELAYED_TIMER_ID:\r
5618       KillTimer(hwnd, delayedTimerEvent);\r
5619       delayedTimerEvent = 0;\r
5620       delayedTimerCallback();\r
5621       break;\r
5622     }\r
5623     break;\r
5624 \r
5625   case WM_USER_Input:\r
5626     InputEvent(hwnd, message, wParam, lParam);\r
5627     break;\r
5628 \r
5629   /* [AS] Also move "attached" child windows */\r
5630   case WM_WINDOWPOSCHANGING:\r
5631 \r
5632     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5633         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5634 \r
5635         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5636             /* Window is moving */\r
5637             RECT rcMain;\r
5638 \r
5639 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5640             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5641             rcMain.right  = wpMain.x + wpMain.width;\r
5642             rcMain.top    = wpMain.y;\r
5643             rcMain.bottom = wpMain.y + wpMain.height;\r
5644             \r
5645             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5646             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5647             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5648             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5649             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5650             wpMain.x = lpwp->x;\r
5651             wpMain.y = lpwp->y;\r
5652         }\r
5653     }\r
5654     break;\r
5655 \r
5656   /* [AS] Snapping */\r
5657   case WM_ENTERSIZEMOVE:\r
5658     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5659     if (hwnd == hwndMain) {\r
5660       doingSizing = TRUE;\r
5661       lastSizing = 0;\r
5662     }\r
5663     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5664     break;\r
5665 \r
5666   case WM_SIZING:\r
5667     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5668     if (hwnd == hwndMain) {\r
5669       lastSizing = wParam;\r
5670     }\r
5671     break;\r
5672 \r
5673   case WM_MOVING:\r
5674     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5675       return OnMoving( &sd, hwnd, wParam, lParam );\r
5676 \r
5677   case WM_EXITSIZEMOVE:\r
5678     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5679     if (hwnd == hwndMain) {\r
5680       RECT client;\r
5681       doingSizing = FALSE;\r
5682       InvalidateRect(hwnd, &boardRect, FALSE);\r
5683       GetClientRect(hwnd, &client);\r
5684       ResizeBoard(client.right, client.bottom, lastSizing);\r
5685       lastSizing = 0;\r
5686       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5687     }\r
5688     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5689     break;\r
5690 \r
5691   case WM_DESTROY: /* message: window being destroyed */\r
5692     PostQuitMessage(0);\r
5693     break;\r
5694 \r
5695   case WM_CLOSE:\r
5696     if (hwnd == hwndMain) {\r
5697       ExitEvent(0);\r
5698     }\r
5699     break;\r
5700 \r
5701   default:      /* Passes it on if unprocessed */\r
5702     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5703   }\r
5704   return 0;\r
5705 }\r
5706 \r
5707 /*---------------------------------------------------------------------------*\\r
5708  *\r
5709  * Misc utility routines\r
5710  *\r
5711 \*---------------------------------------------------------------------------*/\r
5712 \r
5713 /*\r
5714  * Decent random number generator, at least not as bad as Windows\r
5715  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5716  */\r
5717 unsigned int randstate;\r
5718 \r
5719 int\r
5720 myrandom(void)\r
5721 {\r
5722   randstate = randstate * 1664525 + 1013904223;\r
5723   return (int) randstate & 0x7fffffff;\r
5724 }\r
5725 \r
5726 void\r
5727 mysrandom(unsigned int seed)\r
5728 {\r
5729   randstate = seed;\r
5730 }\r
5731 \r
5732 \r
5733 /* \r
5734  * returns TRUE if user selects a different color, FALSE otherwise \r
5735  */\r
5736 \r
5737 BOOL\r
5738 ChangeColor(HWND hwnd, COLORREF *which)\r
5739 {\r
5740   static BOOL firstTime = TRUE;\r
5741   static DWORD customColors[16];\r
5742   CHOOSECOLOR cc;\r
5743   COLORREF newcolor;\r
5744   int i;\r
5745   ColorClass ccl;\r
5746 \r
5747   if (firstTime) {\r
5748     /* Make initial colors in use available as custom colors */\r
5749     /* Should we put the compiled-in defaults here instead? */\r
5750     i = 0;\r
5751     customColors[i++] = lightSquareColor & 0xffffff;\r
5752     customColors[i++] = darkSquareColor & 0xffffff;\r
5753     customColors[i++] = whitePieceColor & 0xffffff;\r
5754     customColors[i++] = blackPieceColor & 0xffffff;\r
5755     customColors[i++] = highlightSquareColor & 0xffffff;\r
5756     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5757 \r
5758     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5759       customColors[i++] = textAttribs[ccl].color;\r
5760     }\r
5761     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5762     firstTime = FALSE;\r
5763   }\r
5764 \r
5765   cc.lStructSize = sizeof(cc);\r
5766   cc.hwndOwner = hwnd;\r
5767   cc.hInstance = NULL;\r
5768   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5769   cc.lpCustColors = (LPDWORD) customColors;\r
5770   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5771 \r
5772   if (!ChooseColor(&cc)) return FALSE;\r
5773 \r
5774   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5775   if (newcolor == *which) return FALSE;\r
5776   *which = newcolor;\r
5777   return TRUE;\r
5778 \r
5779   /*\r
5780   InitDrawingColors();\r
5781   InvalidateRect(hwnd, &boardRect, FALSE);\r
5782   */\r
5783 }\r
5784 \r
5785 BOOLEAN\r
5786 MyLoadSound(MySound *ms)\r
5787 {\r
5788   BOOL ok = FALSE;\r
5789   struct stat st;\r
5790   FILE *f;\r
5791 \r
5792   if (ms->data && ms->flag) free(ms->data);\r
5793   ms->data = NULL;\r
5794 \r
5795   switch (ms->name[0]) {\r
5796   case NULLCHAR:\r
5797     /* Silence */\r
5798     ok = TRUE;\r
5799     break;\r
5800   case '$':\r
5801     /* System sound from Control Panel.  Don't preload here. */\r
5802     ok = TRUE;\r
5803     break;\r
5804   case '!':\r
5805     if (ms->name[1] == NULLCHAR) {\r
5806       /* "!" alone = silence */\r
5807       ok = TRUE;\r
5808     } else {\r
5809       /* Builtin wave resource.  Error if not found. */\r
5810       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5811       if (h == NULL) break;\r
5812       ms->data = (void *)LoadResource(hInst, h);\r
5813       ms->flag = 0; // not maloced, so cannot be freed!\r
5814       if (h == NULL) break;\r
5815       ok = TRUE;\r
5816     }\r
5817     break;\r
5818   default:\r
5819     /* .wav file.  Error if not found. */\r
5820     f = fopen(ms->name, "rb");\r
5821     if (f == NULL) break;\r
5822     if (fstat(fileno(f), &st) < 0) break;\r
5823     ms->data = malloc(st.st_size);\r
5824     ms->flag = 1;\r
5825     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5826     fclose(f);\r
5827     ok = TRUE;\r
5828     break;\r
5829   }\r
5830   if (!ok) {\r
5831     char buf[MSG_SIZ];\r
5832       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5833     DisplayError(buf, GetLastError());\r
5834   }\r
5835   return ok;\r
5836 }\r
5837 \r
5838 BOOLEAN\r
5839 MyPlaySound(MySound *ms)\r
5840 {\r
5841   BOOLEAN ok = FALSE;\r
5842 \r
5843   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5844   switch (ms->name[0]) {\r
5845   case NULLCHAR:\r
5846         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5847     /* Silence */\r
5848     ok = TRUE;\r
5849     break;\r
5850   case '$':\r
5851     /* System sound from Control Panel (deprecated feature).\r
5852        "$" alone or an unset sound name gets default beep (still in use). */\r
5853     if (ms->name[1]) {\r
5854       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5855     }\r
5856     if (!ok) ok = MessageBeep(MB_OK);\r
5857     break; \r
5858   case '!':\r
5859     /* Builtin wave resource, or "!" alone for silence */\r
5860     if (ms->name[1]) {\r
5861       if (ms->data == NULL) return FALSE;\r
5862       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5863     } else {\r
5864       ok = TRUE;\r
5865     }\r
5866     break;\r
5867   default:\r
5868     /* .wav file.  Error if not found. */\r
5869     if (ms->data == NULL) return FALSE;\r
5870     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5871     break;\r
5872   }\r
5873   /* Don't print an error: this can happen innocently if the sound driver\r
5874      is busy; for instance, if another instance of WinBoard is playing\r
5875      a sound at about the same time. */\r
5876   return ok;\r
5877 }\r
5878 \r
5879 \r
5880 LRESULT CALLBACK\r
5881 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5882 {\r
5883   BOOL ok;\r
5884   OPENFILENAME *ofn;\r
5885   static UINT *number; /* gross that this is static */\r
5886 \r
5887   switch (message) {\r
5888   case WM_INITDIALOG: /* message: initialize dialog box */\r
5889     /* Center the dialog over the application window */\r
5890     ofn = (OPENFILENAME *) lParam;\r
5891     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5892       number = (UINT *) ofn->lCustData;\r
5893       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5894     } else {\r
5895       number = NULL;\r
5896     }\r
5897     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5898     Translate(hDlg, 1536);\r
5899     return FALSE;  /* Allow for further processing */\r
5900 \r
5901   case WM_COMMAND:\r
5902     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5903       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5904     }\r
5905     return FALSE;  /* Allow for further processing */\r
5906   }\r
5907   return FALSE;\r
5908 }\r
5909 \r
5910 UINT APIENTRY\r
5911 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5912 {\r
5913   static UINT *number;\r
5914   OPENFILENAME *ofname;\r
5915   OFNOTIFY *ofnot;\r
5916   switch (uiMsg) {\r
5917   case WM_INITDIALOG:\r
5918     Translate(hdlg, DLG_IndexNumber);\r
5919     ofname = (OPENFILENAME *)lParam;\r
5920     number = (UINT *)(ofname->lCustData);\r
5921     break;\r
5922   case WM_NOTIFY:\r
5923     ofnot = (OFNOTIFY *)lParam;\r
5924     if (ofnot->hdr.code == CDN_FILEOK) {\r
5925       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5926     }\r
5927     break;\r
5928   }\r
5929   return 0;\r
5930 }\r
5931 \r
5932 \r
5933 FILE *\r
5934 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5935                char *nameFilt, char *dlgTitle, UINT *number,\r
5936                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5937 {\r
5938   OPENFILENAME openFileName;\r
5939   char buf1[MSG_SIZ];\r
5940   FILE *f;\r
5941 \r
5942   if (fileName == NULL) fileName = buf1;\r
5943   if (defName == NULL) {\r
5944     safeStrCpy(fileName, "*.", 3 );\r
5945     strcat(fileName, defExt);\r
5946   } else {\r
5947     safeStrCpy(fileName, defName, MSG_SIZ );\r
5948   }\r
5949     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5950   if (number) *number = 0;\r
5951 \r
5952   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5953   openFileName.hwndOwner         = hwnd;\r
5954   openFileName.hInstance         = (HANDLE) hInst;\r
5955   openFileName.lpstrFilter       = nameFilt;\r
5956   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5957   openFileName.nMaxCustFilter    = 0L;\r
5958   openFileName.nFilterIndex      = 1L;\r
5959   openFileName.lpstrFile         = fileName;\r
5960   openFileName.nMaxFile          = MSG_SIZ;\r
5961   openFileName.lpstrFileTitle    = fileTitle;\r
5962   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5963   openFileName.lpstrInitialDir   = NULL;\r
5964   openFileName.lpstrTitle        = dlgTitle;\r
5965   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5966     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5967     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5968     | (oldDialog ? 0 : OFN_EXPLORER);\r
5969   openFileName.nFileOffset       = 0;\r
5970   openFileName.nFileExtension    = 0;\r
5971   openFileName.lpstrDefExt       = defExt;\r
5972   openFileName.lCustData         = (LONG) number;\r
5973   openFileName.lpfnHook          = oldDialog ?\r
5974     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5975   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5976 \r
5977   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5978                         GetOpenFileName(&openFileName)) {\r
5979     /* open the file */\r
5980     f = fopen(openFileName.lpstrFile, write);\r
5981     if (f == NULL) {\r
5982       MessageBox(hwnd, _("File open failed"), NULL,\r
5983                  MB_OK|MB_ICONEXCLAMATION);\r
5984       return NULL;\r
5985     }\r
5986   } else {\r
5987     int err = CommDlgExtendedError();\r
5988     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5989     return FALSE;\r
5990   }\r
5991   return f;\r
5992 }\r
5993 \r
5994 \r
5995 \r
5996 VOID APIENTRY\r
5997 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5998 {\r
5999   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6000 \r
6001   /*\r
6002    * Get the first pop-up menu in the menu template. This is the\r
6003    * menu that TrackPopupMenu displays.\r
6004    */\r
6005   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6006   TranslateOneMenu(10, hmenuTrackPopup);\r
6007 \r
6008   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6009 \r
6010   /*\r
6011    * TrackPopup uses screen coordinates, so convert the\r
6012    * coordinates of the mouse click to screen coordinates.\r
6013    */\r
6014   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6015 \r
6016   /* Draw and track the floating pop-up menu. */\r
6017   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6018                  pt.x, pt.y, 0, hwnd, NULL);\r
6019 \r
6020   /* Destroy the menu.*/\r
6021   DestroyMenu(hmenu);\r
6022 }\r
6023    \r
6024 typedef struct {\r
6025   HWND hDlg, hText;\r
6026   int sizeX, sizeY, newSizeX, newSizeY;\r
6027   HDWP hdwp;\r
6028 } ResizeEditPlusButtonsClosure;\r
6029 \r
6030 BOOL CALLBACK\r
6031 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6032 {\r
6033   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6034   RECT rect;\r
6035   POINT pt;\r
6036 \r
6037   if (hChild == cl->hText) return TRUE;\r
6038   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6039   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6040   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6041   ScreenToClient(cl->hDlg, &pt);\r
6042   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6043     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6044   return TRUE;\r
6045 }\r
6046 \r
6047 /* Resize a dialog that has a (rich) edit field filling most of\r
6048    the top, with a row of buttons below */\r
6049 VOID\r
6050 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6051 {\r
6052   RECT rectText;\r
6053   int newTextHeight, newTextWidth;\r
6054   ResizeEditPlusButtonsClosure cl;\r
6055   \r
6056   /*if (IsIconic(hDlg)) return;*/\r
6057   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6058   \r
6059   cl.hdwp = BeginDeferWindowPos(8);\r
6060 \r
6061   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6062   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6063   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6064   if (newTextHeight < 0) {\r
6065     newSizeY += -newTextHeight;\r
6066     newTextHeight = 0;\r
6067   }\r
6068   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6069     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6070 \r
6071   cl.hDlg = hDlg;\r
6072   cl.hText = hText;\r
6073   cl.sizeX = sizeX;\r
6074   cl.sizeY = sizeY;\r
6075   cl.newSizeX = newSizeX;\r
6076   cl.newSizeY = newSizeY;\r
6077   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6078 \r
6079   EndDeferWindowPos(cl.hdwp);\r
6080 }\r
6081 \r
6082 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6083 {\r
6084     RECT    rChild, rParent;\r
6085     int     wChild, hChild, wParent, hParent;\r
6086     int     wScreen, hScreen, xNew, yNew;\r
6087     HDC     hdc;\r
6088 \r
6089     /* Get the Height and Width of the child window */\r
6090     GetWindowRect (hwndChild, &rChild);\r
6091     wChild = rChild.right - rChild.left;\r
6092     hChild = rChild.bottom - rChild.top;\r
6093 \r
6094     /* Get the Height and Width of the parent window */\r
6095     GetWindowRect (hwndParent, &rParent);\r
6096     wParent = rParent.right - rParent.left;\r
6097     hParent = rParent.bottom - rParent.top;\r
6098 \r
6099     /* Get the display limits */\r
6100     hdc = GetDC (hwndChild);\r
6101     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6102     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6103     ReleaseDC(hwndChild, hdc);\r
6104 \r
6105     /* Calculate new X position, then adjust for screen */\r
6106     xNew = rParent.left + ((wParent - wChild) /2);\r
6107     if (xNew < 0) {\r
6108         xNew = 0;\r
6109     } else if ((xNew+wChild) > wScreen) {\r
6110         xNew = wScreen - wChild;\r
6111     }\r
6112 \r
6113     /* Calculate new Y position, then adjust for screen */\r
6114     if( mode == 0 ) {\r
6115         yNew = rParent.top  + ((hParent - hChild) /2);\r
6116     }\r
6117     else {\r
6118         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6119     }\r
6120 \r
6121     if (yNew < 0) {\r
6122         yNew = 0;\r
6123     } else if ((yNew+hChild) > hScreen) {\r
6124         yNew = hScreen - hChild;\r
6125     }\r
6126 \r
6127     /* Set it, and return */\r
6128     return SetWindowPos (hwndChild, NULL,\r
6129                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6130 }\r
6131 \r
6132 /* Center one window over another */\r
6133 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6134 {\r
6135     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6136 }\r
6137 \r
6138 /*---------------------------------------------------------------------------*\\r
6139  *\r
6140  * Startup Dialog functions\r
6141  *\r
6142 \*---------------------------------------------------------------------------*/\r
6143 void\r
6144 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6145 {\r
6146   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6147 \r
6148   while (*cd != NULL) {\r
6149     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6150     cd++;\r
6151   }\r
6152 }\r
6153 \r
6154 void\r
6155 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6156 {\r
6157   char buf1[MAX_ARG_LEN];\r
6158   int len;\r
6159 \r
6160   if (str[0] == '@') {\r
6161     FILE* f = fopen(str + 1, "r");\r
6162     if (f == NULL) {\r
6163       DisplayFatalError(str + 1, errno, 2);\r
6164       return;\r
6165     }\r
6166     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6167     fclose(f);\r
6168     buf1[len] = NULLCHAR;\r
6169     str = buf1;\r
6170   }\r
6171 \r
6172   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6173 \r
6174   for (;;) {\r
6175     char buf[MSG_SIZ];\r
6176     char *end = strchr(str, '\n');\r
6177     if (end == NULL) return;\r
6178     memcpy(buf, str, end - str);\r
6179     buf[end - str] = NULLCHAR;\r
6180     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6181     str = end + 1;\r
6182   }\r
6183 }\r
6184 \r
6185 void\r
6186 SetStartupDialogEnables(HWND hDlg)\r
6187 {\r
6188   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6189     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6190     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6191   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6192     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6193   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6194     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6195   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6196     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6197   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6198     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6199     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6200     IsDlgButtonChecked(hDlg, OPT_View));\r
6201 }\r
6202 \r
6203 char *\r
6204 QuoteForFilename(char *filename)\r
6205 {\r
6206   int dquote, space;\r
6207   dquote = strchr(filename, '"') != NULL;\r
6208   space = strchr(filename, ' ') != NULL;\r
6209   if (dquote || space) {\r
6210     if (dquote) {\r
6211       return "'";\r
6212     } else {\r
6213       return "\"";\r
6214     }\r
6215   } else {\r
6216     return "";\r
6217   }\r
6218 }\r
6219 \r
6220 VOID\r
6221 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6222 {\r
6223   char buf[MSG_SIZ];\r
6224   char *q;\r
6225 \r
6226   InitComboStringsFromOption(hwndCombo, nthnames);\r
6227   q = QuoteForFilename(nthcp);\r
6228     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6229   if (*nthdir != NULLCHAR) {\r
6230     q = QuoteForFilename(nthdir);\r
6231       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6232   }\r
6233   if (*nthcp == NULLCHAR) {\r
6234     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6235   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6236     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6237     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6238   }\r
6239 }\r
6240 \r
6241 LRESULT CALLBACK\r
6242 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6243 {\r
6244   char buf[MSG_SIZ];\r
6245   HANDLE hwndCombo;\r
6246   char *p;\r
6247 \r
6248   switch (message) {\r
6249   case WM_INITDIALOG:\r
6250     /* Center the dialog */\r
6251     CenterWindow (hDlg, GetDesktopWindow());\r
6252     Translate(hDlg, DLG_Startup);\r
6253     /* Initialize the dialog items */\r
6254     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6255                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6256                   firstChessProgramNames);\r
6257     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6258                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6259                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6260     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6261     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6262       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6263     if (*appData.icsHelper != NULLCHAR) {\r
6264       char *q = QuoteForFilename(appData.icsHelper);\r
6265       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6266     }\r
6267     if (*appData.icsHost == NULLCHAR) {\r
6268       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6269       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6270     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6271       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6272       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6273     }\r
6274 \r
6275     if (appData.icsActive) {\r
6276       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6277     }\r
6278     else if (appData.noChessProgram) {\r
6279       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6280     }\r
6281     else {\r
6282       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6283     }\r
6284 \r
6285     SetStartupDialogEnables(hDlg);\r
6286     return TRUE;\r
6287 \r
6288   case WM_COMMAND:\r
6289     switch (LOWORD(wParam)) {\r
6290     case IDOK:\r
6291       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6292         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6293         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6294         p = buf;\r
6295         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6296         ParseArgs(StringGet, &p);\r
6297         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6298         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6299         p = buf;\r
6300         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6301         ParseArgs(StringGet, &p);\r
6302         SwapEngines(singleList); // ... and then make it 'second'\r
6303 \r
6304         appData.noChessProgram = FALSE;\r
6305         appData.icsActive = FALSE;\r
6306       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6307         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6308         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6309         p = buf;\r
6310         ParseArgs(StringGet, &p);\r
6311         if (appData.zippyPlay) {\r
6312           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6313           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6314           p = buf;\r
6315           ParseArgs(StringGet, &p);\r
6316         }\r
6317       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6318         appData.noChessProgram = TRUE;\r
6319         appData.icsActive = FALSE;\r
6320       } else {\r
6321         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6322                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6323         return TRUE;\r
6324       }\r
6325       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6326         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6327         p = buf;\r
6328         ParseArgs(StringGet, &p);\r
6329       }\r
6330       EndDialog(hDlg, TRUE);\r
6331       return TRUE;\r
6332 \r
6333     case IDCANCEL:\r
6334       ExitEvent(0);\r
6335       return TRUE;\r
6336 \r
6337     case IDM_HELPCONTENTS:\r
6338       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6339         MessageBox (GetFocus(),\r
6340                     _("Unable to activate help"),\r
6341                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6342       }\r
6343       break;\r
6344 \r
6345     default:\r
6346       SetStartupDialogEnables(hDlg);\r
6347       break;\r
6348     }\r
6349     break;\r
6350   }\r
6351   return FALSE;\r
6352 }\r
6353 \r
6354 /*---------------------------------------------------------------------------*\\r
6355  *\r
6356  * About box dialog functions\r
6357  *\r
6358 \*---------------------------------------------------------------------------*/\r
6359 \r
6360 /* Process messages for "About" dialog box */\r
6361 LRESULT CALLBACK\r
6362 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6363 {\r
6364   switch (message) {\r
6365   case WM_INITDIALOG: /* message: initialize dialog box */\r
6366     /* Center the dialog over the application window */\r
6367     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6368     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6369     Translate(hDlg, ABOUTBOX);\r
6370     JAWS_COPYRIGHT\r
6371     return (TRUE);\r
6372 \r
6373   case WM_COMMAND: /* message: received a command */\r
6374     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6375         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6376       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6377       return (TRUE);\r
6378     }\r
6379     break;\r
6380   }\r
6381   return (FALSE);\r
6382 }\r
6383 \r
6384 /*---------------------------------------------------------------------------*\\r
6385  *\r
6386  * Comment Dialog functions\r
6387  *\r
6388 \*---------------------------------------------------------------------------*/\r
6389 \r
6390 LRESULT CALLBACK\r
6391 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6392 {\r
6393   static HANDLE hwndText = NULL;\r
6394   int len, newSizeX, newSizeY;\r
6395   static int sizeX, sizeY;\r
6396   char *str;\r
6397   RECT rect;\r
6398   MINMAXINFO *mmi;\r
6399 \r
6400   switch (message) {\r
6401   case WM_INITDIALOG: /* message: initialize dialog box */\r
6402     /* Initialize the dialog items */\r
6403     Translate(hDlg, DLG_EditComment);\r
6404     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6405     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6406     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6407     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6408     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6409     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6410     SetWindowText(hDlg, commentTitle);\r
6411     if (editComment) {\r
6412       SetFocus(hwndText);\r
6413     } else {\r
6414       SetFocus(GetDlgItem(hDlg, IDOK));\r
6415     }\r
6416     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6417                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6418                 MAKELPARAM(FALSE, 0));\r
6419     /* Size and position the dialog */\r
6420     if (!commentDialog) {\r
6421       commentDialog = hDlg;\r
6422       GetClientRect(hDlg, &rect);\r
6423       sizeX = rect.right;\r
6424       sizeY = rect.bottom;\r
6425       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6426           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6427         WINDOWPLACEMENT wp;\r
6428         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6429         wp.length = sizeof(WINDOWPLACEMENT);\r
6430         wp.flags = 0;\r
6431         wp.showCmd = SW_SHOW;\r
6432         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6433         wp.rcNormalPosition.left = wpComment.x;\r
6434         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6435         wp.rcNormalPosition.top = wpComment.y;\r
6436         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6437         SetWindowPlacement(hDlg, &wp);\r
6438 \r
6439         GetClientRect(hDlg, &rect);\r
6440         newSizeX = rect.right;\r
6441         newSizeY = rect.bottom;\r
6442         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6443                               newSizeX, newSizeY);\r
6444         sizeX = newSizeX;\r
6445         sizeY = newSizeY;\r
6446       }\r
6447     }\r
6448     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6449     return FALSE;\r
6450 \r
6451   case WM_COMMAND: /* message: received a command */\r
6452     switch (LOWORD(wParam)) {\r
6453     case IDOK:\r
6454       if (editComment) {\r
6455         char *p, *q;\r
6456         /* Read changed options from the dialog box */\r
6457         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6458         len = GetWindowTextLength(hwndText);\r
6459         str = (char *) malloc(len + 1);\r
6460         GetWindowText(hwndText, str, len + 1);\r
6461         p = q = str;\r
6462         while (*q) {\r
6463           if (*q == '\r')\r
6464             q++;\r
6465           else\r
6466             *p++ = *q++;\r
6467         }\r
6468         *p = NULLCHAR;\r
6469         ReplaceComment(commentIndex, str);\r
6470         free(str);\r
6471       }\r
6472       CommentPopDown();\r
6473       return TRUE;\r
6474 \r
6475     case IDCANCEL:\r
6476     case OPT_CancelComment:\r
6477       CommentPopDown();\r
6478       return TRUE;\r
6479 \r
6480     case OPT_ClearComment:\r
6481       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6482       break;\r
6483 \r
6484     case OPT_EditComment:\r
6485       EditCommentEvent();\r
6486       return TRUE;\r
6487 \r
6488     default:\r
6489       break;\r
6490     }\r
6491     break;\r
6492 \r
6493   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6494         if( wParam == OPT_CommentText ) {\r
6495             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6496 \r
6497             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6498                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6499                 POINTL pt;\r
6500                 LRESULT index;\r
6501 \r
6502                 pt.x = LOWORD( lpMF->lParam );\r
6503                 pt.y = HIWORD( lpMF->lParam );\r
6504 \r
6505                 if(lpMF->msg == WM_CHAR) {\r
6506                         CHARRANGE sel;\r
6507                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6508                         index = sel.cpMin;\r
6509                 } else\r
6510                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6511 \r
6512                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6513                 len = GetWindowTextLength(hwndText);\r
6514                 str = (char *) malloc(len + 1);\r
6515                 GetWindowText(hwndText, str, len + 1);\r
6516                 ReplaceComment(commentIndex, str);\r
6517                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6518                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6519                 free(str);\r
6520 \r
6521                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6522                 lpMF->msg = WM_USER;\r
6523 \r
6524                 return TRUE;\r
6525             }\r
6526         }\r
6527         break;\r
6528 \r
6529   case WM_SIZE:\r
6530     newSizeX = LOWORD(lParam);\r
6531     newSizeY = HIWORD(lParam);\r
6532     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6533     sizeX = newSizeX;\r
6534     sizeY = newSizeY;\r
6535     break;\r
6536 \r
6537   case WM_GETMINMAXINFO:\r
6538     /* Prevent resizing window too small */\r
6539     mmi = (MINMAXINFO *) lParam;\r
6540     mmi->ptMinTrackSize.x = 100;\r
6541     mmi->ptMinTrackSize.y = 100;\r
6542     break;\r
6543   }\r
6544   return FALSE;\r
6545 }\r
6546 \r
6547 VOID\r
6548 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6549 {\r
6550   FARPROC lpProc;\r
6551   char *p, *q;\r
6552 \r
6553   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6554 \r
6555   if (str == NULL) str = "";\r
6556   p = (char *) malloc(2 * strlen(str) + 2);\r
6557   q = p;\r
6558   while (*str) {\r
6559     if (*str == '\n') *q++ = '\r';\r
6560     *q++ = *str++;\r
6561   }\r
6562   *q = NULLCHAR;\r
6563   if (commentText != NULL) free(commentText);\r
6564 \r
6565   commentIndex = index;\r
6566   commentTitle = title;\r
6567   commentText = p;\r
6568   editComment = edit;\r
6569 \r
6570   if (commentDialog) {\r
6571     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6572     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6573   } else {\r
6574     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6575     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6576                  hwndMain, (DLGPROC)lpProc);\r
6577     FreeProcInstance(lpProc);\r
6578   }\r
6579   commentUp = TRUE;\r
6580 }\r
6581 \r
6582 \r
6583 /*---------------------------------------------------------------------------*\\r
6584  *\r
6585  * Type-in move dialog functions\r
6586  * \r
6587 \*---------------------------------------------------------------------------*/\r
6588 \r
6589 LRESULT CALLBACK\r
6590 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6591 {\r
6592   char move[MSG_SIZ];\r
6593   HWND hInput;\r
6594 \r
6595   switch (message) {\r
6596   case WM_INITDIALOG:\r
6597     move[0] = (char) lParam;\r
6598     move[1] = NULLCHAR;\r
6599     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6600     Translate(hDlg, DLG_TypeInMove);\r
6601     hInput = GetDlgItem(hDlg, OPT_Move);\r
6602     SetWindowText(hInput, move);\r
6603     SetFocus(hInput);\r
6604     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6605     return FALSE;\r
6606 \r
6607   case WM_COMMAND:\r
6608     switch (LOWORD(wParam)) {\r
6609     case IDOK:\r
6610 \r
6611       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6612       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6613       TypeInDoneEvent(move);\r
6614       EndDialog(hDlg, TRUE);\r
6615       return TRUE;\r
6616     case IDCANCEL:\r
6617       EndDialog(hDlg, FALSE);\r
6618       return TRUE;\r
6619     default:\r
6620       break;\r
6621     }\r
6622     break;\r
6623   }\r
6624   return FALSE;\r
6625 }\r
6626 \r
6627 VOID\r
6628 PopUpMoveDialog(char firstchar)\r
6629 {\r
6630     FARPROC lpProc;\r
6631 \r
6632       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6633       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6634         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6635       FreeProcInstance(lpProc);\r
6636 }\r
6637 \r
6638 /*---------------------------------------------------------------------------*\\r
6639  *\r
6640  * Type-in name dialog functions\r
6641  * \r
6642 \*---------------------------------------------------------------------------*/\r
6643 \r
6644 LRESULT CALLBACK\r
6645 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6646 {\r
6647   char move[MSG_SIZ];\r
6648   HWND hInput;\r
6649 \r
6650   switch (message) {\r
6651   case WM_INITDIALOG:\r
6652     move[0] = (char) lParam;\r
6653     move[1] = NULLCHAR;\r
6654     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6655     Translate(hDlg, DLG_TypeInName);\r
6656     hInput = GetDlgItem(hDlg, OPT_Name);\r
6657     SetWindowText(hInput, move);\r
6658     SetFocus(hInput);\r
6659     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6660     return FALSE;\r
6661 \r
6662   case WM_COMMAND:\r
6663     switch (LOWORD(wParam)) {\r
6664     case IDOK:\r
6665       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6666       appData.userName = strdup(move);\r
6667       SetUserLogo();\r
6668       SetGameInfo();\r
6669       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6670         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6671         DisplayTitle(move);\r
6672       }\r
6673 \r
6674 \r
6675       EndDialog(hDlg, TRUE);\r
6676       return TRUE;\r
6677     case IDCANCEL:\r
6678       EndDialog(hDlg, FALSE);\r
6679       return TRUE;\r
6680     default:\r
6681       break;\r
6682     }\r
6683     break;\r
6684   }\r
6685   return FALSE;\r
6686 }\r
6687 \r
6688 VOID\r
6689 PopUpNameDialog(char firstchar)\r
6690 {\r
6691     FARPROC lpProc;\r
6692     \r
6693       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6694       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6695         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6696       FreeProcInstance(lpProc);\r
6697 }\r
6698 \r
6699 /*---------------------------------------------------------------------------*\\r
6700  *\r
6701  *  Error dialogs\r
6702  * \r
6703 \*---------------------------------------------------------------------------*/\r
6704 \r
6705 /* Nonmodal error box */\r
6706 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6707                              WPARAM wParam, LPARAM lParam);\r
6708 \r
6709 VOID\r
6710 ErrorPopUp(char *title, char *content)\r
6711 {\r
6712   FARPROC lpProc;\r
6713   char *p, *q;\r
6714   BOOLEAN modal = hwndMain == NULL;\r
6715 \r
6716   p = content;\r
6717   q = errorMessage;\r
6718   while (*p) {\r
6719     if (*p == '\n') {\r
6720       if (modal) {\r
6721         *q++ = ' ';\r
6722         p++;\r
6723       } else {\r
6724         *q++ = '\r';\r
6725         *q++ = *p++;\r
6726       }\r
6727     } else {\r
6728       *q++ = *p++;\r
6729     }\r
6730   }\r
6731   *q = NULLCHAR;\r
6732   strncpy(errorTitle, title, sizeof(errorTitle));\r
6733   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6734   \r
6735   if (modal) {\r
6736     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6737   } else {\r
6738     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6739     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6740                  hwndMain, (DLGPROC)lpProc);\r
6741     FreeProcInstance(lpProc);\r
6742   }\r
6743 }\r
6744 \r
6745 VOID\r
6746 ErrorPopDown()\r
6747 {\r
6748   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6749   if (errorDialog == NULL) return;\r
6750   DestroyWindow(errorDialog);\r
6751   errorDialog = NULL;\r
6752   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6753 }\r
6754 \r
6755 LRESULT CALLBACK\r
6756 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6757 {\r
6758   RECT rChild;\r
6759 \r
6760   switch (message) {\r
6761   case WM_INITDIALOG:\r
6762     GetWindowRect(hDlg, &rChild);\r
6763 \r
6764     /*\r
6765     SetWindowPos(hDlg, NULL, rChild.left,\r
6766       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6767       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6768     */\r
6769 \r
6770     /* \r
6771         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6772         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6773         and it doesn't work when you resize the dialog.\r
6774         For now, just give it a default position.\r
6775     */\r
6776     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6777     Translate(hDlg, DLG_Error);\r
6778 \r
6779     errorDialog = hDlg;\r
6780     SetWindowText(hDlg, errorTitle);\r
6781     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6782     return FALSE;\r
6783 \r
6784   case WM_COMMAND:\r
6785     switch (LOWORD(wParam)) {\r
6786     case IDOK:\r
6787     case IDCANCEL:\r
6788       if (errorDialog == hDlg) errorDialog = NULL;\r
6789       DestroyWindow(hDlg);\r
6790       return TRUE;\r
6791 \r
6792     default:\r
6793       break;\r
6794     }\r
6795     break;\r
6796   }\r
6797   return FALSE;\r
6798 }\r
6799 \r
6800 #ifdef GOTHIC\r
6801 HWND gothicDialog = NULL;\r
6802 \r
6803 LRESULT CALLBACK\r
6804 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6805 {\r
6806   RECT rChild;\r
6807   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6808 \r
6809   switch (message) {\r
6810   case WM_INITDIALOG:\r
6811     GetWindowRect(hDlg, &rChild);\r
6812 \r
6813     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6814                                                              SWP_NOZORDER);\r
6815 \r
6816     /* \r
6817         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6818         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6819         and it doesn't work when you resize the dialog.\r
6820         For now, just give it a default position.\r
6821     */\r
6822     gothicDialog = hDlg;\r
6823     SetWindowText(hDlg, errorTitle);\r
6824     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6825     return FALSE;\r
6826 \r
6827   case WM_COMMAND:\r
6828     switch (LOWORD(wParam)) {\r
6829     case IDOK:\r
6830     case IDCANCEL:\r
6831       if (errorDialog == hDlg) errorDialog = NULL;\r
6832       DestroyWindow(hDlg);\r
6833       return TRUE;\r
6834 \r
6835     default:\r
6836       break;\r
6837     }\r
6838     break;\r
6839   }\r
6840   return FALSE;\r
6841 }\r
6842 \r
6843 VOID\r
6844 GothicPopUp(char *title, VariantClass variant)\r
6845 {\r
6846   FARPROC lpProc;\r
6847   static char *lastTitle;\r
6848 \r
6849   strncpy(errorTitle, title, sizeof(errorTitle));\r
6850   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6851 \r
6852   if(lastTitle != title && gothicDialog != NULL) {\r
6853     DestroyWindow(gothicDialog);\r
6854     gothicDialog = NULL;\r
6855   }\r
6856   if(variant != VariantNormal && gothicDialog == NULL) {\r
6857     title = lastTitle;\r
6858     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6859     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6860                  hwndMain, (DLGPROC)lpProc);\r
6861     FreeProcInstance(lpProc);\r
6862   }\r
6863 }\r
6864 #endif\r
6865 \r
6866 /*---------------------------------------------------------------------------*\\r
6867  *\r
6868  *  Ics Interaction console functions\r
6869  *\r
6870 \*---------------------------------------------------------------------------*/\r
6871 \r
6872 #define HISTORY_SIZE 64\r
6873 static char *history[HISTORY_SIZE];\r
6874 int histIn = 0, histP = 0;\r
6875 \r
6876 \r
6877 VOID\r
6878 SaveInHistory(char *cmd)\r
6879 {\r
6880   if (history[histIn] != NULL) {\r
6881     free(history[histIn]);\r
6882     history[histIn] = NULL;\r
6883   }\r
6884   if (*cmd == NULLCHAR) return;\r
6885   history[histIn] = StrSave(cmd);\r
6886   histIn = (histIn + 1) % HISTORY_SIZE;\r
6887   if (history[histIn] != NULL) {\r
6888     free(history[histIn]);\r
6889 \r
6890     history[histIn] = NULL;\r
6891   }\r
6892   histP = histIn;\r
6893 }\r
6894 \r
6895 char *\r
6896 PrevInHistory(char *cmd)\r
6897 {\r
6898   int newhp;\r
6899   if (histP == histIn) {\r
6900     if (history[histIn] != NULL) free(history[histIn]);\r
6901     history[histIn] = StrSave(cmd);\r
6902   }\r
6903   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6904   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6905   histP = newhp;\r
6906   return history[histP];\r
6907 }\r
6908 \r
6909 char *\r
6910 NextInHistory()\r
6911 {\r
6912   if (histP == histIn) return NULL;\r
6913   histP = (histP + 1) % HISTORY_SIZE;\r
6914   return history[histP];   \r
6915 }\r
6916 \r
6917 HMENU\r
6918 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6919 {\r
6920   HMENU hmenu, h;\r
6921   int i = 0;\r
6922   hmenu = LoadMenu(hInst, "TextMenu");\r
6923   h = GetSubMenu(hmenu, 0);\r
6924   while (e->item) {\r
6925     if (strcmp(e->item, "-") == 0) {\r
6926       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6927     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6928       int flags = MF_STRING, j = 0;\r
6929       if (e->item[0] == '|') {\r
6930         flags |= MF_MENUBARBREAK;\r
6931         j++;\r
6932       }\r
6933       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6934       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6935     }\r
6936     e++;\r
6937     i++;\r
6938   } \r
6939   return hmenu;\r
6940 }\r
6941 \r
6942 WNDPROC consoleTextWindowProc;\r
6943 \r
6944 void\r
6945 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6946 {\r
6947   char buf[MSG_SIZ], name[MSG_SIZ];\r
6948   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6949   CHARRANGE sel;\r
6950 \r
6951   if (!getname) {\r
6952     SetWindowText(hInput, command);\r
6953     if (immediate) {\r
6954       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6955     } else {\r
6956       sel.cpMin = 999999;\r
6957       sel.cpMax = 999999;\r
6958       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6959       SetFocus(hInput);\r
6960     }\r
6961     return;\r
6962   }    \r
6963   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6964   if (sel.cpMin == sel.cpMax) {\r
6965     /* Expand to surrounding word */\r
6966     TEXTRANGE tr;\r
6967     do {\r
6968       tr.chrg.cpMax = sel.cpMin;\r
6969       tr.chrg.cpMin = --sel.cpMin;\r
6970       if (sel.cpMin < 0) break;\r
6971       tr.lpstrText = name;\r
6972       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6973     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6974     sel.cpMin++;\r
6975 \r
6976     do {\r
6977       tr.chrg.cpMin = sel.cpMax;\r
6978       tr.chrg.cpMax = ++sel.cpMax;\r
6979       tr.lpstrText = name;\r
6980       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6981     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6982     sel.cpMax--;\r
6983 \r
6984     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6985       MessageBeep(MB_ICONEXCLAMATION);\r
6986       return;\r
6987     }\r
6988     tr.chrg = sel;\r
6989     tr.lpstrText = name;\r
6990     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6991   } else {\r
6992     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6993       MessageBeep(MB_ICONEXCLAMATION);\r
6994       return;\r
6995     }\r
6996     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6997   }\r
6998   if (immediate) {\r
6999     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7000     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7001     SetWindowText(hInput, buf);\r
7002     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7003   } else {\r
7004     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7005       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7006     SetWindowText(hInput, buf);\r
7007     sel.cpMin = 999999;\r
7008     sel.cpMax = 999999;\r
7009     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7010     SetFocus(hInput);\r
7011   }\r
7012 }\r
7013 \r
7014 LRESULT CALLBACK \r
7015 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7016 {\r
7017   HWND hInput;\r
7018   CHARRANGE sel;\r
7019 \r
7020   switch (message) {\r
7021   case WM_KEYDOWN:\r
7022     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7023     if(wParam=='R') return 0;\r
7024     switch (wParam) {\r
7025     case VK_PRIOR:\r
7026       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7027       return 0;\r
7028     case VK_NEXT:\r
7029       sel.cpMin = 999999;\r
7030       sel.cpMax = 999999;\r
7031       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7032       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7033       return 0;\r
7034     }\r
7035     break;\r
7036   case WM_CHAR:\r
7037    if(wParam != '\022') {\r
7038     if (wParam == '\t') {\r
7039       if (GetKeyState(VK_SHIFT) < 0) {\r
7040         /* shifted */\r
7041         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7042         if (buttonDesc[0].hwnd) {\r
7043           SetFocus(buttonDesc[0].hwnd);\r
7044         } else {\r
7045           SetFocus(hwndMain);\r
7046         }\r
7047       } else {\r
7048         /* unshifted */\r
7049         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7050       }\r
7051     } else {\r
7052       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7053       JAWS_DELETE( SetFocus(hInput); )\r
7054       SendMessage(hInput, message, wParam, lParam);\r
7055     }\r
7056     return 0;\r
7057    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7058    lParam = -1;\r
7059   case WM_RBUTTONDOWN:\r
7060     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7061       /* Move selection here if it was empty */\r
7062       POINT pt;\r
7063       pt.x = LOWORD(lParam);\r
7064       pt.y = HIWORD(lParam);\r
7065       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7066       if (sel.cpMin == sel.cpMax) {\r
7067         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7068         sel.cpMax = sel.cpMin;\r
7069         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7070       }\r
7071       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7072 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7073       POINT pt;\r
7074       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7075       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7076       if (sel.cpMin == sel.cpMax) {\r
7077         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7078         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7079       }\r
7080       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7081         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7082       }\r
7083       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7084       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7085       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7086       MenuPopup(hwnd, pt, hmenu, -1);\r
7087 }\r
7088     }\r
7089     return 0;\r
7090   case WM_RBUTTONUP:\r
7091     if (GetKeyState(VK_SHIFT) & ~1) {\r
7092       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7093         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7094     }\r
7095     return 0;\r
7096   case WM_PASTE:\r
7097     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7098     SetFocus(hInput);\r
7099     return SendMessage(hInput, message, wParam, lParam);\r
7100   case WM_MBUTTONDOWN:\r
7101     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7102   case WM_COMMAND:\r
7103     switch (LOWORD(wParam)) {\r
7104     case IDM_QuickPaste:\r
7105       {\r
7106         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7107         if (sel.cpMin == sel.cpMax) {\r
7108           MessageBeep(MB_ICONEXCLAMATION);\r
7109           return 0;\r
7110         }\r
7111         SendMessage(hwnd, WM_COPY, 0, 0);\r
7112         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7113         SendMessage(hInput, WM_PASTE, 0, 0);\r
7114         SetFocus(hInput);\r
7115         return 0;\r
7116       }\r
7117     case IDM_Cut:\r
7118       SendMessage(hwnd, WM_CUT, 0, 0);\r
7119       return 0;\r
7120     case IDM_Paste:\r
7121       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7122       return 0;\r
7123     case IDM_Copy:\r
7124       SendMessage(hwnd, WM_COPY, 0, 0);\r
7125       return 0;\r
7126     default:\r
7127       {\r
7128         int i = LOWORD(wParam) - IDM_CommandX;\r
7129         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7130             icsTextMenuEntry[i].command != NULL) {\r
7131           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7132                    icsTextMenuEntry[i].getname,\r
7133                    icsTextMenuEntry[i].immediate);\r
7134           return 0;\r
7135         }\r
7136       }\r
7137       break;\r
7138     }\r
7139     break;\r
7140   }\r
7141   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7142 }\r
7143 \r
7144 WNDPROC consoleInputWindowProc;\r
7145 \r
7146 LRESULT CALLBACK\r
7147 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7148 {\r
7149   char buf[MSG_SIZ];\r
7150   char *p;\r
7151   static BOOL sendNextChar = FALSE;\r
7152   static BOOL quoteNextChar = FALSE;\r
7153   InputSource *is = consoleInputSource;\r
7154   CHARFORMAT cf;\r
7155   CHARRANGE sel;\r
7156 \r
7157   switch (message) {\r
7158   case WM_CHAR:\r
7159     if (!appData.localLineEditing || sendNextChar) {\r
7160       is->buf[0] = (CHAR) wParam;\r
7161       is->count = 1;\r
7162       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7163       sendNextChar = FALSE;\r
7164       return 0;\r
7165     }\r
7166     if (quoteNextChar) {\r
7167       buf[0] = (char) wParam;\r
7168       buf[1] = NULLCHAR;\r
7169       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7170       quoteNextChar = FALSE;\r
7171       return 0;\r
7172     }\r
7173     switch (wParam) {\r
7174     case '\r':   /* Enter key */\r
7175       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7176       if (consoleEcho) SaveInHistory(is->buf);\r
7177       is->buf[is->count++] = '\n';\r
7178       is->buf[is->count] = NULLCHAR;\r
7179       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7180       if (consoleEcho) {\r
7181         ConsoleOutput(is->buf, is->count, TRUE);\r
7182       } else if (appData.localLineEditing) {\r
7183         ConsoleOutput("\n", 1, TRUE);\r
7184       }\r
7185       /* fall thru */\r
7186     case '\033': /* Escape key */\r
7187       SetWindowText(hwnd, "");\r
7188       cf.cbSize = sizeof(CHARFORMAT);\r
7189       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7190       if (consoleEcho) {\r
7191         cf.crTextColor = textAttribs[ColorNormal].color;\r
7192       } else {\r
7193         cf.crTextColor = COLOR_ECHOOFF;\r
7194       }\r
7195       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7196       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7197       return 0;\r
7198     case '\t':   /* Tab key */\r
7199       if (GetKeyState(VK_SHIFT) < 0) {\r
7200         /* shifted */\r
7201         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7202       } else {\r
7203         /* unshifted */\r
7204         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7205         if (buttonDesc[0].hwnd) {\r
7206           SetFocus(buttonDesc[0].hwnd);\r
7207         } else {\r
7208           SetFocus(hwndMain);\r
7209         }\r
7210       }\r
7211       return 0;\r
7212     case '\023': /* Ctrl+S */\r
7213       sendNextChar = TRUE;\r
7214       return 0;\r
7215     case '\021': /* Ctrl+Q */\r
7216       quoteNextChar = TRUE;\r
7217       return 0;\r
7218     JAWS_REPLAY\r
7219     default:\r
7220       break;\r
7221     }\r
7222     break;\r
7223   case WM_KEYDOWN:\r
7224     switch (wParam) {\r
7225     case VK_UP:\r
7226       GetWindowText(hwnd, buf, MSG_SIZ);\r
7227       p = PrevInHistory(buf);\r
7228       if (p != NULL) {\r
7229         SetWindowText(hwnd, p);\r
7230         sel.cpMin = 999999;\r
7231         sel.cpMax = 999999;\r
7232         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7233         return 0;\r
7234       }\r
7235       break;\r
7236     case VK_DOWN:\r
7237       p = NextInHistory();\r
7238       if (p != NULL) {\r
7239         SetWindowText(hwnd, p);\r
7240         sel.cpMin = 999999;\r
7241         sel.cpMax = 999999;\r
7242         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7243         return 0;\r
7244       }\r
7245       break;\r
7246     case VK_HOME:\r
7247     case VK_END:\r
7248       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7249       /* fall thru */\r
7250     case VK_PRIOR:\r
7251     case VK_NEXT:\r
7252       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7253       return 0;\r
7254     }\r
7255     break;\r
7256   case WM_MBUTTONDOWN:\r
7257     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7258       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7259     break;\r
7260   case WM_RBUTTONUP:\r
7261     if (GetKeyState(VK_SHIFT) & ~1) {\r
7262       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7263         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7264     } else {\r
7265       POINT pt;\r
7266       HMENU hmenu;\r
7267       hmenu = LoadMenu(hInst, "InputMenu");\r
7268       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7269       if (sel.cpMin == sel.cpMax) {\r
7270         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7271         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7272       }\r
7273       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7274         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7275       }\r
7276       pt.x = LOWORD(lParam);\r
7277       pt.y = HIWORD(lParam);\r
7278       MenuPopup(hwnd, pt, hmenu, -1);\r
7279     }\r
7280     return 0;\r
7281   case WM_COMMAND:\r
7282     switch (LOWORD(wParam)) { \r
7283     case IDM_Undo:\r
7284       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7285       return 0;\r
7286     case IDM_SelectAll:\r
7287       sel.cpMin = 0;\r
7288       sel.cpMax = -1; /*999999?*/\r
7289       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7290       return 0;\r
7291     case IDM_Cut:\r
7292       SendMessage(hwnd, WM_CUT, 0, 0);\r
7293       return 0;\r
7294     case IDM_Paste:\r
7295       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7296       return 0;\r
7297     case IDM_Copy:\r
7298       SendMessage(hwnd, WM_COPY, 0, 0);\r
7299       return 0;\r
7300     }\r
7301     break;\r
7302   }\r
7303   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7304 }\r
7305 \r
7306 #define CO_MAX  100000\r
7307 #define CO_TRIM   1000\r
7308 \r
7309 LRESULT CALLBACK\r
7310 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7311 {\r
7312   static SnapData sd;\r
7313   HWND hText, hInput;\r
7314   RECT rect;\r
7315   static int sizeX, sizeY;\r
7316   int newSizeX, newSizeY;\r
7317   MINMAXINFO *mmi;\r
7318   WORD wMask;\r
7319 \r
7320   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7321   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7322 \r
7323   switch (message) {\r
7324   case WM_NOTIFY:\r
7325     if (((NMHDR*)lParam)->code == EN_LINK)\r
7326     {\r
7327       ENLINK *pLink = (ENLINK*)lParam;\r
7328       if (pLink->msg == WM_LBUTTONUP)\r
7329       {\r
7330         TEXTRANGE tr;\r
7331 \r
7332         tr.chrg = pLink->chrg;\r
7333         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7334         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7335         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7336         free(tr.lpstrText);\r
7337       }\r
7338     }\r
7339     break;\r
7340   case WM_INITDIALOG: /* message: initialize dialog box */\r
7341     hwndConsole = hDlg;\r
7342     SetFocus(hInput);\r
7343     consoleTextWindowProc = (WNDPROC)\r
7344       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7345     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7346     consoleInputWindowProc = (WNDPROC)\r
7347       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7348     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7349     Colorize(ColorNormal, TRUE);\r
7350     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7351     ChangedConsoleFont();\r
7352     GetClientRect(hDlg, &rect);\r
7353     sizeX = rect.right;\r
7354     sizeY = rect.bottom;\r
7355     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7356         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7357       WINDOWPLACEMENT wp;\r
7358       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7359       wp.length = sizeof(WINDOWPLACEMENT);\r
7360       wp.flags = 0;\r
7361       wp.showCmd = SW_SHOW;\r
7362       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7363       wp.rcNormalPosition.left = wpConsole.x;\r
7364       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7365       wp.rcNormalPosition.top = wpConsole.y;\r
7366       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7367       SetWindowPlacement(hDlg, &wp);\r
7368     }\r
7369 \r
7370    // [HGM] Chessknight's change 2004-07-13\r
7371    else { /* Determine Defaults */\r
7372        WINDOWPLACEMENT wp;\r
7373        wpConsole.x = wpMain.width + 1;\r
7374        wpConsole.y = wpMain.y;\r
7375        wpConsole.width = screenWidth -  wpMain.width;\r
7376        wpConsole.height = wpMain.height;\r
7377        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7378        wp.length = sizeof(WINDOWPLACEMENT);\r
7379        wp.flags = 0;\r
7380        wp.showCmd = SW_SHOW;\r
7381        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7382        wp.rcNormalPosition.left = wpConsole.x;\r
7383        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7384        wp.rcNormalPosition.top = wpConsole.y;\r
7385        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7386        SetWindowPlacement(hDlg, &wp);\r
7387     }\r
7388 \r
7389    // Allow hText to highlight URLs and send notifications on them\r
7390    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7391    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7392    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7393    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7394 \r
7395     return FALSE;\r
7396 \r
7397   case WM_SETFOCUS:\r
7398     SetFocus(hInput);\r
7399     return 0;\r
7400 \r
7401   case WM_CLOSE:\r
7402     ExitEvent(0);\r
7403     /* not reached */\r
7404     break;\r
7405 \r
7406   case WM_SIZE:\r
7407     if (IsIconic(hDlg)) break;\r
7408     newSizeX = LOWORD(lParam);\r
7409     newSizeY = HIWORD(lParam);\r
7410     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7411       RECT rectText, rectInput;\r
7412       POINT pt;\r
7413       int newTextHeight, newTextWidth;\r
7414       GetWindowRect(hText, &rectText);\r
7415       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7416       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7417       if (newTextHeight < 0) {\r
7418         newSizeY += -newTextHeight;\r
7419         newTextHeight = 0;\r
7420       }\r
7421       SetWindowPos(hText, NULL, 0, 0,\r
7422         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7423       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7424       pt.x = rectInput.left;\r
7425       pt.y = rectInput.top + newSizeY - sizeY;\r
7426       ScreenToClient(hDlg, &pt);\r
7427       SetWindowPos(hInput, NULL, \r
7428         pt.x, pt.y, /* needs client coords */   \r
7429         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7430         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7431     }\r
7432     sizeX = newSizeX;\r
7433     sizeY = newSizeY;\r
7434     break;\r
7435 \r
7436   case WM_GETMINMAXINFO:\r
7437     /* Prevent resizing window too small */\r
7438     mmi = (MINMAXINFO *) lParam;\r
7439     mmi->ptMinTrackSize.x = 100;\r
7440     mmi->ptMinTrackSize.y = 100;\r
7441     break;\r
7442 \r
7443   /* [AS] Snapping */\r
7444   case WM_ENTERSIZEMOVE:\r
7445     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7446 \r
7447   case WM_SIZING:\r
7448     return OnSizing( &sd, hDlg, wParam, lParam );\r
7449 \r
7450   case WM_MOVING:\r
7451     return OnMoving( &sd, hDlg, wParam, lParam );\r
7452 \r
7453   case WM_EXITSIZEMOVE:\r
7454         UpdateICSWidth(hText);\r
7455     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7456   }\r
7457 \r
7458   return DefWindowProc(hDlg, message, wParam, lParam);\r
7459 }\r
7460 \r
7461 \r
7462 VOID\r
7463 ConsoleCreate()\r
7464 {\r
7465   HWND hCons;\r
7466   if (hwndConsole) return;\r
7467   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7468   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7469 }\r
7470 \r
7471 \r
7472 VOID\r
7473 ConsoleOutput(char* data, int length, int forceVisible)\r
7474 {\r
7475   HWND hText;\r
7476   int trim, exlen;\r
7477   char *p, *q;\r
7478   char buf[CO_MAX+1];\r
7479   POINT pEnd;\r
7480   RECT rect;\r
7481   static int delayLF = 0;\r
7482   CHARRANGE savesel, sel;\r
7483 \r
7484   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7485   p = data;\r
7486   q = buf;\r
7487   if (delayLF) {\r
7488     *q++ = '\r';\r
7489     *q++ = '\n';\r
7490     delayLF = 0;\r
7491   }\r
7492   while (length--) {\r
7493     if (*p == '\n') {\r
7494       if (*++p) {\r
7495         *q++ = '\r';\r
7496         *q++ = '\n';\r
7497       } else {\r
7498         delayLF = 1;\r
7499       }\r
7500     } else if (*p == '\007') {\r
7501        MyPlaySound(&sounds[(int)SoundBell]);\r
7502        p++;\r
7503     } else {\r
7504       *q++ = *p++;\r
7505     }\r
7506   }\r
7507   *q = NULLCHAR;\r
7508   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7509   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7510   /* Save current selection */\r
7511   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7512   exlen = GetWindowTextLength(hText);\r
7513   /* Find out whether current end of text is visible */\r
7514   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7515   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7516   /* Trim existing text if it's too long */\r
7517   if (exlen + (q - buf) > CO_MAX) {\r
7518     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7519     sel.cpMin = 0;\r
7520     sel.cpMax = trim;\r
7521     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7522     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7523     exlen -= trim;\r
7524     savesel.cpMin -= trim;\r
7525     savesel.cpMax -= trim;\r
7526     if (exlen < 0) exlen = 0;\r
7527     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7528     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7529   }\r
7530   /* Append the new text */\r
7531   sel.cpMin = exlen;\r
7532   sel.cpMax = exlen;\r
7533   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7534   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7535   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7536   if (forceVisible || exlen == 0 ||\r
7537       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7538        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7539     /* Scroll to make new end of text visible if old end of text\r
7540        was visible or new text is an echo of user typein */\r
7541     sel.cpMin = 9999999;\r
7542     sel.cpMax = 9999999;\r
7543     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7544     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7545     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7546     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7547   }\r
7548   if (savesel.cpMax == exlen || forceVisible) {\r
7549     /* Move insert point to new end of text if it was at the old\r
7550        end of text or if the new text is an echo of user typein */\r
7551     sel.cpMin = 9999999;\r
7552     sel.cpMax = 9999999;\r
7553     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7554   } else {\r
7555     /* Restore previous selection */\r
7556     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7557   }\r
7558   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7559 }\r
7560 \r
7561 /*---------*/\r
7562 \r
7563 \r
7564 void\r
7565 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7566 {\r
7567   char buf[100];\r
7568   char *str;\r
7569   COLORREF oldFg, oldBg;\r
7570   HFONT oldFont;\r
7571   RECT rect;\r
7572 \r
7573   if(copyNumber > 1)\r
7574     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7575 \r
7576   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7577   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7578   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7579 \r
7580   rect.left = x;\r
7581   rect.right = x + squareSize;\r
7582   rect.top  = y;\r
7583   rect.bottom = y + squareSize;\r
7584   str = buf;\r
7585 \r
7586   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7587                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7588              y, ETO_CLIPPED|ETO_OPAQUE,\r
7589              &rect, str, strlen(str), NULL);\r
7590 \r
7591   (void) SetTextColor(hdc, oldFg);\r
7592   (void) SetBkColor(hdc, oldBg);\r
7593   (void) SelectObject(hdc, oldFont);\r
7594 }\r
7595 \r
7596 void\r
7597 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7598               RECT *rect, char *color, char *flagFell)\r
7599 {\r
7600   char buf[100];\r
7601   char *str;\r
7602   COLORREF oldFg, oldBg;\r
7603   HFONT oldFont;\r
7604 \r
7605   if (twoBoards && partnerUp) return;\r
7606   if (appData.clockMode) {\r
7607     if (tinyLayout)\r
7608       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7609     else\r
7610       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7611     str = buf;\r
7612   } else {\r
7613     str = color;\r
7614   }\r
7615 \r
7616   if (highlight) {\r
7617     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7618     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7619   } else {\r
7620     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7621     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7622   }\r
7623   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7624 \r
7625   JAWS_SILENCE\r
7626 \r
7627   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7628              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7629              rect, str, strlen(str), NULL);\r
7630   if(logoHeight > 0 && appData.clockMode) {\r
7631       RECT r;\r
7632       str += strlen(color)+2;\r
7633       r.top = rect->top + logoHeight/2;\r
7634       r.left = rect->left;\r
7635       r.right = rect->right;\r
7636       r.bottom = rect->bottom;\r
7637       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7638                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7639                  &r, str, strlen(str), NULL);\r
7640   }\r
7641   (void) SetTextColor(hdc, oldFg);\r
7642   (void) SetBkColor(hdc, oldBg);\r
7643   (void) SelectObject(hdc, oldFont);\r
7644 }\r
7645 \r
7646 \r
7647 int\r
7648 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7649            OVERLAPPED *ovl)\r
7650 {\r
7651   int ok, err;\r
7652 \r
7653   /* [AS]  */\r
7654   if( count <= 0 ) {\r
7655     if (appData.debugMode) {\r
7656       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7657     }\r
7658 \r
7659     return ERROR_INVALID_USER_BUFFER;\r
7660   }\r
7661 \r
7662   ResetEvent(ovl->hEvent);\r
7663   ovl->Offset = ovl->OffsetHigh = 0;\r
7664   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7665   if (ok) {\r
7666     err = NO_ERROR;\r
7667   } else {\r
7668     err = GetLastError();\r
7669     if (err == ERROR_IO_PENDING) {\r
7670       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7671       if (ok)\r
7672         err = NO_ERROR;\r
7673       else\r
7674         err = GetLastError();\r
7675     }\r
7676   }\r
7677   return err;\r
7678 }\r
7679 \r
7680 int\r
7681 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7682             OVERLAPPED *ovl)\r
7683 {\r
7684   int ok, err;\r
7685 \r
7686   ResetEvent(ovl->hEvent);\r
7687   ovl->Offset = ovl->OffsetHigh = 0;\r
7688   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7689   if (ok) {\r
7690     err = NO_ERROR;\r
7691   } else {\r
7692     err = GetLastError();\r
7693     if (err == ERROR_IO_PENDING) {\r
7694       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7695       if (ok)\r
7696         err = NO_ERROR;\r
7697       else\r
7698         err = GetLastError();\r
7699     }\r
7700 \r
7701   }\r
7702   return err;\r
7703 }\r
7704 \r
7705 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7706 void CheckForInputBufferFull( InputSource * is )\r
7707 {\r
7708     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7709         /* Look for end of line */\r
7710         char * p = is->buf;\r
7711         \r
7712         while( p < is->next && *p != '\n' ) {\r
7713             p++;\r
7714         }\r
7715 \r
7716         if( p >= is->next ) {\r
7717             if (appData.debugMode) {\r
7718                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7719             }\r
7720 \r
7721             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7722             is->count = (DWORD) -1;\r
7723             is->next = is->buf;\r
7724         }\r
7725     }\r
7726 }\r
7727 \r
7728 DWORD\r
7729 InputThread(LPVOID arg)\r
7730 {\r
7731   InputSource *is;\r
7732   OVERLAPPED ovl;\r
7733 \r
7734   is = (InputSource *) arg;\r
7735   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7736   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7737   while (is->hThread != NULL) {\r
7738     is->error = DoReadFile(is->hFile, is->next,\r
7739                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7740                            &is->count, &ovl);\r
7741     if (is->error == NO_ERROR) {\r
7742       is->next += is->count;\r
7743     } else {\r
7744       if (is->error == ERROR_BROKEN_PIPE) {\r
7745         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7746         is->count = 0;\r
7747       } else {\r
7748         is->count = (DWORD) -1;\r
7749         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7750         break; \r
7751       }\r
7752     }\r
7753 \r
7754     CheckForInputBufferFull( is );\r
7755 \r
7756     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7757 \r
7758     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7759 \r
7760     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7761   }\r
7762 \r
7763   CloseHandle(ovl.hEvent);\r
7764   CloseHandle(is->hFile);\r
7765 \r
7766   if (appData.debugMode) {\r
7767     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7768   }\r
7769 \r
7770   return 0;\r
7771 }\r
7772 \r
7773 \r
7774 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7775 DWORD\r
7776 NonOvlInputThread(LPVOID arg)\r
7777 {\r
7778   InputSource *is;\r
7779   char *p, *q;\r
7780   int i;\r
7781   char prev;\r
7782 \r
7783   is = (InputSource *) arg;\r
7784   while (is->hThread != NULL) {\r
7785     is->error = ReadFile(is->hFile, is->next,\r
7786                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7787                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7788     if (is->error == NO_ERROR) {\r
7789       /* Change CRLF to LF */\r
7790       if (is->next > is->buf) {\r
7791         p = is->next - 1;\r
7792         i = is->count + 1;\r
7793       } else {\r
7794         p = is->next;\r
7795         i = is->count;\r
7796       }\r
7797       q = p;\r
7798       prev = NULLCHAR;\r
7799       while (i > 0) {\r
7800         if (prev == '\r' && *p == '\n') {\r
7801           *(q-1) = '\n';\r
7802           is->count--;\r
7803         } else { \r
7804           *q++ = *p;\r
7805         }\r
7806         prev = *p++;\r
7807         i--;\r
7808       }\r
7809       *q = NULLCHAR;\r
7810       is->next = q;\r
7811     } else {\r
7812       if (is->error == ERROR_BROKEN_PIPE) {\r
7813         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7814         is->count = 0; \r
7815       } else {\r
7816         is->count = (DWORD) -1;\r
7817       }\r
7818     }\r
7819 \r
7820     CheckForInputBufferFull( is );\r
7821 \r
7822     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7823 \r
7824     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7825 \r
7826     if (is->count < 0) break;  /* Quit on error */\r
7827   }\r
7828   CloseHandle(is->hFile);\r
7829   return 0;\r
7830 }\r
7831 \r
7832 DWORD\r
7833 SocketInputThread(LPVOID arg)\r
7834 {\r
7835   InputSource *is;\r
7836 \r
7837   is = (InputSource *) arg;\r
7838   while (is->hThread != NULL) {\r
7839     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7840     if ((int)is->count == SOCKET_ERROR) {\r
7841       is->count = (DWORD) -1;\r
7842       is->error = WSAGetLastError();\r
7843     } else {\r
7844       is->error = NO_ERROR;\r
7845       is->next += is->count;\r
7846       if (is->count == 0 && is->second == is) {\r
7847         /* End of file on stderr; quit with no message */\r
7848         break;\r
7849       }\r
7850     }\r
7851     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7852 \r
7853     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7854 \r
7855     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7856   }\r
7857   return 0;\r
7858 }\r
7859 \r
7860 VOID\r
7861 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7862 {\r
7863   InputSource *is;\r
7864 \r
7865   is = (InputSource *) lParam;\r
7866   if (is->lineByLine) {\r
7867     /* Feed in lines one by one */\r
7868     char *p = is->buf;\r
7869     char *q = p;\r
7870     while (q < is->next) {\r
7871       if (*q++ == '\n') {\r
7872         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7873         p = q;\r
7874       }\r
7875     }\r
7876     \r
7877     /* Move any partial line to the start of the buffer */\r
7878     q = is->buf;\r
7879     while (p < is->next) {\r
7880       *q++ = *p++;\r
7881     }\r
7882     is->next = q;\r
7883 \r
7884     if (is->error != NO_ERROR || is->count == 0) {\r
7885       /* Notify backend of the error.  Note: If there was a partial\r
7886          line at the end, it is not flushed through. */\r
7887       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7888     }\r
7889   } else {\r
7890     /* Feed in the whole chunk of input at once */\r
7891     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7892     is->next = is->buf;\r
7893   }\r
7894 }\r
7895 \r
7896 /*---------------------------------------------------------------------------*\\r
7897  *\r
7898  *  Menu enables. Used when setting various modes.\r
7899  *\r
7900 \*---------------------------------------------------------------------------*/\r
7901 \r
7902 typedef struct {\r
7903   int item;\r
7904   int flags;\r
7905 } Enables;\r
7906 \r
7907 VOID\r
7908 GreyRevert(Boolean grey)\r
7909 { // [HGM] vari: for retracting variations in local mode\r
7910   HMENU hmenu = GetMenu(hwndMain);\r
7911   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7912   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7913 }\r
7914 \r
7915 VOID\r
7916 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7917 {\r
7918   while (enab->item > 0) {\r
7919     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7920     enab++;\r
7921   }\r
7922 }\r
7923 \r
7924 Enables gnuEnables[] = {\r
7925   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7926   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7927   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7928   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7929   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7938 \r
7939   // Needed to switch from ncp to GNU mode on Engine Load\r
7940   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7941   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7942   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7943   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7944   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7945   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7946   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7948   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7949   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7950   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7951   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7952   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7953   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7954   { -1, -1 }\r
7955 };\r
7956 \r
7957 Enables icsEnables[] = {\r
7958   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7961   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7966   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7974   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7978   { -1, -1 }\r
7979 };\r
7980 \r
7981 #if ZIPPY\r
7982 Enables zippyEnables[] = {\r
7983   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7984   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7987   { -1, -1 }\r
7988 };\r
7989 #endif\r
7990 \r
7991 Enables ncpEnables[] = {\r
7992   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7993   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7994   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7995   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8001   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8002   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8003   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8004   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8005   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8014   { -1, -1 }\r
8015 };\r
8016 \r
8017 Enables trainingOnEnables[] = {\r
8018   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8019   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8020   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8027   { -1, -1 }\r
8028 };\r
8029 \r
8030 Enables trainingOffEnables[] = {\r
8031   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8032   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8033   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8034   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8035   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8040   { -1, -1 }\r
8041 };\r
8042 \r
8043 /* These modify either ncpEnables or gnuEnables */\r
8044 Enables cmailEnables[] = {\r
8045   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8046   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8047   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8048   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8051   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8052   { -1, -1 }\r
8053 };\r
8054 \r
8055 Enables machineThinkingEnables[] = {\r
8056   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8057   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8058   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8059   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8060   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8065   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8066   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8068   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8069 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8071   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8072   { -1, -1 }\r
8073 };\r
8074 \r
8075 Enables userThinkingEnables[] = {\r
8076   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8077   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8078   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8079   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8080   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8081   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8082   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8083   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8084   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8085   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8086   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8087   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8088   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8089 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8090   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8091   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8092   { -1, -1 }\r
8093 };\r
8094 \r
8095 /*---------------------------------------------------------------------------*\\r
8096  *\r
8097  *  Front-end interface functions exported by XBoard.\r
8098  *  Functions appear in same order as prototypes in frontend.h.\r
8099  * \r
8100 \*---------------------------------------------------------------------------*/\r
8101 VOID\r
8102 CheckMark(UINT item, int state)\r
8103 {\r
8104     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8105 }\r
8106 \r
8107 VOID\r
8108 ModeHighlight()\r
8109 {\r
8110   static UINT prevChecked = 0;\r
8111   static int prevPausing = 0;\r
8112   UINT nowChecked;\r
8113 \r
8114   if (pausing != prevPausing) {\r
8115     prevPausing = pausing;\r
8116     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8117                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8118     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8119   }\r
8120 \r
8121   switch (gameMode) {\r
8122   case BeginningOfGame:\r
8123     if (appData.icsActive)\r
8124       nowChecked = IDM_IcsClient;\r
8125     else if (appData.noChessProgram)\r
8126       nowChecked = IDM_EditGame;\r
8127     else\r
8128       nowChecked = IDM_MachineBlack;\r
8129     break;\r
8130   case MachinePlaysBlack:\r
8131     nowChecked = IDM_MachineBlack;\r
8132     break;\r
8133   case MachinePlaysWhite:\r
8134     nowChecked = IDM_MachineWhite;\r
8135     break;\r
8136   case TwoMachinesPlay:\r
8137     nowChecked = IDM_TwoMachines;\r
8138     break;\r
8139   case AnalyzeMode:\r
8140     nowChecked = IDM_AnalysisMode;\r
8141     break;\r
8142   case AnalyzeFile:\r
8143     nowChecked = IDM_AnalyzeFile;\r
8144     break;\r
8145   case EditGame:\r
8146     nowChecked = IDM_EditGame;\r
8147     break;\r
8148   case PlayFromGameFile:\r
8149     nowChecked = IDM_LoadGame;\r
8150     break;\r
8151   case EditPosition:\r
8152     nowChecked = IDM_EditPosition;\r
8153     break;\r
8154   case Training:\r
8155     nowChecked = IDM_Training;\r
8156     break;\r
8157   case IcsPlayingWhite:\r
8158   case IcsPlayingBlack:\r
8159   case IcsObserving:\r
8160   case IcsIdle:\r
8161     nowChecked = IDM_IcsClient;\r
8162     break;\r
8163   default:\r
8164   case EndOfGame:\r
8165     nowChecked = 0;\r
8166     break;\r
8167   }\r
8168   CheckMark(prevChecked, MF_UNCHECKED);\r
8169   CheckMark(nowChecked, MF_CHECKED);\r
8170   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8171 \r
8172   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8173     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8174                           MF_BYCOMMAND|MF_ENABLED);\r
8175   } else {\r
8176     (void) EnableMenuItem(GetMenu(hwndMain), \r
8177                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8178   }\r
8179 \r
8180   prevChecked = nowChecked;\r
8181 \r
8182   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8183   if (appData.icsActive) {\r
8184        if (appData.icsEngineAnalyze) {\r
8185                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8186        } else {\r
8187                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8188        }\r
8189   }\r
8190   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8191 }\r
8192 \r
8193 VOID\r
8194 SetICSMode()\r
8195 {\r
8196   HMENU hmenu = GetMenu(hwndMain);\r
8197   SetMenuEnables(hmenu, icsEnables);\r
8198   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8199     MF_BYCOMMAND|MF_ENABLED);\r
8200 #if ZIPPY\r
8201   if (appData.zippyPlay) {\r
8202     SetMenuEnables(hmenu, zippyEnables);\r
8203     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8204          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8205           MF_BYCOMMAND|MF_ENABLED);\r
8206   }\r
8207 #endif\r
8208 }\r
8209 \r
8210 VOID\r
8211 SetGNUMode()\r
8212 {\r
8213   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8214 }\r
8215 \r
8216 VOID\r
8217 SetNCPMode()\r
8218 {\r
8219   HMENU hmenu = GetMenu(hwndMain);\r
8220   SetMenuEnables(hmenu, ncpEnables);\r
8221     DrawMenuBar(hwndMain);\r
8222 }\r
8223 \r
8224 VOID\r
8225 SetCmailMode()\r
8226 {\r
8227   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8228 }\r
8229 \r
8230 VOID \r
8231 SetTrainingModeOn()\r
8232 {\r
8233   int i;\r
8234   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8235   for (i = 0; i < N_BUTTONS; i++) {\r
8236     if (buttonDesc[i].hwnd != NULL)\r
8237       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8238   }\r
8239   CommentPopDown();\r
8240 }\r
8241 \r
8242 VOID SetTrainingModeOff()\r
8243 {\r
8244   int i;\r
8245   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8246   for (i = 0; i < N_BUTTONS; i++) {\r
8247     if (buttonDesc[i].hwnd != NULL)\r
8248       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8249   }\r
8250 }\r
8251 \r
8252 \r
8253 VOID\r
8254 SetUserThinkingEnables()\r
8255 {\r
8256   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8257 }\r
8258 \r
8259 VOID\r
8260 SetMachineThinkingEnables()\r
8261 {\r
8262   HMENU hMenu = GetMenu(hwndMain);\r
8263   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8264 \r
8265   SetMenuEnables(hMenu, machineThinkingEnables);\r
8266 \r
8267   if (gameMode == MachinePlaysBlack) {\r
8268     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8269   } else if (gameMode == MachinePlaysWhite) {\r
8270     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8271   } else if (gameMode == TwoMachinesPlay) {\r
8272     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8273   }\r
8274 }\r
8275 \r
8276 \r
8277 VOID\r
8278 DisplayTitle(char *str)\r
8279 {\r
8280   char title[MSG_SIZ], *host;\r
8281   if (str[0] != NULLCHAR) {\r
8282     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8283   } else if (appData.icsActive) {\r
8284     if (appData.icsCommPort[0] != NULLCHAR)\r
8285       host = "ICS";\r
8286     else \r
8287       host = appData.icsHost;\r
8288       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8289   } else if (appData.noChessProgram) {\r
8290     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8291   } else {\r
8292     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8293     strcat(title, ": ");\r
8294     strcat(title, first.tidy);\r
8295   }\r
8296   SetWindowText(hwndMain, title);\r
8297 }\r
8298 \r
8299 \r
8300 VOID\r
8301 DisplayMessage(char *str1, char *str2)\r
8302 {\r
8303   HDC hdc;\r
8304   HFONT oldFont;\r
8305   int remain = MESSAGE_TEXT_MAX - 1;\r
8306   int len;\r
8307 \r
8308   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8309   messageText[0] = NULLCHAR;\r
8310   if (*str1) {\r
8311     len = strlen(str1);\r
8312     if (len > remain) len = remain;\r
8313     strncpy(messageText, str1, len);\r
8314     messageText[len] = NULLCHAR;\r
8315     remain -= len;\r
8316   }\r
8317   if (*str2 && remain >= 2) {\r
8318     if (*str1) {\r
8319       strcat(messageText, "  ");\r
8320       remain -= 2;\r
8321     }\r
8322     len = strlen(str2);\r
8323     if (len > remain) len = remain;\r
8324     strncat(messageText, str2, len);\r
8325   }\r
8326   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8327   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8328 \r
8329   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8330 \r
8331   SAYMACHINEMOVE();\r
8332 \r
8333   hdc = GetDC(hwndMain);\r
8334   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8335   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8336              &messageRect, messageText, strlen(messageText), NULL);\r
8337   (void) SelectObject(hdc, oldFont);\r
8338   (void) ReleaseDC(hwndMain, hdc);\r
8339 }\r
8340 \r
8341 VOID\r
8342 DisplayError(char *str, int error)\r
8343 {\r
8344   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8345   int len;\r
8346 \r
8347   if (error == 0) {\r
8348     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8349   } else {\r
8350     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8351                         NULL, error, LANG_NEUTRAL,\r
8352                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8353     if (len > 0) {\r
8354       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8355     } else {\r
8356       ErrorMap *em = errmap;\r
8357       while (em->err != 0 && em->err != error) em++;\r
8358       if (em->err != 0) {\r
8359         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8360       } else {\r
8361         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8362       }\r
8363     }\r
8364   }\r
8365   \r
8366   ErrorPopUp(_("Error"), buf);\r
8367 }\r
8368 \r
8369 \r
8370 VOID\r
8371 DisplayMoveError(char *str)\r
8372 {\r
8373   fromX = fromY = -1;\r
8374   ClearHighlights();\r
8375   DrawPosition(FALSE, NULL);\r
8376   if (appData.popupMoveErrors) {\r
8377     ErrorPopUp(_("Error"), str);\r
8378   } else {\r
8379     DisplayMessage(str, "");\r
8380     moveErrorMessageUp = TRUE;\r
8381   }\r
8382 }\r
8383 \r
8384 VOID\r
8385 DisplayFatalError(char *str, int error, int exitStatus)\r
8386 {\r
8387   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8388   int len;\r
8389   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8390 \r
8391   if (error != 0) {\r
8392     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8393                         NULL, error, LANG_NEUTRAL,\r
8394                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8395     if (len > 0) {\r
8396       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8397     } else {\r
8398       ErrorMap *em = errmap;\r
8399       while (em->err != 0 && em->err != error) em++;\r
8400       if (em->err != 0) {\r
8401         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8402       } else {\r
8403         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8404       }\r
8405     }\r
8406     str = buf;\r
8407   }\r
8408   if (appData.debugMode) {\r
8409     fprintf(debugFP, "%s: %s\n", label, str);\r
8410   }\r
8411   if (appData.popupExitMessage) {\r
8412     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8413                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8414   }\r
8415   ExitEvent(exitStatus);\r
8416 }\r
8417 \r
8418 \r
8419 VOID\r
8420 DisplayInformation(char *str)\r
8421 {\r
8422   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8423 }\r
8424 \r
8425 \r
8426 VOID\r
8427 DisplayNote(char *str)\r
8428 {\r
8429   ErrorPopUp(_("Note"), str);\r
8430 }\r
8431 \r
8432 \r
8433 typedef struct {\r
8434   char *title, *question, *replyPrefix;\r
8435   ProcRef pr;\r
8436 } QuestionParams;\r
8437 \r
8438 LRESULT CALLBACK\r
8439 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8440 {\r
8441   static QuestionParams *qp;\r
8442   char reply[MSG_SIZ];\r
8443   int len, err;\r
8444 \r
8445   switch (message) {\r
8446   case WM_INITDIALOG:\r
8447     qp = (QuestionParams *) lParam;\r
8448     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8449     Translate(hDlg, DLG_Question);\r
8450     SetWindowText(hDlg, qp->title);\r
8451     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8452     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8453     return FALSE;\r
8454 \r
8455   case WM_COMMAND:\r
8456     switch (LOWORD(wParam)) {\r
8457     case IDOK:\r
8458       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8459       if (*reply) strcat(reply, " ");\r
8460       len = strlen(reply);\r
8461       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8462       strcat(reply, "\n");\r
8463       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8464       EndDialog(hDlg, TRUE);\r
8465       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8466       return TRUE;\r
8467     case IDCANCEL:\r
8468       EndDialog(hDlg, FALSE);\r
8469       return TRUE;\r
8470     default:\r
8471       break;\r
8472     }\r
8473     break;\r
8474   }\r
8475   return FALSE;\r
8476 }\r
8477 \r
8478 VOID\r
8479 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8480 {\r
8481     QuestionParams qp;\r
8482     FARPROC lpProc;\r
8483     \r
8484     qp.title = title;\r
8485     qp.question = question;\r
8486     qp.replyPrefix = replyPrefix;\r
8487     qp.pr = pr;\r
8488     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8489     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8490       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8491     FreeProcInstance(lpProc);\r
8492 }\r
8493 \r
8494 /* [AS] Pick FRC position */\r
8495 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8496 {\r
8497     static int * lpIndexFRC;\r
8498     BOOL index_is_ok;\r
8499     char buf[16];\r
8500 \r
8501     switch( message )\r
8502     {\r
8503     case WM_INITDIALOG:\r
8504         lpIndexFRC = (int *) lParam;\r
8505 \r
8506         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8507         Translate(hDlg, DLG_NewGameFRC);\r
8508 \r
8509         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8510         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8511         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8512         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8513 \r
8514         break;\r
8515 \r
8516     case WM_COMMAND:\r
8517         switch( LOWORD(wParam) ) {\r
8518         case IDOK:\r
8519             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8520             EndDialog( hDlg, 0 );\r
8521             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8522             return TRUE;\r
8523         case IDCANCEL:\r
8524             EndDialog( hDlg, 1 );   \r
8525             return TRUE;\r
8526         case IDC_NFG_Edit:\r
8527             if( HIWORD(wParam) == EN_CHANGE ) {\r
8528                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8529 \r
8530                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8531             }\r
8532             return TRUE;\r
8533         case IDC_NFG_Random:\r
8534           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8535             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8536             return TRUE;\r
8537         }\r
8538 \r
8539         break;\r
8540     }\r
8541 \r
8542     return FALSE;\r
8543 }\r
8544 \r
8545 int NewGameFRC()\r
8546 {\r
8547     int result;\r
8548     int index = appData.defaultFrcPosition;\r
8549     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8550 \r
8551     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8552 \r
8553     if( result == 0 ) {\r
8554         appData.defaultFrcPosition = index;\r
8555     }\r
8556 \r
8557     return result;\r
8558 }\r
8559 \r
8560 /* [AS] Game list options. Refactored by HGM */\r
8561 \r
8562 HWND gameListOptionsDialog;\r
8563 \r
8564 // low-level front-end: clear text edit / list widget\r
8565 void\r
8566 \r
8567 GLT_ClearList()\r
8568 {\r
8569     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8570 }\r
8571 \r
8572 // low-level front-end: clear text edit / list widget\r
8573 void\r
8574 GLT_DeSelectList()\r
8575 {\r
8576     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8577 }\r
8578 \r
8579 // low-level front-end: append line to text edit / list widget\r
8580 void\r
8581 GLT_AddToList( char *name )\r
8582 {\r
8583     if( name != 0 ) {\r
8584             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8585     }\r
8586 }\r
8587 \r
8588 // low-level front-end: get line from text edit / list widget\r
8589 Boolean\r
8590 GLT_GetFromList( int index, char *name )\r
8591 {\r
8592     if( name != 0 ) {\r
8593             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8594                 return TRUE;\r
8595     }\r
8596     return FALSE;\r
8597 }\r
8598 \r
8599 void GLT_MoveSelection( HWND hDlg, int delta )\r
8600 {\r
8601     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8602     int idx2 = idx1 + delta;\r
8603     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8604 \r
8605     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8606         char buf[128];\r
8607 \r
8608         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8609         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8610         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8611         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8612     }\r
8613 }\r
8614 \r
8615 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8616 {\r
8617     switch( message )\r
8618     {\r
8619     case WM_INITDIALOG:\r
8620         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8621         \r
8622         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8623         Translate(hDlg, DLG_GameListOptions);\r
8624 \r
8625         /* Initialize list */\r
8626         GLT_TagsToList( lpUserGLT );\r
8627 \r
8628         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8629 \r
8630         break;\r
8631 \r
8632     case WM_COMMAND:\r
8633         switch( LOWORD(wParam) ) {\r
8634         case IDOK:\r
8635             GLT_ParseList();\r
8636             EndDialog( hDlg, 0 );\r
8637             return TRUE;\r
8638         case IDCANCEL:\r
8639             EndDialog( hDlg, 1 );\r
8640             return TRUE;\r
8641 \r
8642         case IDC_GLT_Default:\r
8643             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8644             return TRUE;\r
8645 \r
8646         case IDC_GLT_Restore:\r
8647             GLT_TagsToList( appData.gameListTags );\r
8648             return TRUE;\r
8649 \r
8650         case IDC_GLT_Up:\r
8651             GLT_MoveSelection( hDlg, -1 );\r
8652             return TRUE;\r
8653 \r
8654         case IDC_GLT_Down:\r
8655             GLT_MoveSelection( hDlg, +1 );\r
8656             return TRUE;\r
8657         }\r
8658 \r
8659         break;\r
8660     }\r
8661 \r
8662     return FALSE;\r
8663 }\r
8664 \r
8665 int GameListOptions()\r
8666 {\r
8667     int result;\r
8668     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8669 \r
8670       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8671 \r
8672     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8673 \r
8674     if( result == 0 ) {\r
8675         /* [AS] Memory leak here! */\r
8676         appData.gameListTags = strdup( lpUserGLT ); \r
8677     }\r
8678 \r
8679     return result;\r
8680 }\r
8681 \r
8682 VOID\r
8683 DisplayIcsInteractionTitle(char *str)\r
8684 {\r
8685   char consoleTitle[MSG_SIZ];\r
8686 \r
8687     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8688     SetWindowText(hwndConsole, consoleTitle);\r
8689 \r
8690     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8691       char buf[MSG_SIZ], *p = buf, *q;\r
8692         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8693       do {\r
8694         q = strchr(p, ';');\r
8695         if(q) *q++ = 0;\r
8696         if(*p) ChatPopUp(p);\r
8697       } while(p=q);\r
8698     }\r
8699 \r
8700     SetActiveWindow(hwndMain);\r
8701 }\r
8702 \r
8703 void\r
8704 DrawPosition(int fullRedraw, Board board)\r
8705 {\r
8706   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8707 }\r
8708 \r
8709 void NotifyFrontendLogin()\r
8710 {\r
8711         if (hwndConsole)\r
8712                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8713 }\r
8714 \r
8715 VOID\r
8716 ResetFrontEnd()\r
8717 {\r
8718   fromX = fromY = -1;\r
8719   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8720     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8721     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8722     dragInfo.lastpos = dragInfo.pos;\r
8723     dragInfo.start.x = dragInfo.start.y = -1;\r
8724     dragInfo.from = dragInfo.start;\r
8725     ReleaseCapture();\r
8726     DrawPosition(TRUE, NULL);\r
8727   }\r
8728   TagsPopDown();\r
8729 }\r
8730 \r
8731 \r
8732 VOID\r
8733 CommentPopUp(char *title, char *str)\r
8734 {\r
8735   HWND hwnd = GetActiveWindow();\r
8736   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8737   SAY(str);\r
8738   SetActiveWindow(hwnd);\r
8739 }\r
8740 \r
8741 VOID\r
8742 CommentPopDown(void)\r
8743 {\r
8744   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8745   if (commentDialog) {\r
8746     ShowWindow(commentDialog, SW_HIDE);\r
8747   }\r
8748   commentUp = FALSE;\r
8749 }\r
8750 \r
8751 VOID\r
8752 EditCommentPopUp(int index, char *title, char *str)\r
8753 {\r
8754   EitherCommentPopUp(index, title, str, TRUE);\r
8755 }\r
8756 \r
8757 \r
8758 int\r
8759 Roar()\r
8760 {\r
8761   MyPlaySound(&sounds[(int)SoundRoar]);\r
8762   return 1;\r
8763 }\r
8764 \r
8765 VOID\r
8766 RingBell()\r
8767 {\r
8768   MyPlaySound(&sounds[(int)SoundMove]);\r
8769 }\r
8770 \r
8771 VOID PlayIcsWinSound()\r
8772 {\r
8773   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8774 }\r
8775 \r
8776 VOID PlayIcsLossSound()\r
8777 {\r
8778   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8779 }\r
8780 \r
8781 VOID PlayIcsDrawSound()\r
8782 {\r
8783   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8784 }\r
8785 \r
8786 VOID PlayIcsUnfinishedSound()\r
8787 {\r
8788   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8789 }\r
8790 \r
8791 VOID\r
8792 PlayAlarmSound()\r
8793 {\r
8794   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8795 }\r
8796 \r
8797 VOID\r
8798 PlayTellSound()\r
8799 {\r
8800   MyPlaySound(&textAttribs[ColorTell].sound);\r
8801 }\r
8802 \r
8803 \r
8804 VOID\r
8805 EchoOn()\r
8806 {\r
8807   HWND hInput;\r
8808   consoleEcho = TRUE;\r
8809   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8810   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8811   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8812 }\r
8813 \r
8814 \r
8815 VOID\r
8816 EchoOff()\r
8817 {\r
8818   CHARFORMAT cf;\r
8819   HWND hInput;\r
8820   consoleEcho = FALSE;\r
8821   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8822   /* This works OK: set text and background both to the same color */\r
8823   cf = consoleCF;\r
8824   cf.crTextColor = COLOR_ECHOOFF;\r
8825   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8826   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8827 }\r
8828 \r
8829 /* No Raw()...? */\r
8830 \r
8831 void Colorize(ColorClass cc, int continuation)\r
8832 {\r
8833   currentColorClass = cc;\r
8834   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8835   consoleCF.crTextColor = textAttribs[cc].color;\r
8836   consoleCF.dwEffects = textAttribs[cc].effects;\r
8837   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8838 }\r
8839 \r
8840 char *\r
8841 UserName()\r
8842 {\r
8843   static char buf[MSG_SIZ];\r
8844   DWORD bufsiz = MSG_SIZ;\r
8845 \r
8846   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8847         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8848   }\r
8849   if (!GetUserName(buf, &bufsiz)) {\r
8850     /*DisplayError("Error getting user name", GetLastError());*/\r
8851     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8852   }\r
8853   return buf;\r
8854 }\r
8855 \r
8856 char *\r
8857 HostName()\r
8858 {\r
8859   static char buf[MSG_SIZ];\r
8860   DWORD bufsiz = MSG_SIZ;\r
8861 \r
8862   if (!GetComputerName(buf, &bufsiz)) {\r
8863     /*DisplayError("Error getting host name", GetLastError());*/\r
8864     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8865   }\r
8866   return buf;\r
8867 }\r
8868 \r
8869 \r
8870 int\r
8871 ClockTimerRunning()\r
8872 {\r
8873   return clockTimerEvent != 0;\r
8874 }\r
8875 \r
8876 int\r
8877 StopClockTimer()\r
8878 {\r
8879   if (clockTimerEvent == 0) return FALSE;\r
8880   KillTimer(hwndMain, clockTimerEvent);\r
8881   clockTimerEvent = 0;\r
8882   return TRUE;\r
8883 }\r
8884 \r
8885 void\r
8886 StartClockTimer(long millisec)\r
8887 {\r
8888   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8889                              (UINT) millisec, NULL);\r
8890 }\r
8891 \r
8892 void\r
8893 DisplayWhiteClock(long timeRemaining, int highlight)\r
8894 {\r
8895   HDC hdc;\r
8896   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8897 \r
8898   if(appData.noGUI) return;\r
8899   hdc = GetDC(hwndMain);\r
8900   if (!IsIconic(hwndMain)) {\r
8901     DisplayAClock(hdc, timeRemaining, highlight, \r
8902                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8903   }\r
8904   if (highlight && iconCurrent == iconBlack) {\r
8905     iconCurrent = iconWhite;\r
8906     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8907     if (IsIconic(hwndMain)) {\r
8908       DrawIcon(hdc, 2, 2, iconCurrent);\r
8909     }\r
8910   }\r
8911   (void) ReleaseDC(hwndMain, hdc);\r
8912   if (hwndConsole)\r
8913     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8914 }\r
8915 \r
8916 void\r
8917 DisplayBlackClock(long timeRemaining, int highlight)\r
8918 {\r
8919   HDC hdc;\r
8920   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8921 \r
8922 \r
8923   if(appData.noGUI) return;\r
8924   hdc = GetDC(hwndMain);\r
8925   if (!IsIconic(hwndMain)) {\r
8926     DisplayAClock(hdc, timeRemaining, highlight, \r
8927                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8928   }\r
8929   if (highlight && iconCurrent == iconWhite) {\r
8930     iconCurrent = iconBlack;\r
8931     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8932     if (IsIconic(hwndMain)) {\r
8933       DrawIcon(hdc, 2, 2, iconCurrent);\r
8934     }\r
8935   }\r
8936   (void) ReleaseDC(hwndMain, hdc);\r
8937   if (hwndConsole)\r
8938     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8939 }\r
8940 \r
8941 \r
8942 int\r
8943 LoadGameTimerRunning()\r
8944 {\r
8945   return loadGameTimerEvent != 0;\r
8946 }\r
8947 \r
8948 int\r
8949 StopLoadGameTimer()\r
8950 {\r
8951   if (loadGameTimerEvent == 0) return FALSE;\r
8952   KillTimer(hwndMain, loadGameTimerEvent);\r
8953   loadGameTimerEvent = 0;\r
8954   return TRUE;\r
8955 }\r
8956 \r
8957 void\r
8958 StartLoadGameTimer(long millisec)\r
8959 {\r
8960   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8961                                 (UINT) millisec, NULL);\r
8962 }\r
8963 \r
8964 void\r
8965 AutoSaveGame()\r
8966 {\r
8967   char *defName;\r
8968   FILE *f;\r
8969   char fileTitle[MSG_SIZ];\r
8970 \r
8971   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8972   f = OpenFileDialog(hwndMain, "a", defName,\r
8973                      appData.oldSaveStyle ? "gam" : "pgn",\r
8974                      GAME_FILT, \r
8975                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8976   if (f != NULL) {\r
8977     SaveGame(f, 0, "");\r
8978     fclose(f);\r
8979   }\r
8980 }\r
8981 \r
8982 \r
8983 void\r
8984 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8985 {\r
8986   if (delayedTimerEvent != 0) {\r
8987     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8988       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8989     }\r
8990     KillTimer(hwndMain, delayedTimerEvent);\r
8991     delayedTimerEvent = 0;\r
8992     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8993     delayedTimerCallback();\r
8994   }\r
8995   delayedTimerCallback = cb;\r
8996   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8997                                 (UINT) millisec, NULL);\r
8998 }\r
8999 \r
9000 DelayedEventCallback\r
9001 GetDelayedEvent()\r
9002 {\r
9003   if (delayedTimerEvent) {\r
9004     return delayedTimerCallback;\r
9005   } else {\r
9006     return NULL;\r
9007   }\r
9008 }\r
9009 \r
9010 void\r
9011 CancelDelayedEvent()\r
9012 {\r
9013   if (delayedTimerEvent) {\r
9014     KillTimer(hwndMain, delayedTimerEvent);\r
9015     delayedTimerEvent = 0;\r
9016   }\r
9017 }\r
9018 \r
9019 DWORD GetWin32Priority(int nice)\r
9020 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9021 /*\r
9022 REALTIME_PRIORITY_CLASS     0x00000100\r
9023 HIGH_PRIORITY_CLASS         0x00000080\r
9024 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9025 NORMAL_PRIORITY_CLASS       0x00000020\r
9026 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9027 IDLE_PRIORITY_CLASS         0x00000040\r
9028 */\r
9029         if (nice < -15) return 0x00000080;\r
9030         if (nice < 0)   return 0x00008000;\r
9031         if (nice == 0)  return 0x00000020;\r
9032         if (nice < 15)  return 0x00004000;\r
9033         return 0x00000040;\r
9034 }\r
9035 \r
9036 void RunCommand(char *cmdLine)\r
9037 {\r
9038   /* Now create the child process. */\r
9039   STARTUPINFO siStartInfo;\r
9040   PROCESS_INFORMATION piProcInfo;\r
9041 \r
9042   siStartInfo.cb = sizeof(STARTUPINFO);\r
9043   siStartInfo.lpReserved = NULL;\r
9044   siStartInfo.lpDesktop = NULL;\r
9045   siStartInfo.lpTitle = NULL;\r
9046   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9047   siStartInfo.cbReserved2 = 0;\r
9048   siStartInfo.lpReserved2 = NULL;\r
9049   siStartInfo.hStdInput = NULL;\r
9050   siStartInfo.hStdOutput = NULL;\r
9051   siStartInfo.hStdError = NULL;\r
9052 \r
9053   CreateProcess(NULL,\r
9054                 cmdLine,           /* command line */\r
9055                 NULL,      /* process security attributes */\r
9056                 NULL,      /* primary thread security attrs */\r
9057                 TRUE,      /* handles are inherited */\r
9058                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9059                 NULL,      /* use parent's environment */\r
9060                 NULL,\r
9061                 &siStartInfo, /* STARTUPINFO pointer */\r
9062                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9063 \r
9064   CloseHandle(piProcInfo.hThread);\r
9065 }\r
9066 \r
9067 /* Start a child process running the given program.\r
9068    The process's standard output can be read from "from", and its\r
9069    standard input can be written to "to".\r
9070    Exit with fatal error if anything goes wrong.\r
9071    Returns an opaque pointer that can be used to destroy the process\r
9072    later.\r
9073 */\r
9074 int\r
9075 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9076 {\r
9077 #define BUFSIZE 4096\r
9078 \r
9079   HANDLE hChildStdinRd, hChildStdinWr,\r
9080     hChildStdoutRd, hChildStdoutWr;\r
9081   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9082   SECURITY_ATTRIBUTES saAttr;\r
9083   BOOL fSuccess;\r
9084   PROCESS_INFORMATION piProcInfo;\r
9085   STARTUPINFO siStartInfo;\r
9086   ChildProc *cp;\r
9087   char buf[MSG_SIZ];\r
9088   DWORD err;\r
9089 \r
9090   if (appData.debugMode) {\r
9091     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9092   }\r
9093 \r
9094   *pr = NoProc;\r
9095 \r
9096   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9097   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9098   saAttr.bInheritHandle = TRUE;\r
9099   saAttr.lpSecurityDescriptor = NULL;\r
9100 \r
9101   /*\r
9102    * The steps for redirecting child's STDOUT:\r
9103    *     1. Create anonymous pipe to be STDOUT for child.\r
9104    *     2. Create a noninheritable duplicate of read handle,\r
9105    *         and close the inheritable read handle.\r
9106    */\r
9107 \r
9108   /* Create a pipe for the child's STDOUT. */\r
9109   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9110     return GetLastError();\r
9111   }\r
9112 \r
9113   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9114   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9115                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9116                              FALSE,     /* not inherited */\r
9117                              DUPLICATE_SAME_ACCESS);\r
9118   if (! fSuccess) {\r
9119     return GetLastError();\r
9120   }\r
9121   CloseHandle(hChildStdoutRd);\r
9122 \r
9123   /*\r
9124    * The steps for redirecting child's STDIN:\r
9125    *     1. Create anonymous pipe to be STDIN for child.\r
9126    *     2. Create a noninheritable duplicate of write handle,\r
9127    *         and close the inheritable write handle.\r
9128    */\r
9129 \r
9130   /* Create a pipe for the child's STDIN. */\r
9131   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9132     return GetLastError();\r
9133   }\r
9134 \r
9135   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9136   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9137                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9138                              FALSE,     /* not inherited */\r
9139                              DUPLICATE_SAME_ACCESS);\r
9140   if (! fSuccess) {\r
9141     return GetLastError();\r
9142   }\r
9143   CloseHandle(hChildStdinWr);\r
9144 \r
9145   /* Arrange to (1) look in dir for the child .exe file, and\r
9146    * (2) have dir be the child's working directory.  Interpret\r
9147    * dir relative to the directory WinBoard loaded from. */\r
9148   GetCurrentDirectory(MSG_SIZ, buf);\r
9149   SetCurrentDirectory(installDir);\r
9150   SetCurrentDirectory(dir);\r
9151 \r
9152   /* Now create the child process. */\r
9153 \r
9154   siStartInfo.cb = sizeof(STARTUPINFO);\r
9155   siStartInfo.lpReserved = NULL;\r
9156   siStartInfo.lpDesktop = NULL;\r
9157   siStartInfo.lpTitle = NULL;\r
9158   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9159   siStartInfo.cbReserved2 = 0;\r
9160   siStartInfo.lpReserved2 = NULL;\r
9161   siStartInfo.hStdInput = hChildStdinRd;\r
9162   siStartInfo.hStdOutput = hChildStdoutWr;\r
9163   siStartInfo.hStdError = hChildStdoutWr;\r
9164 \r
9165   fSuccess = CreateProcess(NULL,\r
9166                            cmdLine,        /* command line */\r
9167                            NULL,           /* process security attributes */\r
9168                            NULL,           /* primary thread security attrs */\r
9169                            TRUE,           /* handles are inherited */\r
9170                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9171                            NULL,           /* use parent's environment */\r
9172                            NULL,\r
9173                            &siStartInfo, /* STARTUPINFO pointer */\r
9174                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9175 \r
9176   err = GetLastError();\r
9177   SetCurrentDirectory(buf); /* return to prev directory */\r
9178   if (! fSuccess) {\r
9179     return err;\r
9180   }\r
9181 \r
9182   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9183     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9184     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9185   }\r
9186 \r
9187   /* Close the handles we don't need in the parent */\r
9188   CloseHandle(piProcInfo.hThread);\r
9189   CloseHandle(hChildStdinRd);\r
9190   CloseHandle(hChildStdoutWr);\r
9191 \r
9192   /* Prepare return value */\r
9193   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9194   cp->kind = CPReal;\r
9195   cp->hProcess = piProcInfo.hProcess;\r
9196   cp->pid = piProcInfo.dwProcessId;\r
9197   cp->hFrom = hChildStdoutRdDup;\r
9198   cp->hTo = hChildStdinWrDup;\r
9199 \r
9200   *pr = (void *) cp;\r
9201 \r
9202   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9203      2000 where engines sometimes don't see the initial command(s)\r
9204      from WinBoard and hang.  I don't understand how that can happen,\r
9205      but the Sleep is harmless, so I've put it in.  Others have also\r
9206      reported what may be the same problem, so hopefully this will fix\r
9207      it for them too.  */\r
9208   Sleep(500);\r
9209 \r
9210   return NO_ERROR;\r
9211 }\r
9212 \r
9213 \r
9214 void\r
9215 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9216 {\r
9217   ChildProc *cp; int result;\r
9218 \r
9219   cp = (ChildProc *) pr;\r
9220   if (cp == NULL) return;\r
9221 \r
9222   switch (cp->kind) {\r
9223   case CPReal:\r
9224     /* TerminateProcess is considered harmful, so... */\r
9225     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9226     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9227     /* The following doesn't work because the chess program\r
9228        doesn't "have the same console" as WinBoard.  Maybe\r
9229        we could arrange for this even though neither WinBoard\r
9230        nor the chess program uses a console for stdio? */\r
9231     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9232 \r
9233     /* [AS] Special termination modes for misbehaving programs... */\r
9234     if( signal & 8 ) { \r
9235         result = TerminateProcess( cp->hProcess, 0 );\r
9236 \r
9237         if ( appData.debugMode) {\r
9238             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9239         }\r
9240     }\r
9241     else if( signal & 4 ) {\r
9242         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9243 \r
9244         if( dw != WAIT_OBJECT_0 ) {\r
9245             result = TerminateProcess( cp->hProcess, 0 );\r
9246 \r
9247             if ( appData.debugMode) {\r
9248                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9249             }\r
9250 \r
9251         }\r
9252     }\r
9253 \r
9254     CloseHandle(cp->hProcess);\r
9255     break;\r
9256 \r
9257   case CPComm:\r
9258     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9259     break;\r
9260 \r
9261   case CPSock:\r
9262     closesocket(cp->sock);\r
9263     WSACleanup();\r
9264     break;\r
9265 \r
9266   case CPRcmd:\r
9267     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9268     closesocket(cp->sock);\r
9269     closesocket(cp->sock2);\r
9270     WSACleanup();\r
9271     break;\r
9272   }\r
9273   free(cp);\r
9274 }\r
9275 \r
9276 void\r
9277 InterruptChildProcess(ProcRef pr)\r
9278 {\r
9279   ChildProc *cp;\r
9280 \r
9281   cp = (ChildProc *) pr;\r
9282   if (cp == NULL) return;\r
9283   switch (cp->kind) {\r
9284   case CPReal:\r
9285     /* The following doesn't work because the chess program\r
9286        doesn't "have the same console" as WinBoard.  Maybe\r
9287        we could arrange for this even though neither WinBoard\r
9288        nor the chess program uses a console for stdio */\r
9289     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9290     break;\r
9291 \r
9292   case CPComm:\r
9293   case CPSock:\r
9294     /* Can't interrupt */\r
9295     break;\r
9296 \r
9297   case CPRcmd:\r
9298     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9299     break;\r
9300   }\r
9301 }\r
9302 \r
9303 \r
9304 int\r
9305 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9306 {\r
9307   char cmdLine[MSG_SIZ];\r
9308 \r
9309   if (port[0] == NULLCHAR) {\r
9310     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9311   } else {\r
9312     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9313   }\r
9314   return StartChildProcess(cmdLine, "", pr);\r
9315 }\r
9316 \r
9317 \r
9318 /* Code to open TCP sockets */\r
9319 \r
9320 int\r
9321 OpenTCP(char *host, char *port, ProcRef *pr)\r
9322 {\r
9323   ChildProc *cp;\r
9324   int err;\r
9325   SOCKET s;\r
9326 \r
9327   struct sockaddr_in sa, mysa;\r
9328   struct hostent FAR *hp;\r
9329   unsigned short uport;\r
9330   WORD wVersionRequested;\r
9331   WSADATA wsaData;\r
9332 \r
9333   /* Initialize socket DLL */\r
9334   wVersionRequested = MAKEWORD(1, 1);\r
9335   err = WSAStartup(wVersionRequested, &wsaData);\r
9336   if (err != 0) return err;\r
9337 \r
9338   /* Make socket */\r
9339   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9340     err = WSAGetLastError();\r
9341     WSACleanup();\r
9342     return err;\r
9343   }\r
9344 \r
9345   /* Bind local address using (mostly) don't-care values.\r
9346    */\r
9347   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9348   mysa.sin_family = AF_INET;\r
9349   mysa.sin_addr.s_addr = INADDR_ANY;\r
9350   uport = (unsigned short) 0;\r
9351   mysa.sin_port = htons(uport);\r
9352   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9353       == SOCKET_ERROR) {\r
9354     err = WSAGetLastError();\r
9355     WSACleanup();\r
9356     return err;\r
9357   }\r
9358 \r
9359   /* Resolve remote host name */\r
9360   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9361   if (!(hp = gethostbyname(host))) {\r
9362     unsigned int b0, b1, b2, b3;\r
9363 \r
9364     err = WSAGetLastError();\r
9365 \r
9366     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9367       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9368       hp->h_addrtype = AF_INET;\r
9369       hp->h_length = 4;\r
9370       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9371       hp->h_addr_list[0] = (char *) malloc(4);\r
9372       hp->h_addr_list[0][0] = (char) b0;\r
9373       hp->h_addr_list[0][1] = (char) b1;\r
9374       hp->h_addr_list[0][2] = (char) b2;\r
9375       hp->h_addr_list[0][3] = (char) b3;\r
9376     } else {\r
9377       WSACleanup();\r
9378       return err;\r
9379     }\r
9380   }\r
9381   sa.sin_family = hp->h_addrtype;\r
9382   uport = (unsigned short) atoi(port);\r
9383   sa.sin_port = htons(uport);\r
9384   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9385 \r
9386   /* Make connection */\r
9387   if (connect(s, (struct sockaddr *) &sa,\r
9388               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9389     err = WSAGetLastError();\r
9390     WSACleanup();\r
9391     return err;\r
9392   }\r
9393 \r
9394   /* Prepare return value */\r
9395   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9396   cp->kind = CPSock;\r
9397   cp->sock = s;\r
9398   *pr = (ProcRef *) cp;\r
9399 \r
9400   return NO_ERROR;\r
9401 }\r
9402 \r
9403 int\r
9404 OpenCommPort(char *name, ProcRef *pr)\r
9405 {\r
9406   HANDLE h;\r
9407   COMMTIMEOUTS ct;\r
9408   ChildProc *cp;\r
9409   char fullname[MSG_SIZ];\r
9410 \r
9411   if (*name != '\\')\r
9412     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9413   else\r
9414     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9415 \r
9416   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9417                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9418   if (h == (HANDLE) -1) {\r
9419     return GetLastError();\r
9420   }\r
9421   hCommPort = h;\r
9422 \r
9423   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9424 \r
9425   /* Accumulate characters until a 100ms pause, then parse */\r
9426   ct.ReadIntervalTimeout = 100;\r
9427   ct.ReadTotalTimeoutMultiplier = 0;\r
9428   ct.ReadTotalTimeoutConstant = 0;\r
9429   ct.WriteTotalTimeoutMultiplier = 0;\r
9430   ct.WriteTotalTimeoutConstant = 0;\r
9431   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9432 \r
9433   /* Prepare return value */\r
9434   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9435   cp->kind = CPComm;\r
9436   cp->hFrom = h;\r
9437   cp->hTo = h;\r
9438   *pr = (ProcRef *) cp;\r
9439 \r
9440   return NO_ERROR;\r
9441 }\r
9442 \r
9443 int\r
9444 OpenLoopback(ProcRef *pr)\r
9445 {\r
9446   DisplayFatalError(_("Not implemented"), 0, 1);\r
9447   return NO_ERROR;\r
9448 }\r
9449 \r
9450 \r
9451 int\r
9452 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9453 {\r
9454   ChildProc *cp;\r
9455   int err;\r
9456   SOCKET s, s2, s3;\r
9457   struct sockaddr_in sa, mysa;\r
9458   struct hostent FAR *hp;\r
9459   unsigned short uport;\r
9460   WORD wVersionRequested;\r
9461   WSADATA wsaData;\r
9462   int fromPort;\r
9463   char stderrPortStr[MSG_SIZ];\r
9464 \r
9465   /* Initialize socket DLL */\r
9466   wVersionRequested = MAKEWORD(1, 1);\r
9467   err = WSAStartup(wVersionRequested, &wsaData);\r
9468   if (err != 0) return err;\r
9469 \r
9470   /* Resolve remote host name */\r
9471   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9472   if (!(hp = gethostbyname(host))) {\r
9473     unsigned int b0, b1, b2, b3;\r
9474 \r
9475     err = WSAGetLastError();\r
9476 \r
9477     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9478       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9479       hp->h_addrtype = AF_INET;\r
9480       hp->h_length = 4;\r
9481       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9482       hp->h_addr_list[0] = (char *) malloc(4);\r
9483       hp->h_addr_list[0][0] = (char) b0;\r
9484       hp->h_addr_list[0][1] = (char) b1;\r
9485       hp->h_addr_list[0][2] = (char) b2;\r
9486       hp->h_addr_list[0][3] = (char) b3;\r
9487     } else {\r
9488       WSACleanup();\r
9489       return err;\r
9490     }\r
9491   }\r
9492   sa.sin_family = hp->h_addrtype;\r
9493   uport = (unsigned short) 514;\r
9494   sa.sin_port = htons(uport);\r
9495   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9496 \r
9497   /* Bind local socket to unused "privileged" port address\r
9498    */\r
9499   s = INVALID_SOCKET;\r
9500   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9501   mysa.sin_family = AF_INET;\r
9502   mysa.sin_addr.s_addr = INADDR_ANY;\r
9503   for (fromPort = 1023;; fromPort--) {\r
9504     if (fromPort < 0) {\r
9505       WSACleanup();\r
9506       return WSAEADDRINUSE;\r
9507     }\r
9508     if (s == INVALID_SOCKET) {\r
9509       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9510         err = WSAGetLastError();\r
9511         WSACleanup();\r
9512         return err;\r
9513       }\r
9514     }\r
9515     uport = (unsigned short) fromPort;\r
9516     mysa.sin_port = htons(uport);\r
9517     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9518         == SOCKET_ERROR) {\r
9519       err = WSAGetLastError();\r
9520       if (err == WSAEADDRINUSE) continue;\r
9521       WSACleanup();\r
9522       return err;\r
9523     }\r
9524     if (connect(s, (struct sockaddr *) &sa,\r
9525       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9526       err = WSAGetLastError();\r
9527       if (err == WSAEADDRINUSE) {\r
9528         closesocket(s);\r
9529         s = -1;\r
9530         continue;\r
9531       }\r
9532       WSACleanup();\r
9533       return err;\r
9534     }\r
9535     break;\r
9536   }\r
9537 \r
9538   /* Bind stderr local socket to unused "privileged" port address\r
9539    */\r
9540   s2 = INVALID_SOCKET;\r
9541   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9542   mysa.sin_family = AF_INET;\r
9543   mysa.sin_addr.s_addr = INADDR_ANY;\r
9544   for (fromPort = 1023;; fromPort--) {\r
9545     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9546     if (fromPort < 0) {\r
9547       (void) closesocket(s);\r
9548       WSACleanup();\r
9549       return WSAEADDRINUSE;\r
9550     }\r
9551     if (s2 == INVALID_SOCKET) {\r
9552       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9553         err = WSAGetLastError();\r
9554         closesocket(s);\r
9555         WSACleanup();\r
9556         return err;\r
9557       }\r
9558     }\r
9559     uport = (unsigned short) fromPort;\r
9560     mysa.sin_port = htons(uport);\r
9561     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9562         == SOCKET_ERROR) {\r
9563       err = WSAGetLastError();\r
9564       if (err == WSAEADDRINUSE) continue;\r
9565       (void) closesocket(s);\r
9566       WSACleanup();\r
9567       return err;\r
9568     }\r
9569     if (listen(s2, 1) == SOCKET_ERROR) {\r
9570       err = WSAGetLastError();\r
9571       if (err == WSAEADDRINUSE) {\r
9572         closesocket(s2);\r
9573         s2 = INVALID_SOCKET;\r
9574         continue;\r
9575       }\r
9576       (void) closesocket(s);\r
9577       (void) closesocket(s2);\r
9578       WSACleanup();\r
9579       return err;\r
9580     }\r
9581     break;\r
9582   }\r
9583   prevStderrPort = fromPort; // remember port used\r
9584   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9585 \r
9586   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9587     err = WSAGetLastError();\r
9588     (void) closesocket(s);\r
9589     (void) closesocket(s2);\r
9590     WSACleanup();\r
9591     return err;\r
9592   }\r
9593 \r
9594   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9595     err = WSAGetLastError();\r
9596     (void) closesocket(s);\r
9597     (void) closesocket(s2);\r
9598     WSACleanup();\r
9599     return err;\r
9600   }\r
9601   if (*user == NULLCHAR) user = UserName();\r
9602   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9603     err = WSAGetLastError();\r
9604     (void) closesocket(s);\r
9605     (void) closesocket(s2);\r
9606     WSACleanup();\r
9607     return err;\r
9608   }\r
9609   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9610     err = WSAGetLastError();\r
9611     (void) closesocket(s);\r
9612     (void) closesocket(s2);\r
9613     WSACleanup();\r
9614     return err;\r
9615   }\r
9616 \r
9617   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9618     err = WSAGetLastError();\r
9619     (void) closesocket(s);\r
9620     (void) closesocket(s2);\r
9621     WSACleanup();\r
9622     return err;\r
9623   }\r
9624   (void) closesocket(s2);  /* Stop listening */\r
9625 \r
9626   /* Prepare return value */\r
9627   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9628   cp->kind = CPRcmd;\r
9629   cp->sock = s;\r
9630   cp->sock2 = s3;\r
9631   *pr = (ProcRef *) cp;\r
9632 \r
9633   return NO_ERROR;\r
9634 }\r
9635 \r
9636 \r
9637 InputSourceRef\r
9638 AddInputSource(ProcRef pr, int lineByLine,\r
9639                InputCallback func, VOIDSTAR closure)\r
9640 {\r
9641   InputSource *is, *is2 = NULL;\r
9642   ChildProc *cp = (ChildProc *) pr;\r
9643 \r
9644   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9645   is->lineByLine = lineByLine;\r
9646   is->func = func;\r
9647   is->closure = closure;\r
9648   is->second = NULL;\r
9649   is->next = is->buf;\r
9650   if (pr == NoProc) {\r
9651     is->kind = CPReal;\r
9652     consoleInputSource = is;\r
9653   } else {\r
9654     is->kind = cp->kind;\r
9655     /* \r
9656         [AS] Try to avoid a race condition if the thread is given control too early:\r
9657         we create all threads suspended so that the is->hThread variable can be\r
9658         safely assigned, then let the threads start with ResumeThread.\r
9659     */\r
9660     switch (cp->kind) {\r
9661     case CPReal:\r
9662       is->hFile = cp->hFrom;\r
9663       cp->hFrom = NULL; /* now owned by InputThread */\r
9664       is->hThread =\r
9665         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9666                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9667       break;\r
9668 \r
9669     case CPComm:\r
9670       is->hFile = cp->hFrom;\r
9671       cp->hFrom = NULL; /* now owned by InputThread */\r
9672       is->hThread =\r
9673         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9674                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9675       break;\r
9676 \r
9677     case CPSock:\r
9678       is->sock = cp->sock;\r
9679       is->hThread =\r
9680         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9681                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9682       break;\r
9683 \r
9684     case CPRcmd:\r
9685       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9686       *is2 = *is;\r
9687       is->sock = cp->sock;\r
9688       is->second = is2;\r
9689       is2->sock = cp->sock2;\r
9690       is2->second = is2;\r
9691       is->hThread =\r
9692         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9693                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9694       is2->hThread =\r
9695         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9696                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9697       break;\r
9698     }\r
9699 \r
9700     if( is->hThread != NULL ) {\r
9701         ResumeThread( is->hThread );\r
9702     }\r
9703 \r
9704     if( is2 != NULL && is2->hThread != NULL ) {\r
9705         ResumeThread( is2->hThread );\r
9706     }\r
9707   }\r
9708 \r
9709   return (InputSourceRef) is;\r
9710 }\r
9711 \r
9712 void\r
9713 RemoveInputSource(InputSourceRef isr)\r
9714 {\r
9715   InputSource *is;\r
9716 \r
9717   is = (InputSource *) isr;\r
9718   is->hThread = NULL;  /* tell thread to stop */\r
9719   CloseHandle(is->hThread);\r
9720   if (is->second != NULL) {\r
9721     is->second->hThread = NULL;\r
9722     CloseHandle(is->second->hThread);\r
9723   }\r
9724 }\r
9725 \r
9726 int no_wrap(char *message, int count)\r
9727 {\r
9728     ConsoleOutput(message, count, FALSE);\r
9729     return count;\r
9730 }\r
9731 \r
9732 int\r
9733 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9734 {\r
9735   DWORD dOutCount;\r
9736   int outCount = SOCKET_ERROR;\r
9737   ChildProc *cp = (ChildProc *) pr;\r
9738   static OVERLAPPED ovl;\r
9739   static int line = 0;\r
9740 \r
9741   if (pr == NoProc)\r
9742   {\r
9743     if (appData.noJoin || !appData.useInternalWrap)\r
9744       return no_wrap(message, count);\r
9745     else\r
9746     {\r
9747       int width = get_term_width();\r
9748       int len = wrap(NULL, message, count, width, &line);\r
9749       char *msg = malloc(len);\r
9750       int dbgchk;\r
9751 \r
9752       if (!msg)\r
9753         return no_wrap(message, count);\r
9754       else\r
9755       {\r
9756         dbgchk = wrap(msg, message, count, width, &line);\r
9757         if (dbgchk != len && appData.debugMode)\r
9758             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9759         ConsoleOutput(msg, len, FALSE);\r
9760         free(msg);\r
9761         return len;\r
9762       }\r
9763     }\r
9764   }\r
9765 \r
9766   if (ovl.hEvent == NULL) {\r
9767     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9768   }\r
9769   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9770 \r
9771   switch (cp->kind) {\r
9772   case CPSock:\r
9773   case CPRcmd:\r
9774     outCount = send(cp->sock, message, count, 0);\r
9775     if (outCount == SOCKET_ERROR) {\r
9776       *outError = WSAGetLastError();\r
9777     } else {\r
9778       *outError = NO_ERROR;\r
9779     }\r
9780     break;\r
9781 \r
9782   case CPReal:\r
9783     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9784                   &dOutCount, NULL)) {\r
9785       *outError = NO_ERROR;\r
9786       outCount = (int) dOutCount;\r
9787     } else {\r
9788       *outError = GetLastError();\r
9789     }\r
9790     break;\r
9791 \r
9792   case CPComm:\r
9793     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9794                             &dOutCount, &ovl);\r
9795     if (*outError == NO_ERROR) {\r
9796       outCount = (int) dOutCount;\r
9797     }\r
9798     break;\r
9799   }\r
9800   return outCount;\r
9801 }\r
9802 \r
9803 void\r
9804 DoSleep(int n)\r
9805 {\r
9806     if(n != 0) Sleep(n);\r
9807 }\r
9808 \r
9809 int\r
9810 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9811                        long msdelay)\r
9812 {\r
9813   /* Ignore delay, not implemented for WinBoard */\r
9814   return OutputToProcess(pr, message, count, outError);\r
9815 }\r
9816 \r
9817 \r
9818 void\r
9819 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9820                         char *buf, int count, int error)\r
9821 {\r
9822   DisplayFatalError(_("Not implemented"), 0, 1);\r
9823 }\r
9824 \r
9825 /* see wgamelist.c for Game List functions */\r
9826 /* see wedittags.c for Edit Tags functions */\r
9827 \r
9828 \r
9829 int\r
9830 ICSInitScript()\r
9831 {\r
9832   FILE *f;\r
9833   char buf[MSG_SIZ];\r
9834   char *dummy;\r
9835 \r
9836   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9837     f = fopen(buf, "r");\r
9838     if (f != NULL) {\r
9839       ProcessICSInitScript(f);\r
9840       fclose(f);\r
9841       return TRUE;\r
9842     }\r
9843   }\r
9844   return FALSE;\r
9845 }\r
9846 \r
9847 \r
9848 VOID\r
9849 StartAnalysisClock()\r
9850 {\r
9851   if (analysisTimerEvent) return;\r
9852   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9853                                         (UINT) 2000, NULL);\r
9854 }\r
9855 \r
9856 VOID\r
9857 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9858 {\r
9859   highlightInfo.sq[0].x = fromX;\r
9860   highlightInfo.sq[0].y = fromY;\r
9861   highlightInfo.sq[1].x = toX;\r
9862   highlightInfo.sq[1].y = toY;\r
9863 }\r
9864 \r
9865 VOID\r
9866 ClearHighlights()\r
9867 {\r
9868   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9869     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9870 }\r
9871 \r
9872 VOID\r
9873 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9874 {\r
9875   premoveHighlightInfo.sq[0].x = fromX;\r
9876   premoveHighlightInfo.sq[0].y = fromY;\r
9877   premoveHighlightInfo.sq[1].x = toX;\r
9878   premoveHighlightInfo.sq[1].y = toY;\r
9879 }\r
9880 \r
9881 VOID\r
9882 ClearPremoveHighlights()\r
9883 {\r
9884   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9885     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9886 }\r
9887 \r
9888 VOID\r
9889 ShutDownFrontEnd()\r
9890 {\r
9891   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9892   DeleteClipboardTempFiles();\r
9893 }\r
9894 \r
9895 void\r
9896 BoardToTop()\r
9897 {\r
9898     if (IsIconic(hwndMain))\r
9899       ShowWindow(hwndMain, SW_RESTORE);\r
9900 \r
9901     SetActiveWindow(hwndMain);\r
9902 }\r
9903 \r
9904 /*\r
9905  * Prototypes for animation support routines\r
9906  */\r
9907 static void ScreenSquare(int column, int row, POINT * pt);\r
9908 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9909      POINT frames[], int * nFrames);\r
9910 \r
9911 \r
9912 #define kFactor 4\r
9913 \r
9914 void\r
9915 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9916 {       // [HGM] atomic: animate blast wave\r
9917         int i;\r
9918 \r
9919         explodeInfo.fromX = fromX;\r
9920         explodeInfo.fromY = fromY;\r
9921         explodeInfo.toX = toX;\r
9922         explodeInfo.toY = toY;\r
9923         for(i=1; i<4*kFactor; i++) {\r
9924             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9925             DrawPosition(FALSE, board);\r
9926             Sleep(appData.animSpeed);\r
9927         }\r
9928         explodeInfo.radius = 0;\r
9929         DrawPosition(TRUE, board);\r
9930 }\r
9931 \r
9932 void\r
9933 AnimateMove(board, fromX, fromY, toX, toY)\r
9934      Board board;\r
9935      int fromX;\r
9936      int fromY;\r
9937      int toX;\r
9938      int toY;\r
9939 {\r
9940   ChessSquare piece;\r
9941   int x = toX, y = toY;\r
9942   POINT start, finish, mid;\r
9943   POINT frames[kFactor * 2 + 1];\r
9944   int nFrames, n;\r
9945 \r
9946   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9947 \r
9948   if (!appData.animate) return;\r
9949   if (doingSizing) return;\r
9950   if (fromY < 0 || fromX < 0) return;\r
9951   piece = board[fromY][fromX];\r
9952   if (piece >= EmptySquare) return;\r
9953 \r
9954   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
9955 \r
9956 again:\r
9957 \r
9958   ScreenSquare(fromX, fromY, &start);\r
9959   ScreenSquare(toX, toY, &finish);\r
9960 \r
9961   /* All moves except knight jumps move in straight line */\r
9962   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9963     mid.x = start.x + (finish.x - start.x) / 2;\r
9964     mid.y = start.y + (finish.y - start.y) / 2;\r
9965   } else {\r
9966     /* Knight: make straight movement then diagonal */\r
9967     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9968        mid.x = start.x + (finish.x - start.x) / 2;\r
9969        mid.y = start.y;\r
9970      } else {\r
9971        mid.x = start.x;\r
9972        mid.y = start.y + (finish.y - start.y) / 2;\r
9973      }\r
9974   }\r
9975   \r
9976   /* Don't use as many frames for very short moves */\r
9977   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9978     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9979   else\r
9980     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9981 \r
9982   animInfo.from.x = fromX;\r
9983   animInfo.from.y = fromY;\r
9984   animInfo.to.x = toX;\r
9985   animInfo.to.y = toY;\r
9986   animInfo.lastpos = start;\r
9987   animInfo.piece = piece;\r
9988   for (n = 0; n < nFrames; n++) {\r
9989     animInfo.pos = frames[n];\r
9990     DrawPosition(FALSE, NULL);\r
9991     animInfo.lastpos = animInfo.pos;\r
9992     Sleep(appData.animSpeed);\r
9993   }\r
9994   animInfo.pos = finish;\r
9995   DrawPosition(FALSE, NULL);\r
9996 \r
9997   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
9998 \r
9999   animInfo.piece = EmptySquare;\r
10000   Explode(board, fromX, fromY, toX, toY);\r
10001 }\r
10002 \r
10003 /*      Convert board position to corner of screen rect and color       */\r
10004 \r
10005 static void\r
10006 ScreenSquare(column, row, pt)\r
10007      int column; int row; POINT * pt;\r
10008 {\r
10009   if (flipView) {\r
10010     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10011     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10012   } else {\r
10013     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10014     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10015   }\r
10016 }\r
10017 \r
10018 /*      Generate a series of frame coords from start->mid->finish.\r
10019         The movement rate doubles until the half way point is\r
10020         reached, then halves back down to the final destination,\r
10021         which gives a nice slow in/out effect. The algorithmn\r
10022         may seem to generate too many intermediates for short\r
10023         moves, but remember that the purpose is to attract the\r
10024         viewers attention to the piece about to be moved and\r
10025         then to where it ends up. Too few frames would be less\r
10026         noticeable.                                             */\r
10027 \r
10028 static void\r
10029 Tween(start, mid, finish, factor, frames, nFrames)\r
10030      POINT * start; POINT * mid;\r
10031      POINT * finish; int factor;\r
10032      POINT frames[]; int * nFrames;\r
10033 {\r
10034   int n, fraction = 1, count = 0;\r
10035 \r
10036   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10037   for (n = 0; n < factor; n++)\r
10038     fraction *= 2;\r
10039   for (n = 0; n < factor; n++) {\r
10040     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10041     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10042     count ++;\r
10043     fraction = fraction / 2;\r
10044   }\r
10045   \r
10046   /* Midpoint */\r
10047   frames[count] = *mid;\r
10048   count ++;\r
10049   \r
10050   /* Slow out, stepping 1/2, then 1/4, ... */\r
10051   fraction = 2;\r
10052   for (n = 0; n < factor; n++) {\r
10053     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10054     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10055     count ++;\r
10056     fraction = fraction * 2;\r
10057   }\r
10058   *nFrames = count;\r
10059 }\r
10060 \r
10061 void\r
10062 SettingsPopUp(ChessProgramState *cps)\r
10063 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10064       EngineOptionsPopup(savedHwnd, cps);\r
10065 }\r
10066 \r
10067 int flock(int fid, int code)\r
10068 {\r
10069     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10070     OVERLAPPED ov;\r
10071     ov.hEvent = NULL;\r
10072     ov.Offset = 0;\r
10073     ov.OffsetHigh = 0;\r
10074     switch(code) {\r
10075       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10076 \r
10077       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10078       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10079       default: return -1;\r
10080     }\r
10081     return 0;\r
10082 }\r
10083 \r
10084 char *\r
10085 Col2Text (int n)\r
10086 {\r
10087     static int i=0;\r
10088     static char col[8][20];\r
10089     COLORREF color = *(COLORREF *) colorVariable[n];\r
10090     i = i+1 & 7;\r
10091     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10092     return col[i];\r
10093 }\r
10094 \r
10095 void\r
10096 ActivateTheme (int new)\r
10097 {   // Redo initialization of features depending on options that can occur in themes\r
10098    InitTextures();\r
10099    if(new) InitDrawingColors();\r
10100    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10101    InitDrawingSizes(boardSize, 0);\r
10102    InvalidateRect(hwndMain, NULL, TRUE);\r
10103 }\r