Add Wolf, Camel and Zebra bitmaps to WB
[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, 2015 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;\r
170 static int winW, winH;\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
172 static int logoHeight = 0;\r
173 static char messageText[MESSAGE_TEXT_MAX];\r
174 static int clockTimerEvent = 0;\r
175 static int loadGameTimerEvent = 0;\r
176 static int analysisTimerEvent = 0;\r
177 static DelayedEventCallback delayedTimerCallback;\r
178 static int delayedTimerEvent = 0;\r
179 static int buttonCount = 2;\r
180 char *icsTextMenuString;\r
181 char *icsNames;\r
182 char *firstChessProgramNames;\r
183 char *secondChessProgramNames;\r
184 \r
185 #define PALETTESIZE 256\r
186 \r
187 HINSTANCE hInst;          /* current instance */\r
188 Boolean alwaysOnTop = FALSE;\r
189 RECT boardRect;\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
191   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
193 HPALETTE hPal;\r
194 ColorClass currentColorClass;\r
195 \r
196 static HWND savedHwnd;\r
197 HWND hCommPort = NULL;    /* currently open comm port */\r
198 static HWND hwndPause;    /* pause button */\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,\r
201   blackSquareBrush, /* [HGM] for band between board and holdings */\r
202   explodeBrush,     /* [HGM] atomic */\r
203   markerBrush[8],   /* [HGM] markers */\r
204   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
207 static HPEN gridPen = NULL;\r
208 static HPEN highlightPen = NULL;\r
209 static HPEN premovePen = NULL;\r
210 static NPLOGPALETTE pLogPal;\r
211 static BOOL paletteChanged = FALSE;\r
212 static HICON iconWhite, iconBlack, iconCurrent;\r
213 static int doingSizing = FALSE;\r
214 static int lastSizing = 0;\r
215 static int prevStderrPort;\r
216 static HBITMAP userLogo;\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 \r
229 #if defined(_winmajor)\r
230 #define oldDialog (_winmajor < 4)\r
231 #else\r
232 #define oldDialog 0\r
233 #endif\r
234 #endif\r
235 \r
236 #define INTERNATIONAL\r
237 \r
238 #ifdef INTERNATIONAL\r
239 #  define _(s) T_(s)\r
240 #  define N_(s) s\r
241 #else\r
242 #  define _(s) s\r
243 #  define N_(s) s\r
244 #  define T_(s) s\r
245 #  define Translate(x, y)\r
246 #  define LoadLanguageFile(s)\r
247 #endif\r
248 \r
249 #ifdef INTERNATIONAL\r
250 \r
251 Boolean barbaric; // flag indicating if translation is needed\r
252 \r
253 // list of item numbers used in each dialog (used to alter language at run time)\r
254 \r
255 #define ABOUTBOX -1  /* not sure why these are needed */\r
256 #define ABOUTBOX2 -1\r
257 \r
258 int dialogItems[][42] = {\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
261   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
263   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
264   OPT_Ranget, IDOK, IDCANCEL }, \r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
266   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
269   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
272   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
273 { ABOUTBOX2, IDC_ChessBoard }, \r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
275   OPT_GameListClose, IDC_GameListDoFilter }, \r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
277 { DLG_Error, IDOK }, \r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
279   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
282   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
283   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
284 { DLG_IndexNumber, IDC_Index }, \r
285 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
286 { DLG_TypeInName, IDOK, IDCANCEL }, \r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
288   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
290   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
291   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
292   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
293   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
294   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
295   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
297   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
298   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
299   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
300   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
301   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
302   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
303   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
304   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
306   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
307   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
308   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
309   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
310   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
311   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
312   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
314   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
315   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
316   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
317   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
318   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
319   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
320   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
321   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
323   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
324   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
325   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
326   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
329   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
330 { DLG_MoveHistory }, \r
331 { DLG_EvalGraph }, \r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
335   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
336   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
337   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
339   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
340   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
341 { 0 }\r
342 };\r
343 \r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
345 static int lastChecked;\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
347 extern int tinyLayout;\r
348 extern char * menuBarText[][10];\r
349 \r
350 void\r
351 LoadLanguageFile(char *name)\r
352 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
353     FILE *f;\r
354     int i=0, j=0, n=0, k;\r
355     char buf[MSG_SIZ];\r
356 \r
357     if(!name || name[0] == NULLCHAR) return;\r
358       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
359     appData.language = oldLanguage;\r
360     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
361     if((f = fopen(buf, "r")) == NULL) return;\r
362     while((k = fgetc(f)) != EOF) {\r
363         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
364         languageBuf[i] = k;\r
365         if(k == '\n') {\r
366             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
367                 char *p;\r
368                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
369                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
370                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
371                         english[j] = languageBuf + n + 1; *p = 0;\r
372                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
374                     }\r
375                 }\r
376             }\r
377             n = i + 1;\r
378         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
379             switch(k) {\r
380               case 'n': k = '\n'; break;\r
381               case 'r': k = '\r'; break;\r
382               case 't': k = '\t'; break;\r
383             }\r
384             languageBuf[--i] = k;\r
385         }\r
386         i++;\r
387     }\r
388     fclose(f);\r
389     barbaric = (j != 0);\r
390     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
391 }\r
392 \r
393 char *\r
394 T_(char *s)\r
395 {   // return the translation of the given string\r
396     // efficiency can be improved a lot...\r
397     int i=0;\r
398     static char buf[MSG_SIZ];\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
400     if(!barbaric) return s;\r
401     if(!s) return ""; // sanity\r
402     while(english[i]) {\r
403         if(!strcmp(s, english[i])) return foreign[i];\r
404         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
405             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
406             return buf;\r
407         }\r
408         i++;\r
409     }\r
410     return s;\r
411 }\r
412 \r
413 void\r
414 Translate(HWND hDlg, int dialogID)\r
415 {   // translate all text items in the given dialog\r
416     int i=0, j, k;\r
417     char buf[MSG_SIZ], *s;\r
418     if(!barbaric) return;\r
419     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
420     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
421     GetWindowText( hDlg, buf, MSG_SIZ );\r
422     s = T_(buf);\r
423     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
424     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
425         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
426         if(strlen(buf) == 0) continue;\r
427         s = T_(buf);\r
428         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
429     }\r
430 }\r
431 \r
432 HMENU\r
433 TranslateOneMenu(int i, HMENU subMenu)\r
434 {\r
435     int j;\r
436     static MENUITEMINFO info;\r
437 \r
438     info.cbSize = sizeof(MENUITEMINFO);\r
439     info.fMask = MIIM_STATE | MIIM_TYPE;\r
440           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
441             char buf[MSG_SIZ];\r
442             info.dwTypeData = buf;\r
443             info.cch = sizeof(buf);\r
444             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
445             if(i < 10) {\r
446                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
447                 else menuText[i][j] = strdup(buf); // remember original on first change\r
448             }\r
449             if(buf[0] == NULLCHAR) continue;\r
450             info.dwTypeData = T_(buf);\r
451             info.cch = strlen(buf)+1;\r
452             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
453           }\r
454     return subMenu;\r
455 }\r
456 \r
457 void\r
458 TranslateMenus(int addLanguage)\r
459 {\r
460     int i;\r
461     WIN32_FIND_DATA fileData;\r
462     HANDLE hFind;\r
463 #define IDM_English 1970\r
464     if(1) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
467           HMENU subMenu = GetSubMenu(mainMenu, i);\r
468           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
469                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
470           TranslateOneMenu(i, subMenu);\r
471         }\r
472         DrawMenuBar(hwndMain);\r
473     }\r
474 \r
475     if(!addLanguage) return;\r
476     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
477         HMENU mainMenu = GetMenu(hwndMain);\r
478         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
479         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
480         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
481         i = 0; lastChecked = IDM_English;\r
482         do {\r
483             char *p, *q = fileData.cFileName;\r
484             int checkFlag = MF_UNCHECKED;\r
485             languageFile[i] = strdup(q);\r
486             if(barbaric && !strcmp(oldLanguage, q)) {\r
487                 checkFlag = MF_CHECKED;\r
488                 lastChecked = IDM_English + i + 1;\r
489                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
490             }\r
491             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
492             p = strstr(fileData.cFileName, ".lng");\r
493             if(p) *p = 0;\r
494             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
495         } while(FindNextFile(hFind, &fileData));\r
496         FindClose(hFind);\r
497     }\r
498 }\r
499 \r
500 #endif\r
501 \r
502 #define IDM_RecentEngines 3000\r
503 \r
504 void\r
505 RecentEngineMenu (char *s)\r
506 {\r
507     if(appData.icsActive) return;\r
508     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
509         HMENU mainMenu = GetMenu(hwndMain);\r
510         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
511         int i=IDM_RecentEngines;\r
512         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
513         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
514         while(*s) {\r
515           char *p = strchr(s, '\n');\r
516           if(p == NULL) return; // malformed!\r
517           *p = NULLCHAR;\r
518           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
519           *p = '\n';\r
520           s = p+1;\r
521         }\r
522     }\r
523 }\r
524 \r
525 \r
526 typedef struct {\r
527   char *name;\r
528   int squareSize;\r
529   int lineGap;\r
530   int smallLayout;\r
531   int tinyLayout;\r
532   int cliWidth, cliHeight;\r
533 } SizeInfo;\r
534 \r
535 SizeInfo sizeInfo[] = \r
536 {\r
537   { "tiny",     21, 0, 1, 2, 0, 0 },\r
538   { "teeny",    25, 1, 1, 2, 0, 0 },\r
539   { "dinky",    29, 1, 1, 2, 0, 0 },\r
540   { "petite",   33, 1, 1, 2, 0, 0 },\r
541   { "slim",     37, 2, 1, 1, 0, 0 },\r
542   { "small",    40, 2, 1, 1, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
574   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
575   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
576   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
577   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
578   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[3][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },\r
607   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
608 };\r
609 \r
610 \r
611 MySound sounds[(int)NSoundClasses];\r
612 MyTextAttribs textAttribs[(int)NColorClasses];\r
613 \r
614 MyColorizeAttribs colorizeAttribs[] = {\r
615   { (COLORREF)0, 0, N_("Shout Text") },\r
616   { (COLORREF)0, 0, N_("SShout/CShout") },\r
617   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
618   { (COLORREF)0, 0, N_("Channel Text") },\r
619   { (COLORREF)0, 0, N_("Kibitz Text") },\r
620   { (COLORREF)0, 0, N_("Tell Text") },\r
621   { (COLORREF)0, 0, N_("Challenge Text") },\r
622   { (COLORREF)0, 0, N_("Request Text") },\r
623   { (COLORREF)0, 0, N_("Seek Text") },\r
624   { (COLORREF)0, 0, N_("Normal Text") },\r
625   { (COLORREF)0, 0, N_("None") }\r
626 };\r
627 \r
628 \r
629 \r
630 static char *commentTitle;\r
631 static char *commentText;\r
632 static int commentIndex;\r
633 static Boolean editComment = FALSE;\r
634 \r
635 \r
636 char errorTitle[MSG_SIZ];\r
637 char errorMessage[2*MSG_SIZ];\r
638 HWND errorDialog = NULL;\r
639 BOOLEAN moveErrorMessageUp = FALSE;\r
640 BOOLEAN consoleEcho = TRUE;\r
641 CHARFORMAT consoleCF;\r
642 COLORREF consoleBackgroundColor;\r
643 \r
644 char *programVersion;\r
645 \r
646 #define CPReal 1\r
647 #define CPComm 2\r
648 #define CPSock 3\r
649 #define CPRcmd 4\r
650 typedef int CPKind;\r
651 \r
652 typedef struct {\r
653   CPKind kind;\r
654   HANDLE hProcess;\r
655   DWORD pid;\r
656   HANDLE hTo;\r
657   HANDLE hFrom;\r
658   SOCKET sock;\r
659   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
660 } ChildProc;\r
661 \r
662 #define INPUT_SOURCE_BUF_SIZE 4096\r
663 \r
664 typedef struct _InputSource {\r
665   CPKind kind;\r
666   HANDLE hFile;\r
667   SOCKET sock;\r
668   int lineByLine;\r
669   HANDLE hThread;\r
670   DWORD id;\r
671   char buf[INPUT_SOURCE_BUF_SIZE];\r
672   char *next;\r
673   DWORD count;\r
674   int error;\r
675   InputCallback func;\r
676   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
677   VOIDSTAR closure;\r
678 } InputSource;\r
679 \r
680 InputSource *consoleInputSource;\r
681 \r
682 DCB dcb;\r
683 \r
684 /* forward */\r
685 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
686 VOID ConsoleCreate();\r
687 LRESULT CALLBACK\r
688   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
689 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
690 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
691 VOID ParseCommSettings(char *arg, DCB *dcb);\r
692 LRESULT CALLBACK\r
693   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
694 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
695 void ParseIcsTextMenu(char *icsTextMenuString);\r
696 VOID PopUpNameDialog(char firstchar);\r
697 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
698 \r
699 /* [AS] */\r
700 int NewGameFRC();\r
701 int GameListOptions();\r
702 \r
703 int dummy; // [HGM] for obsolete args\r
704 \r
705 HWND hwndMain = NULL;        /* root window*/\r
706 HWND hwndConsole = NULL;\r
707 HWND commentDialog = NULL;\r
708 HWND moveHistoryDialog = NULL;\r
709 HWND evalGraphDialog = NULL;\r
710 HWND engineOutputDialog = NULL;\r
711 HWND gameListDialog = NULL;\r
712 HWND editTagsDialog = NULL;\r
713 \r
714 int commentUp = FALSE;\r
715 \r
716 WindowPlacement wpMain;\r
717 WindowPlacement wpConsole;\r
718 WindowPlacement wpComment;\r
719 WindowPlacement wpMoveHistory;\r
720 WindowPlacement wpEvalGraph;\r
721 WindowPlacement wpEngineOutput;\r
722 WindowPlacement wpGameList;\r
723 WindowPlacement wpTags;\r
724 \r
725 VOID EngineOptionsPopup(); // [HGM] settings\r
726 \r
727 VOID GothicPopUp(char *title, VariantClass variant);\r
728 /*\r
729  * Setting "frozen" should disable all user input other than deleting\r
730  * the window.  We do this while engines are initializing themselves.\r
731  */\r
732 static int frozen = 0;\r
733 static int oldMenuItemState[MENU_BAR_ITEMS];\r
734 void FreezeUI()\r
735 {\r
736   HMENU hmenu;\r
737   int i;\r
738 \r
739   if (frozen) return;\r
740   frozen = 1;\r
741   hmenu = GetMenu(hwndMain);\r
742   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
743     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
744   }\r
745   DrawMenuBar(hwndMain);\r
746 }\r
747 \r
748 /* Undo a FreezeUI */\r
749 void ThawUI()\r
750 {\r
751   HMENU hmenu;\r
752   int i;\r
753 \r
754   if (!frozen) return;\r
755   frozen = 0;\r
756   hmenu = GetMenu(hwndMain);\r
757   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
758     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
759   }\r
760   DrawMenuBar(hwndMain);\r
761 }\r
762 \r
763 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
764 \r
765 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
766 #ifdef JAWS\r
767 #include "jaws.c"\r
768 #else\r
769 #define JAWS_INIT\r
770 #define JAWS_ARGS\r
771 #define JAWS_ALT_INTERCEPT\r
772 #define JAWS_KBUP_NAVIGATION\r
773 #define JAWS_KBDOWN_NAVIGATION\r
774 #define JAWS_MENU_ITEMS\r
775 #define JAWS_SILENCE\r
776 #define JAWS_REPLAY\r
777 #define JAWS_ACCEL\r
778 #define JAWS_COPYRIGHT\r
779 #define JAWS_DELETE(X) X\r
780 #define SAYMACHINEMOVE()\r
781 #define SAY(X)\r
782 #endif\r
783 \r
784 /*---------------------------------------------------------------------------*\\r
785  *\r
786  * WinMain\r
787  *\r
788 \*---------------------------------------------------------------------------*/\r
789 \r
790 static void HandleMessage P((MSG *message));\r
791 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
792 \r
793 int APIENTRY\r
794 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
795         LPSTR lpCmdLine, int nCmdShow)\r
796 {\r
797   MSG msg;\r
798 //  INITCOMMONCONTROLSEX ex;\r
799 \r
800   debugFP = stderr;\r
801 \r
802   LoadLibrary("RICHED32.DLL");\r
803   consoleCF.cbSize = sizeof(CHARFORMAT);\r
804 \r
805   if (!InitApplication(hInstance)) {\r
806     return (FALSE);\r
807   }\r
808   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
809     return (FALSE);\r
810   }\r
811 \r
812   JAWS_INIT\r
813   TranslateMenus(1);\r
814 \r
815 //  InitCommonControlsEx(&ex);\r
816   InitCommonControls();\r
817 \r
818   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
819   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
820   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
821 \r
822   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
823 \r
824   while (GetMessage(&msg, /* message structure */\r
825                     NULL, /* handle of window receiving the message */\r
826                     0,    /* lowest message to examine */\r
827                     0))   /* highest message to examine */\r
828     {\r
829         HandleMessage(&msg);\r
830     }\r
831 \r
832 \r
833   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
834 }\r
835 \r
836 static void\r
837 HandleMessage (MSG *message)\r
838 {\r
839     MSG msg = *message;\r
840 \r
841       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
842         // [HGM] navigate: switch between all windows with tab\r
843         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
844         int i, currentElement = 0;\r
845 \r
846         // first determine what element of the chain we come from (if any)\r
847         if(appData.icsActive) {\r
848             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
849             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
850         }\r
851         if(engineOutputDialog && EngineOutputIsUp()) {\r
852             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
853             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
854         }\r
855         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
856             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
857         }\r
858         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
859         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
860         if(msg.hwnd == e1)                 currentElement = 2; else\r
861         if(msg.hwnd == e2)                 currentElement = 3; else\r
862         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
863         if(msg.hwnd == mh)                currentElement = 4; else\r
864         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
865         if(msg.hwnd == hText)  currentElement = 5; else\r
866         if(msg.hwnd == hInput) currentElement = 6; else\r
867         for (i = 0; i < N_BUTTONS; i++) {\r
868             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
869         }\r
870 \r
871         // determine where to go to\r
872         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
873           do {\r
874             currentElement = (currentElement + direction) % 7;\r
875             switch(currentElement) {\r
876                 case 0:\r
877                   h = hwndMain; break; // passing this case always makes the loop exit\r
878                 case 1:\r
879                   h = buttonDesc[0].hwnd; break; // could be NULL\r
880                 case 2:\r
881                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
882                   h = e1; break;\r
883                 case 3:\r
884                   if(!EngineOutputIsUp()) continue;\r
885                   h = e2; break;\r
886                 case 4:\r
887                   if(!MoveHistoryIsUp()) continue;\r
888                   h = mh; break;\r
889 //              case 6: // input to eval graph does not seem to get here!\r
890 //                if(!EvalGraphIsUp()) continue;\r
891 //                h = evalGraphDialog; break;\r
892                 case 5:\r
893                   if(!appData.icsActive) continue;\r
894                   SAY("display");\r
895                   h = hText; break;\r
896                 case 6:\r
897                   if(!appData.icsActive) continue;\r
898                   SAY("input");\r
899                   h = hInput; break;\r
900             }\r
901           } while(h == 0);\r
902 \r
903           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
904           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
905           SetFocus(h);\r
906 \r
907           return; // this message now has been processed\r
908         }\r
909       }\r
910 \r
911       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
912           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
913           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
914           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
915           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
916           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
917           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
918           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
919           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
920           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
921         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
922         for(i=0; i<MAX_CHAT; i++) \r
923             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
924                 done = 1; break;\r
925         }\r
926         if(done) return; // [HGM] chat: end patch\r
927         TranslateMessage(&msg); /* Translates virtual key codes */\r
928         DispatchMessage(&msg);  /* Dispatches message to window */\r
929       }\r
930 }\r
931 \r
932 void\r
933 DoEvents ()\r
934 { /* Dispatch pending messages */\r
935   MSG msg;\r
936   while (PeekMessage(&msg, /* message structure */\r
937                      NULL, /* handle of window receiving the message */\r
938                      0,    /* lowest message to examine */\r
939                      0,    /* highest message to examine */\r
940                      PM_REMOVE))\r
941     {\r
942         HandleMessage(&msg);\r
943     }\r
944 }\r
945 \r
946 /*---------------------------------------------------------------------------*\\r
947  *\r
948  * Initialization functions\r
949  *\r
950 \*---------------------------------------------------------------------------*/\r
951 \r
952 void\r
953 SetUserLogo()\r
954 {   // update user logo if necessary\r
955     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
956 \r
957     if(appData.autoLogo) {\r
958           curName = UserName();\r
959           if(strcmp(curName, oldUserName)) {\r
960                 GetCurrentDirectory(MSG_SIZ, dir);\r
961                 SetCurrentDirectory(installDir);\r
962                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
963                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
964                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
965                 if(userLogo == NULL)\r
966                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
967                 SetCurrentDirectory(dir); /* return to prev directory */\r
968           }\r
969     }\r
970 }\r
971 \r
972 BOOL\r
973 InitApplication(HINSTANCE hInstance)\r
974 {\r
975   WNDCLASS wc;\r
976 \r
977   /* Fill in window class structure with parameters that describe the */\r
978   /* main window. */\r
979 \r
980   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
981   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
982   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
983   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
984   wc.hInstance     = hInstance;         /* Owner of this class */\r
985   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
986   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
987   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
988   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
989   wc.lpszClassName = szAppName;                 /* Name to register as */\r
990 \r
991   /* Register the window class and return success/failure code. */\r
992   if (!RegisterClass(&wc)) return FALSE;\r
993 \r
994   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
995   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
996   wc.cbClsExtra    = 0;\r
997   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
998   wc.hInstance     = hInstance;\r
999   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
1000   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1001   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1002   wc.lpszMenuName  = NULL;\r
1003   wc.lpszClassName = szConsoleName;\r
1004 \r
1005   if (!RegisterClass(&wc)) return FALSE;\r
1006   return TRUE;\r
1007 }\r
1008 \r
1009 \r
1010 /* Set by InitInstance, used by EnsureOnScreen */\r
1011 int screenHeight, screenWidth;\r
1012 RECT screenGeometry;\r
1013 \r
1014 void\r
1015 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1016 {\r
1017 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1018   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1019   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1020   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1021   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1022   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1023 }\r
1024 \r
1025 VOID\r
1026 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1027 {\r
1028   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1029   GetCurrentDirectory(MSG_SIZ, dir);\r
1030   SetCurrentDirectory(installDir);\r
1031   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1032       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1033 \r
1034       if (cps->programLogo == NULL && appData.debugMode) {\r
1035           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1036       }\r
1037   } else if(appData.autoLogo) {\r
1038       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1039         char *opponent = "";\r
1040         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1041         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1042         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1043         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1044             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1045             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1046         }\r
1047       } else\r
1048       if(appData.directory[n] && appData.directory[n][0]) {\r
1049         SetCurrentDirectory(appData.directory[n]);\r
1050         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1051       }\r
1052   }\r
1053   SetCurrentDirectory(dir); /* return to prev directory */\r
1054 }\r
1055 \r
1056 VOID\r
1057 InitTextures()\r
1058 {\r
1059   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1060   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1061   \r
1062   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1063       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1064       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1065       liteBackTextureMode = appData.liteBackTextureMode;\r
1066 \r
1067       if (liteBackTexture == NULL && appData.debugMode) {\r
1068           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1069       }\r
1070   }\r
1071   \r
1072   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1073       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1074       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1075       darkBackTextureMode = appData.darkBackTextureMode;\r
1076 \r
1077       if (darkBackTexture == NULL && appData.debugMode) {\r
1078           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1079       }\r
1080   }\r
1081 }\r
1082 \r
1083 #ifndef SM_CXVIRTUALSCREEN\r
1084 #define SM_CXVIRTUALSCREEN 78\r
1085 #endif\r
1086 #ifndef SM_CYVIRTUALSCREEN\r
1087 #define SM_CYVIRTUALSCREEN 79\r
1088 #endif\r
1089 #ifndef SM_XVIRTUALSCREEN \r
1090 #define SM_XVIRTUALSCREEN 76\r
1091 #endif\r
1092 #ifndef SM_YVIRTUALSCREEN \r
1093 #define SM_YVIRTUALSCREEN 77\r
1094 #endif\r
1095 \r
1096 VOID\r
1097 InitGeometry()\r
1098 {\r
1099   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1100   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1101   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1102   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1103   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1104   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1105   screenGeometry.right = screenGeometry.left + screenWidth;\r
1106   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1107 }\r
1108 \r
1109 ChessProgramState broadcast;\r
1110 \r
1111 BOOL\r
1112 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1113 {\r
1114   HWND hwnd; /* Main window handle. */\r
1115   int ibs;\r
1116   WINDOWPLACEMENT wp;\r
1117   char *filepart;\r
1118 \r
1119   hInst = hInstance;    /* Store instance handle in our global variable */\r
1120   programName = szAppName;\r
1121 \r
1122   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1123     *filepart = NULLCHAR;\r
1124     SetCurrentDirectory(installDir);\r
1125   } else {\r
1126     GetCurrentDirectory(MSG_SIZ, installDir);\r
1127   }\r
1128   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1129   InitGeometry();\r
1130   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1131   /* xboard, and older WinBoards, controlled the move sound with the\r
1132      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1133      always turn the option on (so that the backend will call us),\r
1134      then let the user turn the sound off by setting it to silence if\r
1135      desired.  To accommodate old winboard.ini files saved by old\r
1136      versions of WinBoard, we also turn off the sound if the option\r
1137      was initially set to false. [HGM] taken out of InitAppData */\r
1138   if (!appData.ringBellAfterMoves) {\r
1139     sounds[(int)SoundMove].name = strdup("");\r
1140     appData.ringBellAfterMoves = TRUE;\r
1141   }\r
1142   if (appData.debugMode) {\r
1143     char *c = appData.nameOfDebugFile;\r
1144     if(strstr(c, "///") == c) {\r
1145       broadcast.which = "broadcaster";\r
1146       broadcast.pr   = NoProc;\r
1147       broadcast.isr  = NULL;\r
1148       broadcast.program = c + 3;\r
1149       broadcast.dir  = ".";\r
1150       broadcast.host = "localhost";\r
1151       StartChessProgram(&broadcast);\r
1152       debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");\r
1153     } else\r
1154     debugFP = fopen(c, "w");\r
1155     setbuf(debugFP, NULL);\r
1156   }\r
1157 \r
1158   LoadLanguageFile(appData.language);\r
1159 \r
1160   InitBackEnd1();\r
1161 \r
1162 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1163 //  InitEngineUCI( installDir, &second );\r
1164 \r
1165   /* Create a main window for this application instance. */\r
1166   hwnd = CreateWindow(szAppName, szTitle,\r
1167                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1168                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1169                       NULL, NULL, hInstance, NULL);\r
1170   hwndMain = hwnd;\r
1171 \r
1172   /* If window could not be created, return "failure" */\r
1173   if (!hwnd) {\r
1174     return (FALSE);\r
1175   }\r
1176 \r
1177   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1178   LoadLogo(&first, 0, FALSE);\r
1179   LoadLogo(&second, 1, appData.icsActive);\r
1180 \r
1181   SetUserLogo();\r
1182 \r
1183   iconWhite = LoadIcon(hInstance, "icon_white");\r
1184   iconBlack = LoadIcon(hInstance, "icon_black");\r
1185   iconCurrent = iconWhite;\r
1186   InitDrawingColors();\r
1187 \r
1188   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1189   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1190     /* Compute window size for each board size, and use the largest\r
1191        size that fits on this screen as the default. */\r
1192     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1193     if (boardSize == (BoardSize)-1 &&\r
1194         winH <= screenHeight\r
1195            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1196         && winW <= screenWidth) {\r
1197       boardSize = (BoardSize)ibs;\r
1198     }\r
1199   }\r
1200 \r
1201   InitDrawingSizes(boardSize, 0);\r
1202   RecentEngineMenu(appData.recentEngineList);\r
1203   InitMenuChecks();\r
1204   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1205 \r
1206   /* [AS] Load textures if specified */\r
1207   InitTextures();\r
1208 \r
1209   mysrandom( (unsigned) time(NULL) );\r
1210 \r
1211   /* [AS] Restore layout */\r
1212   if( wpMoveHistory.visible ) {\r
1213       MoveHistoryPopUp();\r
1214   }\r
1215 \r
1216   if( wpEvalGraph.visible ) {\r
1217       EvalGraphPopUp();\r
1218   }\r
1219 \r
1220   if( wpEngineOutput.visible ) {\r
1221       EngineOutputPopUp();\r
1222   }\r
1223 \r
1224   /* Make the window visible; update its client area; and return "success" */\r
1225   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1226   wp.length = sizeof(WINDOWPLACEMENT);\r
1227   wp.flags = 0;\r
1228   wp.showCmd = nCmdShow;\r
1229   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1230   wp.rcNormalPosition.left = wpMain.x;\r
1231   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1232   wp.rcNormalPosition.top = wpMain.y;\r
1233   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1234   SetWindowPlacement(hwndMain, &wp);\r
1235 \r
1236   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1237 \r
1238   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1239                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1240 \r
1241   if (hwndConsole) {\r
1242 #if AOT_CONSOLE\r
1243     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1244                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1245 #endif\r
1246     ShowWindow(hwndConsole, nCmdShow);\r
1247     SetActiveWindow(hwndConsole);\r
1248   }\r
1249   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1250   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1251 \r
1252   return TRUE;\r
1253 \r
1254 }\r
1255 \r
1256 VOID\r
1257 InitMenuChecks()\r
1258 {\r
1259   HMENU hmenu = GetMenu(hwndMain);\r
1260 \r
1261   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1262                         MF_BYCOMMAND|((appData.icsActive &&\r
1263                                        *appData.icsCommPort != NULLCHAR) ?\r
1264                                       MF_ENABLED : MF_GRAYED));\r
1265   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1266                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1267                                      MF_CHECKED : MF_UNCHECKED));\r
1268   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1269 }\r
1270 \r
1271 //---------------------------------------------------------------------------------------------------------\r
1272 \r
1273 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1274 #define XBOARD FALSE\r
1275 \r
1276 #define OPTCHAR "/"\r
1277 #define SEPCHAR "="\r
1278 #define TOPLEVEL 0\r
1279 \r
1280 #include "args.h"\r
1281 \r
1282 // front-end part of option handling\r
1283 \r
1284 VOID\r
1285 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1286 {\r
1287   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1288   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1289   DeleteDC(hdc);\r
1290   lf->lfWidth = 0;\r
1291   lf->lfEscapement = 0;\r
1292   lf->lfOrientation = 0;\r
1293   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1294   lf->lfItalic = mfp->italic;\r
1295   lf->lfUnderline = mfp->underline;\r
1296   lf->lfStrikeOut = mfp->strikeout;\r
1297   lf->lfCharSet = mfp->charset;\r
1298   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1299 \r
1300 \r
1301 \r
1302   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1303   lf->lfQuality = DEFAULT_QUALITY;\r
1304   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1305     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1306 }\r
1307 \r
1308 void\r
1309 CreateFontInMF(MyFont *mf)\r
1310\r
1311   LFfromMFP(&mf->lf, &mf->mfp);\r
1312   if (mf->hf) DeleteObject(mf->hf);\r
1313   mf->hf = CreateFontIndirect(&mf->lf);\r
1314 }\r
1315 \r
1316 // [HGM] This platform-dependent table provides the location for storing the color info\r
1317 void *\r
1318 colorVariable[] = {\r
1319   &whitePieceColor, \r
1320   &blackPieceColor, \r
1321   &lightSquareColor,\r
1322   &darkSquareColor, \r
1323   &highlightSquareColor,\r
1324   &premoveHighlightColor,\r
1325   NULL,\r
1326   &consoleBackgroundColor,\r
1327   &appData.fontForeColorWhite,\r
1328   &appData.fontBackColorWhite,\r
1329   &appData.fontForeColorBlack,\r
1330   &appData.fontBackColorBlack,\r
1331   &appData.evalHistColorWhite,\r
1332   &appData.evalHistColorBlack,\r
1333   &appData.highlightArrowColor,\r
1334 };\r
1335 \r
1336 /* Command line font name parser.  NULL name means do nothing.\r
1337    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1338    For backward compatibility, syntax without the colon is also\r
1339    accepted, but font names with digits in them won't work in that case.\r
1340 */\r
1341 VOID\r
1342 ParseFontName(char *name, MyFontParams *mfp)\r
1343 {\r
1344   char *p, *q;\r
1345   if (name == NULL) return;\r
1346   p = name;\r
1347   q = strchr(p, ':');\r
1348   if (q) {\r
1349     if (q - p >= sizeof(mfp->faceName))\r
1350       ExitArgError(_("Font name too long:"), name, TRUE);\r
1351     memcpy(mfp->faceName, p, q - p);\r
1352     mfp->faceName[q - p] = NULLCHAR;\r
1353     p = q + 1;\r
1354   } else {\r
1355     q = mfp->faceName;\r
1356 \r
1357     while (*p && !isdigit(*p)) {\r
1358       *q++ = *p++;\r
1359       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1360         ExitArgError(_("Font name too long:"), name, TRUE);\r
1361     }\r
1362     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1363     *q = NULLCHAR;\r
1364   }\r
1365   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1366   mfp->pointSize = (float) atof(p);\r
1367   mfp->bold = (strchr(p, 'b') != NULL);\r
1368   mfp->italic = (strchr(p, 'i') != NULL);\r
1369   mfp->underline = (strchr(p, 'u') != NULL);\r
1370   mfp->strikeout = (strchr(p, 's') != NULL);\r
1371   mfp->charset = DEFAULT_CHARSET;\r
1372   q = strchr(p, 'c');\r
1373   if (q)\r
1374     mfp->charset = (BYTE) atoi(q+1);\r
1375 }\r
1376 \r
1377 void\r
1378 ParseFont(char *name, int number)\r
1379 { // wrapper to shield back-end from 'font'\r
1380   ParseFontName(name, &font[boardSize][number]->mfp);\r
1381 }\r
1382 \r
1383 void\r
1384 SetFontDefaults()\r
1385 { // in WB  we have a 2D array of fonts; this initializes their description\r
1386   int i, j;\r
1387   /* Point font array elements to structures and\r
1388      parse default font names */\r
1389   for (i=0; i<NUM_FONTS; i++) {\r
1390     for (j=0; j<NUM_SIZES; j++) {\r
1391       font[j][i] = &fontRec[j][i];\r
1392       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1393     }\r
1394   }\r
1395 }\r
1396 \r
1397 void\r
1398 CreateFonts()\r
1399 { // here we create the actual fonts from the selected descriptions\r
1400   int i, j;\r
1401   for (i=0; i<NUM_FONTS; i++) {\r
1402     for (j=0; j<NUM_SIZES; j++) {\r
1403       CreateFontInMF(font[j][i]);\r
1404     }\r
1405   }\r
1406 }\r
1407 /* Color name parser.\r
1408    X version accepts X color names, but this one\r
1409    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1410 COLORREF\r
1411 ParseColorName(char *name)\r
1412 {\r
1413   int red, green, blue, count;\r
1414   char buf[MSG_SIZ];\r
1415 \r
1416   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1417   if (count != 3) {\r
1418     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1419       &red, &green, &blue);\r
1420   }\r
1421   if (count != 3) {\r
1422     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1423     DisplayError(buf, 0);\r
1424     return RGB(0, 0, 0);\r
1425   }\r
1426   return PALETTERGB(red, green, blue);\r
1427 }\r
1428 \r
1429 void\r
1430 ParseColor(int n, char *name)\r
1431 { // for WinBoard the color is an int, which needs to be derived from the string\r
1432   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1433 }\r
1434 \r
1435 void\r
1436 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1437 {\r
1438   char *e = argValue;\r
1439   int eff = 0;\r
1440 \r
1441   while (*e) {\r
1442     if (*e == 'b')      eff |= CFE_BOLD;\r
1443     else if (*e == 'i') eff |= CFE_ITALIC;\r
1444     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1445     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1446     else if (*e == '#' || isdigit(*e)) break;\r
1447     e++;\r
1448   }\r
1449   *effects = eff;\r
1450   *color   = ParseColorName(e);\r
1451 }\r
1452 \r
1453 void\r
1454 ParseTextAttribs(ColorClass cc, char *s)\r
1455 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1456     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1457     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1458 }\r
1459 \r
1460 void\r
1461 ParseBoardSize(void *addr, char *name)\r
1462 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1463   BoardSize bs = SizeTiny;\r
1464   while (sizeInfo[bs].name != NULL) {\r
1465     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1466         *(BoardSize *)addr = bs;\r
1467         return;\r
1468     }\r
1469     bs++;\r
1470   }\r
1471   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1472 }\r
1473 \r
1474 void\r
1475 LoadAllSounds()\r
1476 { // [HGM] import name from appData first\r
1477   ColorClass cc;\r
1478   SoundClass sc;\r
1479   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1480     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1481     textAttribs[cc].sound.data = NULL;\r
1482     MyLoadSound(&textAttribs[cc].sound);\r
1483   }\r
1484   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1485     textAttribs[cc].sound.name = strdup("");\r
1486     textAttribs[cc].sound.data = NULL;\r
1487   }\r
1488   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1489     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1490     sounds[sc].data = NULL;\r
1491     MyLoadSound(&sounds[sc]);\r
1492   }\r
1493 }\r
1494 \r
1495 void\r
1496 SetCommPortDefaults()\r
1497 {\r
1498    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1499   dcb.DCBlength = sizeof(DCB);\r
1500   dcb.BaudRate = 9600;\r
1501   dcb.fBinary = TRUE;\r
1502   dcb.fParity = FALSE;\r
1503   dcb.fOutxCtsFlow = FALSE;\r
1504   dcb.fOutxDsrFlow = FALSE;\r
1505   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1506   dcb.fDsrSensitivity = FALSE;\r
1507   dcb.fTXContinueOnXoff = TRUE;\r
1508   dcb.fOutX = FALSE;\r
1509   dcb.fInX = FALSE;\r
1510   dcb.fNull = FALSE;\r
1511   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1512   dcb.fAbortOnError = FALSE;\r
1513   dcb.ByteSize = 7;\r
1514   dcb.Parity = SPACEPARITY;\r
1515   dcb.StopBits = ONESTOPBIT;\r
1516 }\r
1517 \r
1518 // [HGM] args: these three cases taken out to stay in front-end\r
1519 void\r
1520 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1521 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1522         // while the curent board size determines the element. This system should be ported to XBoard.\r
1523         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1524         int bs;\r
1525         for (bs=0; bs<NUM_SIZES; bs++) {\r
1526           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1527           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1528           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1529             ad->argName, mfp->faceName, mfp->pointSize,\r
1530             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1531             mfp->bold ? "b" : "",\r
1532             mfp->italic ? "i" : "",\r
1533             mfp->underline ? "u" : "",\r
1534             mfp->strikeout ? "s" : "",\r
1535             (int)mfp->charset);\r
1536         }\r
1537       }\r
1538 \r
1539 void\r
1540 ExportSounds()\r
1541 { // [HGM] copy the names from the internal WB variables to appData\r
1542   ColorClass cc;\r
1543   SoundClass sc;\r
1544   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1545     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1546   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1547     (&appData.soundMove)[sc] = sounds[sc].name;\r
1548 }\r
1549 \r
1550 void\r
1551 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1552 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1553         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1554         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1555           (ta->effects & CFE_BOLD) ? "b" : "",\r
1556           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1557           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1558           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1559           (ta->effects) ? " " : "",\r
1560           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1561       }\r
1562 \r
1563 void\r
1564 SaveColor(FILE *f, ArgDescriptor *ad)\r
1565 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1566         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1567         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1568           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1569 }\r
1570 \r
1571 void\r
1572 SaveBoardSize(FILE *f, char *name, void *addr)\r
1573 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1574   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1575 }\r
1576 \r
1577 void\r
1578 ParseCommPortSettings(char *s)\r
1579 { // wrapper to keep dcb from back-end\r
1580   ParseCommSettings(s, &dcb);\r
1581 }\r
1582 \r
1583 void\r
1584 GetWindowCoords()\r
1585 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1586   GetActualPlacement(hwndMain, &wpMain);\r
1587   GetActualPlacement(hwndConsole, &wpConsole);\r
1588   GetActualPlacement(commentDialog, &wpComment);\r
1589   GetActualPlacement(editTagsDialog, &wpTags);\r
1590   GetActualPlacement(gameListDialog, &wpGameList);\r
1591   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1592   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1593   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1594 }\r
1595 \r
1596 void\r
1597 PrintCommPortSettings(FILE *f, char *name)\r
1598 { // wrapper to shield back-end from DCB\r
1599       PrintCommSettings(f, name, &dcb);\r
1600 }\r
1601 \r
1602 int\r
1603 MySearchPath(char *installDir, char *name, char *fullname)\r
1604 {\r
1605   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1606   if(name[0]== '%') {\r
1607     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1608     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1609       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1610       *strchr(buf, '%') = 0;\r
1611       strcat(fullname, getenv(buf));\r
1612       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1613     }\r
1614     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1615     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1616     return (int) strlen(fullname);\r
1617   }\r
1618   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1619 }\r
1620 \r
1621 int\r
1622 MyGetFullPathName(char *name, char *fullname)\r
1623 {\r
1624   char *dummy;\r
1625   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1626 }\r
1627 \r
1628 int\r
1629 MainWindowUp()\r
1630 { // [HGM] args: allows testing if main window is realized from back-end\r
1631   return hwndMain != NULL;\r
1632 }\r
1633 \r
1634 void\r
1635 PopUpStartupDialog()\r
1636 {\r
1637     FARPROC lpProc;\r
1638     \r
1639     LoadLanguageFile(appData.language);\r
1640     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1641     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1642     FreeProcInstance(lpProc);\r
1643 }\r
1644 \r
1645 /*---------------------------------------------------------------------------*\\r
1646  *\r
1647  * GDI board drawing routines\r
1648  *\r
1649 \*---------------------------------------------------------------------------*/\r
1650 \r
1651 /* [AS] Draw square using background texture */\r
1652 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1653 {\r
1654     XFORM   x;\r
1655 \r
1656     if( mode == 0 ) {\r
1657         return; /* Should never happen! */\r
1658     }\r
1659 \r
1660     SetGraphicsMode( dst, GM_ADVANCED );\r
1661 \r
1662     switch( mode ) {\r
1663     case 1:\r
1664         /* Identity */\r
1665         break;\r
1666     case 2:\r
1667         /* X reflection */\r
1668         x.eM11 = -1.0;\r
1669         x.eM12 = 0;\r
1670         x.eM21 = 0;\r
1671         x.eM22 = 1.0;\r
1672         x.eDx = (FLOAT) dw + dx - 1;\r
1673         x.eDy = 0;\r
1674         dx = 0;\r
1675         SetWorldTransform( dst, &x );\r
1676         break;\r
1677     case 3:\r
1678         /* Y reflection */\r
1679         x.eM11 = 1.0;\r
1680         x.eM12 = 0;\r
1681         x.eM21 = 0;\r
1682         x.eM22 = -1.0;\r
1683         x.eDx = 0;\r
1684         x.eDy = (FLOAT) dh + dy - 1;\r
1685         dy = 0;\r
1686         SetWorldTransform( dst, &x );\r
1687         break;\r
1688     case 4:\r
1689         /* X/Y flip */\r
1690         x.eM11 = 0;\r
1691         x.eM12 = 1.0;\r
1692         x.eM21 = 1.0;\r
1693         x.eM22 = 0;\r
1694         x.eDx = (FLOAT) dx;\r
1695         x.eDy = (FLOAT) dy;\r
1696         dx = 0;\r
1697         dy = 0;\r
1698         SetWorldTransform( dst, &x );\r
1699         break;\r
1700     }\r
1701 \r
1702     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1703 \r
1704     x.eM11 = 1.0;\r
1705     x.eM12 = 0;\r
1706     x.eM21 = 0;\r
1707     x.eM22 = 1.0;\r
1708     x.eDx = 0;\r
1709     x.eDy = 0;\r
1710     SetWorldTransform( dst, &x );\r
1711 \r
1712     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1713 }\r
1714 \r
1715 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1716 enum {\r
1717     PM_WP = (int) WhitePawn, \r
1718     PM_WN = (int) WhiteKnight, \r
1719     PM_WB = (int) WhiteBishop, \r
1720     PM_WR = (int) WhiteRook, \r
1721     PM_WQ = (int) WhiteQueen, \r
1722     PM_WF = (int) WhiteFerz, \r
1723     PM_WW = (int) WhiteWazir, \r
1724     PM_WE = (int) WhiteAlfil, \r
1725     PM_WM = (int) WhiteMan, \r
1726     PM_WO = (int) WhiteCannon, \r
1727     PM_WU = (int) WhiteUnicorn, \r
1728     PM_WH = (int) WhiteNightrider, \r
1729     PM_WA = (int) WhiteAngel, \r
1730     PM_WC = (int) WhiteMarshall, \r
1731     PM_WAB = (int) WhiteCardinal, \r
1732     PM_WD = (int) WhiteDragon, \r
1733     PM_WL = (int) WhiteLance, \r
1734     PM_WS = (int) WhiteCobra, \r
1735     PM_WV = (int) WhiteFalcon, \r
1736     PM_WSG = (int) WhiteSilver, \r
1737     PM_WG = (int) WhiteGrasshopper, \r
1738     PM_WK = (int) WhiteKing,\r
1739     PM_BP = (int) BlackPawn, \r
1740     PM_BN = (int) BlackKnight, \r
1741     PM_BB = (int) BlackBishop, \r
1742     PM_BR = (int) BlackRook, \r
1743     PM_BQ = (int) BlackQueen, \r
1744     PM_BF = (int) BlackFerz, \r
1745     PM_BW = (int) BlackWazir, \r
1746     PM_BE = (int) BlackAlfil, \r
1747     PM_BM = (int) BlackMan,\r
1748     PM_BO = (int) BlackCannon, \r
1749     PM_BU = (int) BlackUnicorn, \r
1750     PM_BH = (int) BlackNightrider, \r
1751     PM_BA = (int) BlackAngel, \r
1752     PM_BC = (int) BlackMarshall, \r
1753     PM_BG = (int) BlackGrasshopper, \r
1754     PM_BAB = (int) BlackCardinal,\r
1755     PM_BD = (int) BlackDragon,\r
1756     PM_BL = (int) BlackLance,\r
1757     PM_BS = (int) BlackCobra,\r
1758     PM_BV = (int) BlackFalcon,\r
1759     PM_BSG = (int) BlackSilver,\r
1760     PM_BK = (int) BlackKing\r
1761 };\r
1762 \r
1763 static HFONT hPieceFont = NULL;\r
1764 static HBITMAP hPieceMask[(int) EmptySquare];\r
1765 static HBITMAP hPieceFace[(int) EmptySquare];\r
1766 static int fontBitmapSquareSize = 0;\r
1767 static char pieceToFontChar[(int) EmptySquare] =\r
1768                               { 'p', 'n', 'b', 'r', 'q', \r
1769                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1770                       'k', 'o', 'm', 'v', 't', 'w', \r
1771                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1772                                                               'l' };\r
1773 \r
1774 extern BOOL SetCharTable( char *table, const char * map );\r
1775 /* [HGM] moved to backend.c */\r
1776 \r
1777 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1778 {\r
1779     HBRUSH hbrush;\r
1780     BYTE r1 = GetRValue( color );\r
1781     BYTE g1 = GetGValue( color );\r
1782     BYTE b1 = GetBValue( color );\r
1783     BYTE r2 = r1 / 2;\r
1784     BYTE g2 = g1 / 2;\r
1785     BYTE b2 = b1 / 2;\r
1786     RECT rc;\r
1787 \r
1788     /* Create a uniform background first */\r
1789     hbrush = CreateSolidBrush( color );\r
1790     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1791     FillRect( hdc, &rc, hbrush );\r
1792     DeleteObject( hbrush );\r
1793     \r
1794     if( mode == 1 ) {\r
1795         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1796         int steps = squareSize / 2;\r
1797         int i;\r
1798 \r
1799         for( i=0; i<steps; i++ ) {\r
1800             BYTE r = r1 - (r1-r2) * i / steps;\r
1801             BYTE g = g1 - (g1-g2) * i / steps;\r
1802             BYTE b = b1 - (b1-b2) * i / steps;\r
1803 \r
1804             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1805             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1806             FillRect( hdc, &rc, hbrush );\r
1807             DeleteObject(hbrush);\r
1808         }\r
1809     }\r
1810     else if( mode == 2 ) {\r
1811         /* Diagonal gradient, good more or less for every piece */\r
1812         POINT triangle[3];\r
1813         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1814         HBRUSH hbrush_old;\r
1815         int steps = squareSize;\r
1816         int i;\r
1817 \r
1818         triangle[0].x = squareSize - steps;\r
1819         triangle[0].y = squareSize;\r
1820         triangle[1].x = squareSize;\r
1821         triangle[1].y = squareSize;\r
1822         triangle[2].x = squareSize;\r
1823         triangle[2].y = squareSize - steps;\r
1824 \r
1825         for( i=0; i<steps; i++ ) {\r
1826             BYTE r = r1 - (r1-r2) * i / steps;\r
1827             BYTE g = g1 - (g1-g2) * i / steps;\r
1828             BYTE b = b1 - (b1-b2) * i / steps;\r
1829 \r
1830             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1831             hbrush_old = SelectObject( hdc, hbrush );\r
1832             Polygon( hdc, triangle, 3 );\r
1833             SelectObject( hdc, hbrush_old );\r
1834             DeleteObject(hbrush);\r
1835             triangle[0].x++;\r
1836             triangle[2].y++;\r
1837         }\r
1838 \r
1839         SelectObject( hdc, hpen );\r
1840     }\r
1841 }\r
1842 \r
1843 /*\r
1844     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1845     seems to work ok. The main problem here is to find the "inside" of a chess\r
1846     piece: follow the steps as explained below.\r
1847 */\r
1848 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1849 {\r
1850     HBITMAP hbm;\r
1851     HBITMAP hbm_old;\r
1852     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1853     RECT rc;\r
1854     SIZE sz;\r
1855 \r
1856 \r
1857     POINT pt;\r
1858     int backColor = whitePieceColor; \r
1859     int foreColor = blackPieceColor;\r
1860     \r
1861     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1862         backColor = appData.fontBackColorWhite;\r
1863         foreColor = appData.fontForeColorWhite;\r
1864     }\r
1865     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1866         backColor = appData.fontBackColorBlack;\r
1867         foreColor = appData.fontForeColorBlack;\r
1868     }\r
1869 \r
1870     /* Mask */\r
1871     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1872 \r
1873     hbm_old = SelectObject( hdc, hbm );\r
1874 \r
1875     rc.left = 0;\r
1876     rc.top = 0;\r
1877     rc.right = squareSize;\r
1878     rc.bottom = squareSize;\r
1879 \r
1880     /* Step 1: background is now black */\r
1881     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1882 \r
1883     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1884 \r
1885     pt.x = (squareSize - sz.cx) / 2;\r
1886     pt.y = (squareSize - sz.cy) / 2;\r
1887 \r
1888     SetBkMode( hdc, TRANSPARENT );\r
1889     SetTextColor( hdc, chroma );\r
1890     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1891     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1892 \r
1893     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1894     /* Step 3: the area outside the piece is filled with white */\r
1895 //    FloodFill( hdc, 0, 0, chroma );\r
1896     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1897     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1898     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1899     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1900     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1901     /* \r
1902         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1903         but if the start point is not inside the piece we're lost!\r
1904         There should be a better way to do this... if we could create a region or path\r
1905         from the fill operation we would be fine for example.\r
1906     */\r
1907 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1908     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1909 \r
1910     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1911         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1912         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1913 \r
1914         SelectObject( dc2, bm2 );\r
1915         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1916         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1917         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1918         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1919         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1920 \r
1921         DeleteDC( dc2 );\r
1922         DeleteObject( bm2 );\r
1923     }\r
1924 \r
1925     SetTextColor( hdc, 0 );\r
1926     /* \r
1927         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1928         draw the piece again in black for safety.\r
1929     */\r
1930     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1931 \r
1932     SelectObject( hdc, hbm_old );\r
1933 \r
1934     if( hPieceMask[index] != NULL ) {\r
1935         DeleteObject( hPieceMask[index] );\r
1936     }\r
1937 \r
1938     hPieceMask[index] = hbm;\r
1939 \r
1940     /* Face */\r
1941     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1942 \r
1943     SelectObject( hdc, hbm );\r
1944 \r
1945     {\r
1946         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1947         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1948         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1949 \r
1950         SelectObject( dc1, hPieceMask[index] );\r
1951         SelectObject( dc2, bm2 );\r
1952         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1953         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1954         \r
1955         /* \r
1956             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1957             the piece background and deletes (makes transparent) the rest.\r
1958             Thanks to that mask, we are free to paint the background with the greates\r
1959             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1960             We use this, to make gradients and give the pieces a "roundish" look.\r
1961         */\r
1962         SetPieceBackground( hdc, backColor, 2 );\r
1963         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1964 \r
1965         DeleteDC( dc2 );\r
1966         DeleteDC( dc1 );\r
1967         DeleteObject( bm2 );\r
1968     }\r
1969 \r
1970     SetTextColor( hdc, foreColor );\r
1971     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1972 \r
1973     SelectObject( hdc, hbm_old );\r
1974 \r
1975     if( hPieceFace[index] != NULL ) {\r
1976         DeleteObject( hPieceFace[index] );\r
1977     }\r
1978 \r
1979     hPieceFace[index] = hbm;\r
1980 }\r
1981 \r
1982 static int TranslatePieceToFontPiece( int piece )\r
1983 {\r
1984     switch( piece ) {\r
1985     case BlackPawn:\r
1986         return PM_BP;\r
1987     case BlackKnight:\r
1988         return PM_BN;\r
1989     case BlackBishop:\r
1990         return PM_BB;\r
1991     case BlackRook:\r
1992         return PM_BR;\r
1993     case BlackQueen:\r
1994         return PM_BQ;\r
1995     case BlackKing:\r
1996         return PM_BK;\r
1997     case WhitePawn:\r
1998         return PM_WP;\r
1999     case WhiteKnight:\r
2000         return PM_WN;\r
2001     case WhiteBishop:\r
2002         return PM_WB;\r
2003     case WhiteRook:\r
2004         return PM_WR;\r
2005     case WhiteQueen:\r
2006         return PM_WQ;\r
2007     case WhiteKing:\r
2008         return PM_WK;\r
2009 \r
2010     case BlackAngel:\r
2011         return PM_BA;\r
2012     case BlackMarshall:\r
2013         return PM_BC;\r
2014     case BlackFerz:\r
2015         return PM_BF;\r
2016     case BlackNightrider:\r
2017         return PM_BH;\r
2018     case BlackAlfil:\r
2019         return PM_BE;\r
2020     case BlackWazir:\r
2021         return PM_BW;\r
2022     case BlackUnicorn:\r
2023         return PM_BU;\r
2024     case BlackCannon:\r
2025         return PM_BO;\r
2026     case BlackGrasshopper:\r
2027         return PM_BG;\r
2028     case BlackMan:\r
2029         return PM_BM;\r
2030     case BlackSilver:\r
2031         return PM_BSG;\r
2032     case BlackLance:\r
2033         return PM_BL;\r
2034     case BlackFalcon:\r
2035         return PM_BV;\r
2036     case BlackCobra:\r
2037         return PM_BS;\r
2038     case BlackCardinal:\r
2039         return PM_BAB;\r
2040     case BlackDragon:\r
2041         return PM_BD;\r
2042 \r
2043     case WhiteAngel:\r
2044         return PM_WA;\r
2045     case WhiteMarshall:\r
2046         return PM_WC;\r
2047     case WhiteFerz:\r
2048         return PM_WF;\r
2049     case WhiteNightrider:\r
2050         return PM_WH;\r
2051     case WhiteAlfil:\r
2052         return PM_WE;\r
2053     case WhiteWazir:\r
2054         return PM_WW;\r
2055     case WhiteUnicorn:\r
2056         return PM_WU;\r
2057     case WhiteCannon:\r
2058         return PM_WO;\r
2059     case WhiteGrasshopper:\r
2060         return PM_WG;\r
2061     case WhiteMan:\r
2062         return PM_WM;\r
2063     case WhiteSilver:\r
2064         return PM_WSG;\r
2065     case WhiteLance:\r
2066         return PM_WL;\r
2067     case WhiteFalcon:\r
2068         return PM_WV;\r
2069     case WhiteCobra:\r
2070         return PM_WS;\r
2071     case WhiteCardinal:\r
2072         return PM_WAB;\r
2073     case WhiteDragon:\r
2074         return PM_WD;\r
2075     }\r
2076 \r
2077     return 0;\r
2078 }\r
2079 \r
2080 void CreatePiecesFromFont()\r
2081 {\r
2082     LOGFONT lf;\r
2083     HDC hdc_window = NULL;\r
2084     HDC hdc = NULL;\r
2085     HFONT hfont_old;\r
2086     int fontHeight;\r
2087     int i;\r
2088 \r
2089     if( fontBitmapSquareSize < 0 ) {\r
2090         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2091         return;\r
2092     }\r
2093 \r
2094     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2095             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2096         fontBitmapSquareSize = -1;\r
2097         return;\r
2098     }\r
2099 \r
2100     if( fontBitmapSquareSize != squareSize ) {\r
2101         hdc_window = GetDC( hwndMain );\r
2102         hdc = CreateCompatibleDC( hdc_window );\r
2103 \r
2104         if( hPieceFont != NULL ) {\r
2105             DeleteObject( hPieceFont );\r
2106         }\r
2107         else {\r
2108             for( i=0; i<=(int)BlackKing; i++ ) {\r
2109                 hPieceMask[i] = NULL;\r
2110                 hPieceFace[i] = NULL;\r
2111             }\r
2112         }\r
2113 \r
2114         fontHeight = 75;\r
2115 \r
2116         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2117             fontHeight = appData.fontPieceSize;\r
2118         }\r
2119 \r
2120         fontHeight = (fontHeight * squareSize) / 100;\r
2121 \r
2122         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2123         lf.lfWidth = 0;\r
2124         lf.lfEscapement = 0;\r
2125         lf.lfOrientation = 0;\r
2126         lf.lfWeight = FW_NORMAL;\r
2127         lf.lfItalic = 0;\r
2128         lf.lfUnderline = 0;\r
2129         lf.lfStrikeOut = 0;\r
2130         lf.lfCharSet = DEFAULT_CHARSET;\r
2131         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2132         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2133         lf.lfQuality = PROOF_QUALITY;\r
2134         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2135         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2136         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2137 \r
2138         hPieceFont = CreateFontIndirect( &lf );\r
2139 \r
2140         if( hPieceFont == NULL ) {\r
2141             fontBitmapSquareSize = -2;\r
2142         }\r
2143         else {\r
2144             /* Setup font-to-piece character table */\r
2145             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2146                 /* No (or wrong) global settings, try to detect the font */\r
2147                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2148                     /* Alpha */\r
2149                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2150                 }\r
2151                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2152                     /* DiagramTT* family */\r
2153                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2154                 }\r
2155                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2156                     /* Fairy symbols */\r
2157                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2158                 }\r
2159                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2160                     /* Good Companion (Some characters get warped as literal :-( */\r
2161                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2162                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2163                     SetCharTable(pieceToFontChar, s);\r
2164                 }\r
2165                 else {\r
2166                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2167                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2168                 }\r
2169             }\r
2170 \r
2171             /* Create bitmaps */\r
2172             hfont_old = SelectObject( hdc, hPieceFont );\r
2173             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2174                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2175                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2176 \r
2177             SelectObject( hdc, hfont_old );\r
2178 \r
2179             fontBitmapSquareSize = squareSize;\r
2180         }\r
2181     }\r
2182 \r
2183     if( hdc != NULL ) {\r
2184         DeleteDC( hdc );\r
2185     }\r
2186 \r
2187     if( hdc_window != NULL ) {\r
2188         ReleaseDC( hwndMain, hdc_window );\r
2189     }\r
2190 }\r
2191 \r
2192 HBITMAP\r
2193 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2194 {\r
2195   char name[128], buf[MSG_SIZ];\r
2196 \r
2197     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2198   if(appData.pieceDirectory[0]) {\r
2199     HBITMAP res;\r
2200     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2201     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2202     if(res) return res;\r
2203   }\r
2204   if (gameInfo.event &&\r
2205       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2206       strcmp(name, "k80s") == 0) {\r
2207     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2208   }\r
2209   return LoadBitmap(hinst, name);\r
2210 }\r
2211 \r
2212 \r
2213 /* Insert a color into the program's logical palette\r
2214    structure.  This code assumes the given color is\r
2215    the result of the RGB or PALETTERGB macro, and it\r
2216    knows how those macros work (which is documented).\r
2217 */\r
2218 VOID\r
2219 InsertInPalette(COLORREF color)\r
2220 {\r
2221   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2222 \r
2223   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2224     DisplayFatalError(_("Too many colors"), 0, 1);\r
2225     pLogPal->palNumEntries--;\r
2226     return;\r
2227   }\r
2228 \r
2229   pe->peFlags = (char) 0;\r
2230   pe->peRed = (char) (0xFF & color);\r
2231   pe->peGreen = (char) (0xFF & (color >> 8));\r
2232   pe->peBlue = (char) (0xFF & (color >> 16));\r
2233   return;\r
2234 }\r
2235 \r
2236 \r
2237 VOID\r
2238 InitDrawingColors()\r
2239 {\r
2240   int i;\r
2241   if (pLogPal == NULL) {\r
2242     /* Allocate enough memory for a logical palette with\r
2243      * PALETTESIZE entries and set the size and version fields\r
2244      * of the logical palette structure.\r
2245      */\r
2246     pLogPal = (NPLOGPALETTE)\r
2247       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2248                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2249     pLogPal->palVersion    = 0x300;\r
2250   }\r
2251   pLogPal->palNumEntries = 0;\r
2252 \r
2253   InsertInPalette(lightSquareColor);\r
2254   InsertInPalette(darkSquareColor);\r
2255   InsertInPalette(whitePieceColor);\r
2256   InsertInPalette(blackPieceColor);\r
2257   InsertInPalette(highlightSquareColor);\r
2258   InsertInPalette(premoveHighlightColor);\r
2259 \r
2260   /*  create a logical color palette according the information\r
2261    *  in the LOGPALETTE structure.\r
2262    */\r
2263   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2264 \r
2265   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2266   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2267   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2268   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2269   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2270   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2271   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2272     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2273 \r
2274    /* [AS] Force rendering of the font-based pieces */\r
2275   if( fontBitmapSquareSize > 0 ) {\r
2276     fontBitmapSquareSize = 0;\r
2277   }\r
2278 }\r
2279 \r
2280 \r
2281 int\r
2282 BoardWidth(int boardSize, int n)\r
2283 { /* [HGM] argument n added to allow different width and height */\r
2284   int lineGap = sizeInfo[boardSize].lineGap;\r
2285 \r
2286   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2287       lineGap = appData.overrideLineGap;\r
2288   }\r
2289 \r
2290   return (n + 1) * lineGap +\r
2291           n * sizeInfo[boardSize].squareSize;\r
2292 }\r
2293 \r
2294 /* Respond to board resize by dragging edge */\r
2295 VOID\r
2296 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2297 {\r
2298   BoardSize newSize = NUM_SIZES - 1;\r
2299   static int recurse = 0;\r
2300   if (IsIconic(hwndMain)) return;\r
2301   if (recurse > 0) return;\r
2302   recurse++;\r
2303   while (newSize > 0) {\r
2304         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2305         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2306            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2307     newSize--;\r
2308   } \r
2309   boardSize = newSize;\r
2310   InitDrawingSizes(boardSize, flags);\r
2311   recurse--;\r
2312 }\r
2313 \r
2314 \r
2315 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2316 \r
2317 VOID\r
2318 InitDrawingSizes(BoardSize boardSize, int flags)\r
2319 {\r
2320   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2321   ChessSquare piece;\r
2322   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2323   HDC hdc;\r
2324   SIZE clockSize, messageSize;\r
2325   HFONT oldFont;\r
2326   char buf[MSG_SIZ];\r
2327   char *str;\r
2328   HMENU hmenu = GetMenu(hwndMain);\r
2329   RECT crect, wrect, oldRect;\r
2330   int offby;\r
2331   LOGBRUSH logbrush;\r
2332   VariantClass v = gameInfo.variant;\r
2333 \r
2334   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2335   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2336 \r
2337   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2338   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2339   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2340   oldBoardSize = boardSize;\r
2341 \r
2342   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2343   { // correct board size to one where built-in pieces exist\r
2344     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2345        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2346 \r
2347       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2348       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2349       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2350       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2351       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2352                                    boardSize = SizeMiddling;\r
2353     }\r
2354   }\r
2355   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2356 \r
2357   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2358   oldRect.top = wpMain.y;\r
2359   oldRect.right = wpMain.x + wpMain.width;\r
2360   oldRect.bottom = wpMain.y + wpMain.height;\r
2361 \r
2362   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2363   smallLayout = sizeInfo[boardSize].smallLayout;\r
2364   squareSize = sizeInfo[boardSize].squareSize;\r
2365   lineGap = sizeInfo[boardSize].lineGap;\r
2366   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2367   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2368 \r
2369   // [HGM] decide on tininess based on total board width rather than square size\r
2370   tinyLayout = squareSize * (BOARD_WIDTH);\r
2371   tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;\r
2372 \r
2373   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2374       lineGap = appData.overrideLineGap;\r
2375   }\r
2376 \r
2377   if (tinyLayout != oldTinyLayout) {\r
2378     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2379     if (tinyLayout == 2) {\r
2380       style &= ~WS_SYSMENU;\r
2381       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2382                  "&Minimize\tCtrl+F4");\r
2383     } else {\r
2384       style |= WS_SYSMENU;\r
2385       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2386     }\r
2387     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2388 \r
2389     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2390       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2391         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2392     }\r
2393     DrawMenuBar(hwndMain);\r
2394   }\r
2395 \r
2396   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2397   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2398 \r
2399   /* Get text area sizes */\r
2400   hdc = GetDC(hwndMain);\r
2401   if (appData.clockMode) {\r
2402     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2403   } else {\r
2404     snprintf(buf, MSG_SIZ, _("White"));\r
2405   }\r
2406   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2407   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2408   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2409   str = _("We only care about the height here");\r
2410   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2411   SelectObject(hdc, oldFont);\r
2412   ReleaseDC(hwndMain, hdc);\r
2413 \r
2414   /* Compute where everything goes */\r
2415   if((first.programLogo || second.programLogo) && tinyLayout != 2) {\r
2416         /* [HGM] logo: if either logo is on, reserve space for it */\r
2417         logoHeight =  2*clockSize.cy;\r
2418         leftLogoRect.left   = OUTER_MARGIN;\r
2419         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2420         leftLogoRect.top    = OUTER_MARGIN;\r
2421         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2422 \r
2423         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2424         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2425         rightLogoRect.top    = OUTER_MARGIN;\r
2426         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2427 \r
2428 \r
2429     whiteRect.left = leftLogoRect.right;\r
2430     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2431     whiteRect.top = OUTER_MARGIN;\r
2432     whiteRect.bottom = whiteRect.top + logoHeight;\r
2433 \r
2434     blackRect.right = rightLogoRect.left;\r
2435     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2436     blackRect.top = whiteRect.top;\r
2437     blackRect.bottom = whiteRect.bottom;\r
2438   } else {\r
2439     whiteRect.left = OUTER_MARGIN;\r
2440     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2441     whiteRect.top = OUTER_MARGIN;\r
2442     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2443 \r
2444     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2445     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2446     blackRect.top = whiteRect.top;\r
2447     blackRect.bottom = whiteRect.bottom;\r
2448 \r
2449     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2450   }\r
2451 \r
2452   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2453   if (appData.showButtonBar) {\r
2454     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2455       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2456   } else {\r
2457     messageRect.right = OUTER_MARGIN + boardWidth;\r
2458   }\r
2459   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2460   messageRect.bottom = messageRect.top + messageSize.cy;\r
2461 \r
2462   boardRect.left = OUTER_MARGIN;\r
2463   boardRect.right = boardRect.left + boardWidth;\r
2464   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2465   boardRect.bottom = boardRect.top + boardHeight;\r
2466 \r
2467   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2468   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2469   oldTinyLayout = tinyLayout;\r
2470   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2471   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2472     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2473   winW *= 1 + twoBoards;\r
2474   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2475   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2476   wpMain.height = winH; //       without disturbing window attachments\r
2477   GetWindowRect(hwndMain, &wrect);\r
2478   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2479                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2480 \r
2481   // [HGM] placement: let attached windows follow size change.\r
2482   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2483   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2484   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2485   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2486   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2487 \r
2488   /* compensate if menu bar wrapped */\r
2489   GetClientRect(hwndMain, &crect);\r
2490   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2491   wpMain.height += offby;\r
2492   switch (flags) {\r
2493   case WMSZ_TOPLEFT:\r
2494     SetWindowPos(hwndMain, NULL, \r
2495                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2496                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2497     break;\r
2498 \r
2499   case WMSZ_TOPRIGHT:\r
2500   case WMSZ_TOP:\r
2501     SetWindowPos(hwndMain, NULL, \r
2502                  wrect.left, wrect.bottom - wpMain.height, \r
2503                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2504     break;\r
2505 \r
2506   case WMSZ_BOTTOMLEFT:\r
2507   case WMSZ_LEFT:\r
2508     SetWindowPos(hwndMain, NULL, \r
2509                  wrect.right - wpMain.width, wrect.top, \r
2510                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2511     break;\r
2512 \r
2513   case WMSZ_BOTTOMRIGHT:\r
2514   case WMSZ_BOTTOM:\r
2515   case WMSZ_RIGHT:\r
2516   default:\r
2517     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2518                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2519     break;\r
2520   }\r
2521 \r
2522   hwndPause = NULL;\r
2523   for (i = 0; i < N_BUTTONS; i++) {\r
2524     if (buttonDesc[i].hwnd != NULL) {\r
2525       DestroyWindow(buttonDesc[i].hwnd);\r
2526       buttonDesc[i].hwnd = NULL;\r
2527     }\r
2528     if (appData.showButtonBar) {\r
2529       buttonDesc[i].hwnd =\r
2530         CreateWindow("BUTTON", buttonDesc[i].label,\r
2531                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2532                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2533                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2534                      (HMENU) buttonDesc[i].id,\r
2535                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2536       if (tinyLayout == 2) {\r
2537         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2538                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2539                     MAKELPARAM(FALSE, 0));\r
2540       }\r
2541       if (buttonDesc[i].id == IDM_Pause)\r
2542         hwndPause = buttonDesc[i].hwnd;\r
2543       buttonDesc[i].wndproc = (WNDPROC)\r
2544         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2545     }\r
2546   }\r
2547   if (gridPen != NULL) DeleteObject(gridPen);\r
2548   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2549   if (premovePen != NULL) DeleteObject(premovePen);\r
2550   if (lineGap != 0) {\r
2551     logbrush.lbStyle = BS_SOLID;\r
2552     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2553     gridPen =\r
2554       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2555                    lineGap, &logbrush, 0, NULL);\r
2556     logbrush.lbColor = highlightSquareColor;\r
2557     highlightPen =\r
2558       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2559                    lineGap, &logbrush, 0, NULL);\r
2560 \r
2561     logbrush.lbColor = premoveHighlightColor; \r
2562     premovePen =\r
2563       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2564                    lineGap, &logbrush, 0, NULL);\r
2565 \r
2566     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2567     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2568       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2569       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2570         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2571       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2572         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2573       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2574     }\r
2575     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2576       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2577       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2578         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2579         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2580       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2581         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2582       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2583     }\r
2584   }\r
2585 \r
2586   /* [HGM] Licensing requirement */\r
2587 #ifdef GOTHIC\r
2588   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2589 #endif\r
2590 #ifdef FALCON\r
2591   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2592 #endif\r
2593   GothicPopUp( "", VariantNormal);\r
2594 \r
2595 \r
2596 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2597 \r
2598   /* Load piece bitmaps for this board size */\r
2599   for (i=0; i<=2; i++) {\r
2600     for (piece = WhitePawn;\r
2601          (int) piece < (int) BlackPawn;\r
2602          piece = (ChessSquare) ((int) piece + 1)) {\r
2603       if (pieceBitmap[i][piece] != NULL)\r
2604         DeleteObject(pieceBitmap[i][piece]);\r
2605       pieceBitmap[i][piece] = NULL;\r
2606     }\r
2607   }\r
2608 \r
2609   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2610 \r
2611   // Orthodox Chess pieces\r
2612   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2613   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2614   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2615   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2616   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2617   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2618   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2619   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2620   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2621   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2622   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2623   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2624   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2625   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2626   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2627   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2628     // in Shogi, Hijack the unused Queen for Lance\r
2629     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2630     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2631     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2632   } else {\r
2633     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2634     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2635     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2636   }\r
2637 \r
2638   if(squareSize <= 72 && squareSize >= 33) { \r
2639     /* A & C are available in most sizes now */\r
2640     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2641       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2642       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2643       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2644       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2645       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2646       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2647       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2648       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2649       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2650       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2651       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2652       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2653     } else { // Smirf-like\r
2654       if(gameInfo.variant == VariantSChess) {\r
2655         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2656         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2657         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2658       } else {\r
2659         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2660         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2661         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2662       }\r
2663     }\r
2664     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2665       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2666       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2667       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2668     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2669       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2670       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2671       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2672     } else { // WinBoard standard\r
2673       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2674       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2675       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2676     }\r
2677   }\r
2678 \r
2679 \r
2680   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2681     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2682     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2683     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2684     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2685     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2686     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2687     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2688     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2689     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2690     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2691     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2692     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2693     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2694     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2695     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2696     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2697     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2698     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2699     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2700     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2701     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2702     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2703     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2704     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2705     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2706     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2707     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2708     pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2709     pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2710     pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2711     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2712     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2713     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2714     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2715     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2716     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2717     pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2718     pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2719     pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2720     pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");\r
2721     pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");\r
2722     pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");\r
2723     pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");\r
2724     pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");\r
2725     pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");\r
2726     pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");\r
2727     pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");\r
2728     pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2729 \r
2730     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
2731       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2732       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2733       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2734       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2735       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2736       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2737       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2738       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2739       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2740       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2741       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2742       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2743     } else {\r
2744       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2745       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2746       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2747       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2748       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2749       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2750       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2751       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2752       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2753       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2754       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2755       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2756     }\r
2757 \r
2758   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2759     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2760     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2761     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2762     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2763     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2764     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2765     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2766     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2767     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2768     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2769     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2770     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2771     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2772     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2773   }\r
2774 \r
2775 \r
2776   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2777   /* special Shogi support in this size */\r
2778   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2779       for (piece = WhitePawn;\r
2780            (int) piece < (int) BlackPawn;\r
2781            piece = (ChessSquare) ((int) piece + 1)) {\r
2782         if (pieceBitmap[i][piece] != NULL)\r
2783           DeleteObject(pieceBitmap[i][piece]);\r
2784       }\r
2785     }\r
2786   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2787   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2788   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2789   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2790   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2791   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2792   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2793   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2794   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2795   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2796   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2797   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2798   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2799   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2800   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2801   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2802   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2803   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2804   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2805   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2806   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2807   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2808   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2809   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2810   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2811   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2812   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2813   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2814   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2815   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2816   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2817   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2818   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2819   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2820   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2821   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2822   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2823   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2824   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2825   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2826   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2827   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2828   minorSize = 0;\r
2829   }\r
2830 \r
2831   if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention\r
2832     char buf[MSG_SIZ];\r
2833     if(pieceBitmap[0][i]) continue;\r
2834     snprintf(buf, MSG_SIZ, "piece%d_", i);\r
2835     pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");\r
2836     pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");\r
2837     pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");\r
2838   }\r
2839 }\r
2840 \r
2841 HBITMAP\r
2842 PieceBitmap(ChessSquare p, int kind)\r
2843 {\r
2844   if ((int) p >= (int) BlackPawn)\r
2845     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2846 \r
2847   return pieceBitmap[kind][(int) p];\r
2848 }\r
2849 \r
2850 /***************************************************************/\r
2851 \r
2852 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2853 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2854 /*\r
2855 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2856 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2857 */\r
2858 \r
2859 VOID\r
2860 SquareToPos(int row, int column, int * x, int * y)\r
2861 {\r
2862   if (flipView) {\r
2863     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2864     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2865   } else {\r
2866     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2867     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2868   }\r
2869 }\r
2870 \r
2871 VOID\r
2872 DrawCoordsOnDC(HDC hdc)\r
2873 {\r
2874   static char files[] = "0123456789012345678901221098765432109876543210";\r
2875   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2876   char str[2] = { NULLCHAR, NULLCHAR };\r
2877   int oldMode, oldAlign, x, y, start, i;\r
2878   HFONT oldFont;\r
2879   HBRUSH oldBrush;\r
2880 \r
2881   if (!appData.showCoords)\r
2882     return;\r
2883 \r
2884   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2885 \r
2886   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2887   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2888   oldAlign = GetTextAlign(hdc);\r
2889   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2890 \r
2891   y = boardRect.top + lineGap;\r
2892   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2893 \r
2894   if(border) {\r
2895     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2896     x += border - lineGap - 4; y += squareSize - 6;\r
2897   } else\r
2898   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2899   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2900     str[0] = files[start + i];\r
2901     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2902     y += squareSize + lineGap;\r
2903   }\r
2904 \r
2905   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2906 \r
2907   if(border) {\r
2908     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2909     x += -border + 4; y += border - squareSize + 6;\r
2910   } else\r
2911   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2912   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2913     str[0] = ranks[start + i];\r
2914     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2915     x += squareSize + lineGap;\r
2916   }    \r
2917 \r
2918   SelectObject(hdc, oldBrush);\r
2919   SetBkMode(hdc, oldMode);\r
2920   SetTextAlign(hdc, oldAlign);\r
2921   SelectObject(hdc, oldFont);\r
2922 }\r
2923 \r
2924 VOID\r
2925 DrawGridOnDC(HDC hdc)\r
2926 {\r
2927   HPEN oldPen;\r
2928  \r
2929   if (lineGap != 0) {\r
2930     oldPen = SelectObject(hdc, gridPen);\r
2931     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2932     SelectObject(hdc, oldPen);\r
2933   }\r
2934 }\r
2935 \r
2936 #define HIGHLIGHT_PEN 0\r
2937 #define PREMOVE_PEN   1\r
2938 \r
2939 VOID\r
2940 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2941 {\r
2942   int x1, y1;\r
2943   HPEN oldPen, hPen;\r
2944   if (lineGap == 0) return;\r
2945   if (flipView) {\r
2946     x1 = boardRect.left +\r
2947       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2948     y1 = boardRect.top +\r
2949       lineGap/2 + y * (squareSize + lineGap) + border;\r
2950   } else {\r
2951     x1 = boardRect.left +\r
2952       lineGap/2 + x * (squareSize + lineGap) + border;\r
2953     y1 = boardRect.top +\r
2954       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2955   }\r
2956   hPen = pen ? premovePen : highlightPen;\r
2957   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2958   MoveToEx(hdc, x1, y1, NULL);\r
2959   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2960   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2961   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2962   LineTo(hdc, x1, y1);\r
2963   SelectObject(hdc, oldPen);\r
2964 }\r
2965 \r
2966 VOID\r
2967 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2968 {\r
2969   int i;\r
2970   for (i=0; i<2; i++) {\r
2971     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2972       DrawHighlightOnDC(hdc, TRUE,\r
2973                         h->sq[i].x, h->sq[i].y,\r
2974                         pen);\r
2975   }\r
2976 }\r
2977 \r
2978 /* Note: sqcolor is used only in monoMode */\r
2979 /* Note that this code is largely duplicated in woptions.c,\r
2980    function DrawSampleSquare, so that needs to be updated too */\r
2981 VOID\r
2982 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2983 {\r
2984   HBITMAP oldBitmap;\r
2985   HBRUSH oldBrush;\r
2986   int tmpSize;\r
2987 \r
2988   if (appData.blindfold) return;\r
2989 \r
2990   /* [AS] Use font-based pieces if needed */\r
2991   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2992     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2993     CreatePiecesFromFont();\r
2994 \r
2995     if( fontBitmapSquareSize == squareSize ) {\r
2996         int index = TranslatePieceToFontPiece(piece);\r
2997 \r
2998         SelectObject( tmphdc, hPieceMask[ index ] );\r
2999 \r
3000       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3001         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
3002       else\r
3003         BitBlt( hdc,\r
3004             x, y,\r
3005             squareSize, squareSize,\r
3006             tmphdc,\r
3007             0, 0,\r
3008             SRCAND );\r
3009 \r
3010         SelectObject( tmphdc, hPieceFace[ index ] );\r
3011 \r
3012       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3013         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
3014       else\r
3015         BitBlt( hdc,\r
3016             x, y,\r
3017             squareSize, squareSize,\r
3018             tmphdc,\r
3019             0, 0,\r
3020             SRCPAINT );\r
3021 \r
3022         return;\r
3023     }\r
3024   }\r
3025 \r
3026   if (appData.monoMode) {\r
3027     SelectObject(tmphdc, PieceBitmap(piece, \r
3028       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3029     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3030            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3031   } else {\r
3032     HBRUSH xBrush = whitePieceBrush;\r
3033     tmpSize = squareSize;\r
3034     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
3035     if(minorSize &&\r
3036         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3037          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3038       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3039       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3040       x += (squareSize - minorSize)>>1;\r
3041       y += squareSize - minorSize - 2;\r
3042       tmpSize = minorSize;\r
3043     }\r
3044     if (color || appData.allWhite ) {\r
3045       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3046       if( color )\r
3047               oldBrush = SelectObject(hdc, xBrush);\r
3048       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3049       if(appData.upsideDown && color==flipView)\r
3050         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3051       else\r
3052         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3053       /* Use black for outline of white pieces */\r
3054       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3055       if(appData.upsideDown && color==flipView)\r
3056         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3057       else\r
3058         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3059     } else if(appData.pieceDirectory[0]) {\r
3060       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3061       oldBrush = SelectObject(hdc, xBrush);\r
3062       if(appData.upsideDown && color==flipView)\r
3063         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3064       else\r
3065         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3066       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3067       if(appData.upsideDown && color==flipView)\r
3068         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3069       else\r
3070         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3071     } else {\r
3072       /* Use square color for details of black pieces */\r
3073       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3074       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3075       if(appData.upsideDown && !flipView)\r
3076         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3077       else\r
3078         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3079     }\r
3080     SelectObject(hdc, oldBrush);\r
3081     SelectObject(tmphdc, oldBitmap);\r
3082   }\r
3083 }\r
3084 \r
3085 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3086 int GetBackTextureMode( int algo )\r
3087 {\r
3088     int result = BACK_TEXTURE_MODE_DISABLED;\r
3089 \r
3090     switch( algo ) \r
3091     {\r
3092         case BACK_TEXTURE_MODE_PLAIN:\r
3093             result = 1; /* Always use identity map */\r
3094             break;\r
3095         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3096             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3097             break;\r
3098     }\r
3099 \r
3100     return result;\r
3101 }\r
3102 \r
3103 /* \r
3104     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3105     to handle redraws cleanly (as random numbers would always be different).\r
3106 */\r
3107 VOID RebuildTextureSquareInfo()\r
3108 {\r
3109     BITMAP bi;\r
3110     int lite_w = 0;\r
3111     int lite_h = 0;\r
3112     int dark_w = 0;\r
3113     int dark_h = 0;\r
3114     int row;\r
3115     int col;\r
3116 \r
3117     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3118 \r
3119     if( liteBackTexture != NULL ) {\r
3120         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3121             lite_w = bi.bmWidth;\r
3122             lite_h = bi.bmHeight;\r
3123         }\r
3124     }\r
3125 \r
3126     if( darkBackTexture != NULL ) {\r
3127         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3128             dark_w = bi.bmWidth;\r
3129             dark_h = bi.bmHeight;\r
3130         }\r
3131     }\r
3132 \r
3133     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3134         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3135             if( (col + row) & 1 ) {\r
3136                 /* Lite square */\r
3137                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3138                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3139                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3140                   else\r
3141                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3142                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3143                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3144                   else\r
3145                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3146                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3147                 }\r
3148             }\r
3149             else {\r
3150                 /* Dark square */\r
3151                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3152                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3153                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3154                   else\r
3155                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3156                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3157                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3158                   else\r
3159                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3160                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3161                 }\r
3162             }\r
3163         }\r
3164     }\r
3165 }\r
3166 \r
3167 /* [AS] Arrow highlighting support */\r
3168 \r
3169 static double A_WIDTH = 5; /* Width of arrow body */\r
3170 \r
3171 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3172 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3173 \r
3174 static double Sqr( double x )\r
3175 {\r
3176     return x*x;\r
3177 }\r
3178 \r
3179 static int Round( double x )\r
3180 {\r
3181     return (int) (x + 0.5);\r
3182 }\r
3183 \r
3184 /* Draw an arrow between two points using current settings */\r
3185 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3186 {\r
3187     POINT arrow[7];\r
3188     double dx, dy, j, k, x, y;\r
3189 \r
3190     if( d_x == s_x ) {\r
3191         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3192 \r
3193         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3194         arrow[0].y = s_y;\r
3195 \r
3196         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3197         arrow[1].y = d_y - h;\r
3198 \r
3199         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3200         arrow[2].y = d_y - h;\r
3201 \r
3202         arrow[3].x = d_x;\r
3203         arrow[3].y = d_y;\r
3204 \r
3205         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3206         arrow[5].y = d_y - h;\r
3207 \r
3208         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3209         arrow[4].y = d_y - h;\r
3210 \r
3211         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3212         arrow[6].y = s_y;\r
3213     }\r
3214     else if( d_y == s_y ) {\r
3215         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3216 \r
3217         arrow[0].x = s_x;\r
3218         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3219 \r
3220         arrow[1].x = d_x - w;\r
3221         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3222 \r
3223         arrow[2].x = d_x - w;\r
3224         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3225 \r
3226         arrow[3].x = d_x;\r
3227         arrow[3].y = d_y;\r
3228 \r
3229         arrow[5].x = d_x - w;\r
3230         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3231 \r
3232         arrow[4].x = d_x - w;\r
3233         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3234 \r
3235         arrow[6].x = s_x;\r
3236         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3237     }\r
3238     else {\r
3239         /* [AS] Needed a lot of paper for this! :-) */\r
3240         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3241         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3242   \r
3243         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3244 \r
3245         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3246 \r
3247         x = s_x;\r
3248         y = s_y;\r
3249 \r
3250         arrow[0].x = Round(x - j);\r
3251         arrow[0].y = Round(y + j*dx);\r
3252 \r
3253         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3254         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3255 \r
3256         if( d_x > s_x ) {\r
3257             x = (double) d_x - k;\r
3258             y = (double) d_y - k*dy;\r
3259         }\r
3260         else {\r
3261             x = (double) d_x + k;\r
3262             y = (double) d_y + k*dy;\r
3263         }\r
3264 \r
3265         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3266 \r
3267         arrow[6].x = Round(x - j);\r
3268         arrow[6].y = Round(y + j*dx);\r
3269 \r
3270         arrow[2].x = Round(arrow[6].x + 2*j);\r
3271         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3272 \r
3273         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3274         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3275 \r
3276         arrow[4].x = d_x;\r
3277         arrow[4].y = d_y;\r
3278 \r
3279         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3280         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3281     }\r
3282 \r
3283     Polygon( hdc, arrow, 7 );\r
3284 }\r
3285 \r
3286 /* [AS] Draw an arrow between two squares */\r
3287 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3288 {\r
3289     int s_x, s_y, d_x, d_y;\r
3290     HPEN hpen;\r
3291     HPEN holdpen;\r
3292     HBRUSH hbrush;\r
3293     HBRUSH holdbrush;\r
3294     LOGBRUSH stLB;\r
3295 \r
3296     if( s_col == d_col && s_row == d_row ) {\r
3297         return;\r
3298     }\r
3299 \r
3300     /* Get source and destination points */\r
3301     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3302     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3303 \r
3304     if( d_y > s_y ) {\r
3305         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3306     }\r
3307     else if( d_y < s_y ) {\r
3308         d_y += squareSize / 2 + squareSize / 4;\r
3309     }\r
3310     else {\r
3311         d_y += squareSize / 2;\r
3312     }\r
3313 \r
3314     if( d_x > s_x ) {\r
3315         d_x += squareSize / 2 - squareSize / 4;\r
3316     }\r
3317     else if( d_x < s_x ) {\r
3318         d_x += squareSize / 2 + squareSize / 4;\r
3319     }\r
3320     else {\r
3321         d_x += squareSize / 2;\r
3322     }\r
3323 \r
3324     s_x += squareSize / 2;\r
3325     s_y += squareSize / 2;\r
3326 \r
3327     /* Adjust width */\r
3328     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3329 \r
3330     /* Draw */\r
3331     stLB.lbStyle = BS_SOLID;\r
3332     stLB.lbColor = appData.highlightArrowColor;\r
3333     stLB.lbHatch = 0;\r
3334 \r
3335     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3336     holdpen = SelectObject( hdc, hpen );\r
3337     hbrush = CreateBrushIndirect( &stLB );\r
3338     holdbrush = SelectObject( hdc, hbrush );\r
3339 \r
3340     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3341 \r
3342     SelectObject( hdc, holdpen );\r
3343     SelectObject( hdc, holdbrush );\r
3344     DeleteObject( hpen );\r
3345     DeleteObject( hbrush );\r
3346 }\r
3347 \r
3348 BOOL HasHighlightInfo()\r
3349 {\r
3350     BOOL result = FALSE;\r
3351 \r
3352     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3353         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3354     {\r
3355         result = TRUE;\r
3356     }\r
3357 \r
3358     return result;\r
3359 \r
3360 \r
3361 \r
3362 }\r
3363 \r
3364 BOOL IsDrawArrowEnabled()\r
3365 {\r
3366     BOOL result = FALSE;\r
3367 \r
3368     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3369         result = TRUE;\r
3370     }\r
3371 \r
3372     return result;\r
3373 }\r
3374 \r
3375 VOID DrawArrowHighlight( HDC hdc )\r
3376 {\r
3377     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3378         DrawArrowBetweenSquares( hdc,\r
3379             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3380             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3381     }\r
3382 }\r
3383 \r
3384 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3385 {\r
3386     HRGN result = NULL;\r
3387 \r
3388     if( HasHighlightInfo() ) {\r
3389         int x1, y1, x2, y2;\r
3390         int sx, sy, dx, dy;\r
3391 \r
3392         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3393         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3394 \r
3395         sx = MIN( x1, x2 );\r
3396         sy = MIN( y1, y2 );\r
3397         dx = MAX( x1, x2 ) + squareSize;\r
3398         dy = MAX( y1, y2 ) + squareSize;\r
3399 \r
3400         result = CreateRectRgn( sx, sy, dx, dy );\r
3401     }\r
3402 \r
3403     return result;\r
3404 }\r
3405 \r
3406 /*\r
3407     Warning: this function modifies the behavior of several other functions. \r
3408     \r
3409     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3410     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3411     repaint is scattered all over the place, which is not good for features such as\r
3412     "arrow highlighting" that require a full repaint of the board.\r
3413 \r
3414     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3415     user interaction, when speed is not so important) but especially to avoid errors\r
3416     in the displayed graphics.\r
3417 \r
3418     In such patched places, I always try refer to this function so there is a single\r
3419     place to maintain knowledge.\r
3420     \r
3421     To restore the original behavior, just return FALSE unconditionally.\r
3422 */\r
3423 BOOL IsFullRepaintPreferrable()\r
3424 {\r
3425     BOOL result = FALSE;\r
3426 \r
3427     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3428         /* Arrow may appear on the board */\r
3429         result = TRUE;\r
3430     }\r
3431 \r
3432     return result;\r
3433 }\r
3434 \r
3435 /* \r
3436     This function is called by DrawPosition to know whether a full repaint must\r
3437     be forced or not.\r
3438 \r
3439     Only DrawPosition may directly call this function, which makes use of \r
3440     some state information. Other function should call DrawPosition specifying \r
3441     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3442 */\r
3443 BOOL DrawPositionNeedsFullRepaint()\r
3444 {\r
3445     BOOL result = FALSE;\r
3446 \r
3447     /* \r
3448         Probably a slightly better policy would be to trigger a full repaint\r
3449         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3450         but animation is fast enough that it's difficult to notice.\r
3451     */\r
3452     if( animInfo.piece == EmptySquare ) {\r
3453         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3454             result = TRUE;\r
3455         }\r
3456     }\r
3457 \r
3458     return result;\r
3459 }\r
3460 \r
3461 static HBITMAP borderBitmap;\r
3462 \r
3463 VOID\r
3464 DrawBackgroundOnDC(HDC hdc)\r
3465 {\r
3466   \r
3467   BITMAP bi;\r
3468   HDC tmphdc;\r
3469   HBITMAP hbm;\r
3470   static char oldBorder[MSG_SIZ];\r
3471   int w = 600, h = 600, mode;\r
3472 \r
3473   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3474     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3475     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3476   }\r
3477   if(borderBitmap == NULL) { // loading failed, use white\r
3478     FillRect( hdc, &boardRect, whitePieceBrush );\r
3479     return;\r
3480   }\r
3481   tmphdc = CreateCompatibleDC(hdc);\r
3482   hbm = SelectObject(tmphdc, borderBitmap);\r
3483   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3484             w = bi.bmWidth;\r
3485             h = bi.bmHeight;\r
3486   }\r
3487   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3488   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3489                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3490   SetStretchBltMode(hdc, mode);\r
3491   SelectObject(tmphdc, hbm);\r
3492   DeleteDC(tmphdc);\r
3493 }\r
3494 \r
3495 VOID\r
3496 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3497 {\r
3498   int row, column, x, y, square_color, piece_color;\r
3499   ChessSquare piece;\r
3500   HBRUSH oldBrush;\r
3501   HDC texture_hdc = NULL;\r
3502 \r
3503   /* [AS] Initialize background textures if needed */\r
3504   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3505       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3506       if( backTextureSquareSize != squareSize \r
3507        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3508           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3509           backTextureSquareSize = squareSize;\r
3510           RebuildTextureSquareInfo();\r
3511       }\r
3512 \r
3513       texture_hdc = CreateCompatibleDC( hdc );\r
3514   }\r
3515 \r
3516   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3517     for (column = 0; column < BOARD_WIDTH; column++) {\r
3518   \r
3519       SquareToPos(row, column, &x, &y);\r
3520 \r
3521       piece = board[row][column];\r
3522 \r
3523       square_color = ((column + row) % 2) == 1;\r
3524       if( gameInfo.variant == VariantXiangqi ) {\r
3525           square_color = !InPalace(row, column);\r
3526           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3527           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3528       }\r
3529       piece_color = (int) piece < (int) BlackPawn;\r
3530 \r
3531 \r
3532       /* [HGM] holdings file: light square or black */\r
3533       if(column == BOARD_LEFT-2) {\r
3534             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3535                 square_color = 1;\r
3536             else {\r
3537                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3538                 continue;\r
3539             }\r
3540       } else\r
3541       if(column == BOARD_RGHT + 1 ) {\r
3542             if( row < gameInfo.holdingsSize )\r
3543                 square_color = 1;\r
3544             else {\r
3545                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3546                 continue;\r
3547             }\r
3548       }\r
3549       if(column == BOARD_LEFT-1 ) /* left align */\r
3550             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3551       else if( column == BOARD_RGHT) /* right align */\r
3552             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3553       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3554       else\r
3555       if (appData.monoMode) {\r
3556         if (piece == EmptySquare) {\r
3557           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3558                  square_color ? WHITENESS : BLACKNESS);\r
3559         } else {\r
3560           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3561         }\r
3562       } \r
3563       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3564           /* [AS] Draw the square using a texture bitmap */\r
3565           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3566           int r = row, c = column; // [HGM] do not flip board in flipView\r
3567           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3568 \r
3569           DrawTile( x, y, \r
3570               squareSize, squareSize, \r
3571               hdc, \r
3572               texture_hdc,\r
3573               backTextureSquareInfo[r][c].mode,\r
3574               backTextureSquareInfo[r][c].x,\r
3575               backTextureSquareInfo[r][c].y );\r
3576 \r
3577           SelectObject( texture_hdc, hbm );\r
3578 \r
3579           if (piece != EmptySquare) {\r
3580               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3581           }\r
3582       }\r
3583       else {\r
3584         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3585 \r
3586         oldBrush = SelectObject(hdc, brush );\r
3587         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3588         SelectObject(hdc, oldBrush);\r
3589         if (piece != EmptySquare)\r
3590           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3591       }\r
3592     }\r
3593   }\r
3594 \r
3595   if( texture_hdc != NULL ) {\r
3596     DeleteDC( texture_hdc );\r
3597   }\r
3598 }\r
3599 \r
3600 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3601 void fputDW(FILE *f, int x)\r
3602 {\r
3603         fputc(x     & 255, f);\r
3604         fputc(x>>8  & 255, f);\r
3605         fputc(x>>16 & 255, f);\r
3606         fputc(x>>24 & 255, f);\r
3607 }\r
3608 \r
3609 #define MAX_CLIPS 200   /* more than enough */\r
3610 \r
3611 VOID\r
3612 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3613 {\r
3614 //  HBITMAP bufferBitmap;\r
3615   BITMAP bi;\r
3616 //  RECT Rect;\r
3617   HDC tmphdc;\r
3618   HBITMAP hbm;\r
3619   int w = 100, h = 50;\r
3620 \r
3621   if(logo == NULL) {\r
3622     if(!logoHeight) return;\r
3623     FillRect( hdc, &logoRect, whitePieceBrush );\r
3624   }\r
3625 //  GetClientRect(hwndMain, &Rect);\r
3626 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3627 //                                      Rect.bottom-Rect.top+1);\r
3628   tmphdc = CreateCompatibleDC(hdc);\r
3629   hbm = SelectObject(tmphdc, logo);\r
3630   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3631             w = bi.bmWidth;\r
3632             h = bi.bmHeight;\r
3633   }\r
3634   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3635                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3636   SelectObject(tmphdc, hbm);\r
3637   DeleteDC(tmphdc);\r
3638 }\r
3639 \r
3640 VOID\r
3641 DisplayLogos()\r
3642 {\r
3643   if(logoHeight) {\r
3644         HDC hdc = GetDC(hwndMain);\r
3645         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3646         if(appData.autoLogo) {\r
3647           \r
3648           switch(gameMode) { // pick logos based on game mode\r
3649             case IcsObserving:\r
3650                 whiteLogo = second.programLogo; // ICS logo\r
3651                 blackLogo = second.programLogo;\r
3652             default:\r
3653                 break;\r
3654             case IcsPlayingWhite:\r
3655                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3656                 blackLogo = second.programLogo; // ICS logo\r
3657                 break;\r
3658             case IcsPlayingBlack:\r
3659                 whiteLogo = second.programLogo; // ICS logo\r
3660                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3661                 break;\r
3662             case TwoMachinesPlay:\r
3663                 if(first.twoMachinesColor[0] == 'b') {\r
3664                     whiteLogo = second.programLogo;\r
3665                     blackLogo = first.programLogo;\r
3666                 }\r
3667                 break;\r
3668             case MachinePlaysWhite:\r
3669                 blackLogo = userLogo;\r
3670                 break;\r
3671             case MachinePlaysBlack:\r
3672                 whiteLogo = userLogo;\r
3673                 blackLogo = first.programLogo;\r
3674           }\r
3675         }\r
3676         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3677         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3678         ReleaseDC(hwndMain, hdc);\r
3679   }\r
3680 }\r
3681 \r
3682 void\r
3683 UpdateLogos(int display)\r
3684 { // called after loading new engine(s), in tourney or from menu\r
3685   LoadLogo(&first, 0, FALSE);\r
3686   LoadLogo(&second, 1, appData.icsActive);\r
3687   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3688   if(display) DisplayLogos();\r
3689 }\r
3690 \r
3691 static HDC hdcSeek;\r
3692 \r
3693 // [HGM] seekgraph\r
3694 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3695 {\r
3696     POINT stPt;\r
3697     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3698     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3699     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3700     SelectObject( hdcSeek, hp );\r
3701 }\r
3702 \r
3703 // front-end wrapper for drawing functions to do rectangles\r
3704 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3705 {\r
3706     HPEN hp;\r
3707     RECT rc;\r
3708 \r
3709     if (hdcSeek == NULL) {\r
3710     hdcSeek = GetDC(hwndMain);\r
3711       if (!appData.monoMode) {\r
3712         SelectPalette(hdcSeek, hPal, FALSE);\r
3713         RealizePalette(hdcSeek);\r
3714       }\r
3715     }\r
3716     hp = SelectObject( hdcSeek, gridPen );\r
3717     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3718     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3719     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3720     SelectObject( hdcSeek, hp );\r
3721 }\r
3722 \r
3723 // front-end wrapper for putting text in graph\r
3724 void DrawSeekText(char *buf, int x, int y)\r
3725 {\r
3726         SIZE stSize;\r
3727         SetBkMode( hdcSeek, TRANSPARENT );\r
3728         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3729         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3730 }\r
3731 \r
3732 void DrawSeekDot(int x, int y, int color)\r
3733 {\r
3734         int square = color & 0x80;\r
3735         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3736                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3737         color &= 0x7F;\r
3738         if(square)\r
3739             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3740                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3741         else\r
3742             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3743                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3744             SelectObject(hdcSeek, oldBrush);\r
3745 }\r
3746 \r
3747 void DrawSeekOpen()\r
3748 {\r
3749 }\r
3750 \r
3751 void DrawSeekClose()\r
3752 {\r
3753 }\r
3754 \r
3755 VOID\r
3756 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3757 {\r
3758   static Board lastReq[2], lastDrawn[2];\r
3759   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3760   static int lastDrawnFlipView = 0;\r
3761   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3762   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3763   HDC tmphdc;\r
3764   HDC hdcmem;\r
3765   HBITMAP bufferBitmap;\r
3766   HBITMAP oldBitmap;\r
3767   RECT Rect;\r
3768   HRGN clips[MAX_CLIPS];\r
3769   ChessSquare dragged_piece = EmptySquare;\r
3770   int nr = twoBoards*partnerUp;\r
3771 \r
3772   /* I'm undecided on this - this function figures out whether a full\r
3773    * repaint is necessary on its own, so there's no real reason to have the\r
3774    * caller tell it that.  I think this can safely be set to FALSE - but\r
3775    * if we trust the callers not to request full repaints unnessesarily, then\r
3776    * we could skip some clipping work.  In other words, only request a full\r
3777    * redraw when the majority of pieces have changed positions (ie. flip, \r
3778    * gamestart and similar)  --Hawk\r
3779    */\r
3780   Boolean fullrepaint = repaint;\r
3781 \r
3782   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3783 \r
3784   if( DrawPositionNeedsFullRepaint() ) {\r
3785       fullrepaint = TRUE;\r
3786   }\r
3787 \r
3788   if (board == NULL) {\r
3789     if (!lastReqValid[nr]) {\r
3790       return;\r
3791     }\r
3792     board = lastReq[nr];\r
3793   } else {\r
3794     CopyBoard(lastReq[nr], board);\r
3795     lastReqValid[nr] = 1;\r
3796   }\r
3797 \r
3798   if (doingSizing) {\r
3799     return;\r
3800   }\r
3801 \r
3802   if (IsIconic(hwndMain)) {\r
3803     return;\r
3804   }\r
3805 \r
3806   if (hdc == NULL) {\r
3807     hdc = GetDC(hwndMain);\r
3808     if (!appData.monoMode) {\r
3809       SelectPalette(hdc, hPal, FALSE);\r
3810       RealizePalette(hdc);\r
3811     }\r
3812     releaseDC = TRUE;\r
3813   } else {\r
3814     releaseDC = FALSE;\r
3815   }\r
3816 \r
3817   /* Create some work-DCs */\r
3818   hdcmem = CreateCompatibleDC(hdc);\r
3819   tmphdc = CreateCompatibleDC(hdc);\r
3820 \r
3821   /* If dragging is in progress, we temporarely remove the piece */\r
3822   /* [HGM] or temporarily decrease count if stacked              */\r
3823   /*       !! Moved to before board compare !!                   */\r
3824   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3825     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3826     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3827             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3828         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3829     } else \r
3830     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3831             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3832         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3833     } else \r
3834         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3835   }\r
3836 \r
3837   /* Figure out which squares need updating by comparing the \r
3838    * newest board with the last drawn board and checking if\r
3839    * flipping has changed.\r
3840    */\r
3841   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3842     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3843       for (column = 0; column < BOARD_WIDTH; column++) {\r
3844         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3845           SquareToPos(row, column, &x, &y);\r
3846           clips[num_clips++] =\r
3847             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3848         }\r
3849       }\r
3850     }\r
3851    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3852     for (i=0; i<2; i++) {\r
3853       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3854           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3855         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3856             lastDrawnHighlight.sq[i].y >= 0) {\r
3857           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3858                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3859           clips[num_clips++] =\r
3860             CreateRectRgn(x - lineGap, y - lineGap, \r
3861                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3862         }\r
3863         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3864           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3865           clips[num_clips++] =\r
3866             CreateRectRgn(x - lineGap, y - lineGap, \r
3867                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3868         }\r
3869       }\r
3870     }\r
3871     for (i=0; i<2; i++) {\r
3872       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3873           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3874         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3875             lastDrawnPremove.sq[i].y >= 0) {\r
3876           SquareToPos(lastDrawnPremove.sq[i].y,\r
3877                       lastDrawnPremove.sq[i].x, &x, &y);\r
3878           clips[num_clips++] =\r
3879             CreateRectRgn(x - lineGap, y - lineGap, \r
3880                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3881         }\r
3882         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3883             premoveHighlightInfo.sq[i].y >= 0) {\r
3884           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3885                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3886           clips[num_clips++] =\r
3887             CreateRectRgn(x - lineGap, y - lineGap, \r
3888                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3889         }\r
3890       }\r
3891     }\r
3892    } else { // nr == 1\r
3893         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3894         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3895         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3896         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3897       for (i=0; i<2; i++) {\r
3898         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3899             partnerHighlightInfo.sq[i].y >= 0) {\r
3900           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3901                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3902           clips[num_clips++] =\r
3903             CreateRectRgn(x - lineGap, y - lineGap, \r
3904                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3905         }\r
3906         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3907             oldPartnerHighlight.sq[i].y >= 0) {\r
3908           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3909                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3910           clips[num_clips++] =\r
3911             CreateRectRgn(x - lineGap, y - lineGap, \r
3912                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3913         }\r
3914       }\r
3915    }\r
3916   } else {\r
3917     fullrepaint = TRUE;\r
3918   }\r
3919 \r
3920   /* Create a buffer bitmap - this is the actual bitmap\r
3921    * being written to.  When all the work is done, we can\r
3922    * copy it to the real DC (the screen).  This avoids\r
3923    * the problems with flickering.\r
3924    */\r
3925   GetClientRect(hwndMain, &Rect);\r
3926   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3927                                         Rect.bottom-Rect.top+1);\r
3928   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3929   if (!appData.monoMode) {\r
3930     SelectPalette(hdcmem, hPal, FALSE);\r
3931   }\r
3932 \r
3933   /* Create clips for dragging */\r
3934   if (!fullrepaint) {\r
3935     if (dragInfo.from.x >= 0) {\r
3936       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3937       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3938     }\r
3939     if (dragInfo.start.x >= 0) {\r
3940       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3941       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3942     }\r
3943     if (dragInfo.pos.x >= 0) {\r
3944       x = dragInfo.pos.x - squareSize / 2;\r
3945       y = dragInfo.pos.y - squareSize / 2;\r
3946       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3947     }\r
3948     if (dragInfo.lastpos.x >= 0) {\r
3949       x = dragInfo.lastpos.x - squareSize / 2;\r
3950       y = dragInfo.lastpos.y - squareSize / 2;\r
3951       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3952     }\r
3953   }\r
3954 \r
3955   /* Are we animating a move?  \r
3956    * If so, \r
3957    *   - remove the piece from the board (temporarely)\r
3958    *   - calculate the clipping region\r
3959    */\r
3960   if (!fullrepaint) {\r
3961     if (animInfo.piece != EmptySquare) {\r
3962       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3963       x = boardRect.left + animInfo.lastpos.x;\r
3964       y = boardRect.top + animInfo.lastpos.y;\r
3965       x2 = boardRect.left + animInfo.pos.x;\r
3966       y2 = boardRect.top + animInfo.pos.y;\r
3967       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3968       /* Slight kludge.  The real problem is that after AnimateMove is\r
3969          done, the position on the screen does not match lastDrawn.\r
3970          This currently causes trouble only on e.p. captures in\r
3971          atomic, where the piece moves to an empty square and then\r
3972          explodes.  The old and new positions both had an empty square\r
3973          at the destination, but animation has drawn a piece there and\r
3974          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3975       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3976     }\r
3977   }\r
3978 \r
3979   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3980   if (num_clips == 0)\r
3981     fullrepaint = TRUE;\r
3982 \r
3983   /* Set clipping on the memory DC */\r
3984   if (!fullrepaint) {\r
3985     SelectClipRgn(hdcmem, clips[0]);\r
3986     for (x = 1; x < num_clips; x++) {\r
3987       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3988         abort();  // this should never ever happen!\r
3989     }\r
3990   }\r
3991 \r
3992   /* Do all the drawing to the memory DC */\r
3993   if(explodeInfo.radius) { // [HGM] atomic\r
3994         HBRUSH oldBrush;\r
3995         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3996         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3997         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3998         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3999         x += squareSize/2;\r
4000         y += squareSize/2;\r
4001         if(!fullrepaint) {\r
4002           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4003           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4004         }\r
4005         DrawGridOnDC(hdcmem);\r
4006         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4007         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4008         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4009         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
4010         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4011         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4012         SelectObject(hdcmem, oldBrush);\r
4013   } else {\r
4014     if(border) DrawBackgroundOnDC(hdcmem);\r
4015     DrawGridOnDC(hdcmem);\r
4016     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
4017         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4018         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4019     } else {\r
4020         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
4021         oldPartnerHighlight = partnerHighlightInfo;\r
4022     }\r
4023     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4024   }\r
4025   if(nr == 0) // [HGM] dual: markers only on left board\r
4026   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4027     for (column = 0; column < BOARD_WIDTH; column++) {\r
4028         if (marker[row][column]) { // marker changes only occur with full repaint!\r
4029             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
4030             SquareToPos(row, column, &x, &y);\r
4031             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
4032                           x + 3*squareSize/4, y + 3*squareSize/4);\r
4033             SelectObject(hdcmem, oldBrush);\r
4034         }\r
4035     }\r
4036   }\r
4037 \r
4038   if( appData.highlightMoveWithArrow ) {\r
4039 \r
4040     DrawArrowHighlight(hdcmem);\r
4041   }\r
4042 \r
4043   DrawCoordsOnDC(hdcmem);\r
4044 \r
4045   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4046                  /* to make sure lastDrawn contains what is actually drawn */\r
4047 \r
4048   /* Put the dragged piece back into place and draw it (out of place!) */\r
4049     if (dragged_piece != EmptySquare) {\r
4050     /* [HGM] or restack */\r
4051     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4052                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4053     else\r
4054     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4055                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4056 \r
4057     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4058     x = dragInfo.pos.x - squareSize / 2;\r
4059     y = dragInfo.pos.y - squareSize / 2;\r
4060     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4061                   ((int) dragInfo.piece < (int) BlackPawn), \r
4062                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4063   }   \r
4064   \r
4065   /* Put the animated piece back into place and draw it */\r
4066   if (animInfo.piece != EmptySquare) {\r
4067     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4068     x = boardRect.left + animInfo.pos.x;\r
4069     y = boardRect.top + animInfo.pos.y;\r
4070     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4071                   ((int) animInfo.piece < (int) BlackPawn),\r
4072                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4073   }\r
4074 \r
4075   /* Release the bufferBitmap by selecting in the old bitmap \r
4076    * and delete the memory DC\r
4077    */\r
4078   SelectObject(hdcmem, oldBitmap);\r
4079   DeleteDC(hdcmem);\r
4080 \r
4081   /* Set clipping on the target DC */\r
4082   if (!fullrepaint) {\r
4083     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4084         RECT rect;\r
4085         GetRgnBox(clips[x], &rect);\r
4086         DeleteObject(clips[x]);\r
4087         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4088                           rect.right + wpMain.width/2, rect.bottom);\r
4089     }\r
4090     SelectClipRgn(hdc, clips[0]);\r
4091     for (x = 1; x < num_clips; x++) {\r
4092       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4093         abort();   // this should never ever happen!\r
4094     } \r
4095   }\r
4096 \r
4097   /* Copy the new bitmap onto the screen in one go.\r
4098    * This way we avoid any flickering\r
4099    */\r
4100   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4101   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4102          boardRect.right - boardRect.left,\r
4103          boardRect.bottom - boardRect.top,\r
4104          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4105   if(saveDiagFlag) { \r
4106     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4107     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4108 \r
4109     GetObject(bufferBitmap, sizeof(b), &b);\r
4110     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4111         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4112         bih.biWidth = b.bmWidth;\r
4113         bih.biHeight = b.bmHeight;\r
4114         bih.biPlanes = 1;\r
4115         bih.biBitCount = b.bmBitsPixel;\r
4116         bih.biCompression = 0;\r
4117         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4118         bih.biXPelsPerMeter = 0;\r
4119         bih.biYPelsPerMeter = 0;\r
4120         bih.biClrUsed = 0;\r
4121         bih.biClrImportant = 0;\r
4122 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4123 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4124         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4125 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4126 \r
4127         wb = b.bmWidthBytes;\r
4128         // count colors\r
4129         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4130                 int k = ((int*) pData)[i];\r
4131                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4132                 if(j >= 16) break;\r
4133                 color[j] = k;\r
4134                 if(j >= nrColors) nrColors = j+1;\r
4135         }\r
4136         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4137                 INT p = 0;\r
4138                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4139                     for(w=0; w<(wb>>2); w+=2) {\r
4140                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4141                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4142                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4143                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4144                         pData[p++] = m | j<<4;\r
4145                     }\r
4146                     while(p&3) pData[p++] = 0;\r
4147                 }\r
4148                 fac = 3;\r
4149                 wb = ((wb+31)>>5)<<2;\r
4150         }\r
4151         // write BITMAPFILEHEADER\r
4152         fprintf(diagFile, "BM");\r
4153         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4154         fputDW(diagFile, 0);\r
4155         fputDW(diagFile, 0x36 + (fac?64:0));\r
4156         // write BITMAPINFOHEADER\r
4157         fputDW(diagFile, 40);\r
4158         fputDW(diagFile, b.bmWidth);\r
4159         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4160         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4161         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4162         fputDW(diagFile, 0);\r
4163         fputDW(diagFile, 0);\r
4164         fputDW(diagFile, 0);\r
4165         fputDW(diagFile, 0);\r
4166         fputDW(diagFile, 0);\r
4167         fputDW(diagFile, 0);\r
4168         // write color table\r
4169         if(fac)\r
4170         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4171         // write bitmap data\r
4172         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4173                 fputc(pData[i], diagFile);\r
4174         free(pData);\r
4175      }\r
4176   }\r
4177 \r
4178   SelectObject(tmphdc, oldBitmap);\r
4179 \r
4180   /* Massive cleanup */\r
4181   for (x = 0; x < num_clips; x++)\r
4182     DeleteObject(clips[x]);\r
4183 \r
4184   DeleteDC(tmphdc);\r
4185   DeleteObject(bufferBitmap);\r
4186 \r
4187   if (releaseDC) \r
4188     ReleaseDC(hwndMain, hdc);\r
4189   \r
4190   if (lastDrawnFlipView != flipView && nr == 0) {\r
4191     if (flipView)\r
4192       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4193     else\r
4194       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4195   }\r
4196 \r
4197 /*  CopyBoard(lastDrawn, board);*/\r
4198   lastDrawnHighlight = highlightInfo;\r
4199   lastDrawnPremove   = premoveHighlightInfo;\r
4200   lastDrawnFlipView = flipView;\r
4201   lastDrawnValid[nr] = 1;\r
4202 }\r
4203 \r
4204 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4205 int\r
4206 SaveDiagram(f)\r
4207      FILE *f;\r
4208 {\r
4209     saveDiagFlag = 1; diagFile = f;\r
4210     HDCDrawPosition(NULL, TRUE, NULL);\r
4211     saveDiagFlag = 0;\r
4212 \r
4213     fclose(f);\r
4214     return TRUE;\r
4215 }\r
4216 \r
4217 \r
4218 /*---------------------------------------------------------------------------*\\r
4219 | CLIENT PAINT PROCEDURE\r
4220 |   This is the main event-handler for the WM_PAINT message.\r
4221 |\r
4222 \*---------------------------------------------------------------------------*/\r
4223 VOID\r
4224 PaintProc(HWND hwnd)\r
4225 {\r
4226   HDC         hdc;\r
4227   PAINTSTRUCT ps;\r
4228   HFONT       oldFont;\r
4229 \r
4230   if((hdc = BeginPaint(hwnd, &ps))) {\r
4231     if (IsIconic(hwnd)) {\r
4232       DrawIcon(hdc, 2, 2, iconCurrent);\r
4233     } else {\r
4234       if (!appData.monoMode) {\r
4235         SelectPalette(hdc, hPal, FALSE);\r
4236         RealizePalette(hdc);\r
4237       }\r
4238       HDCDrawPosition(hdc, 1, NULL);\r
4239       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4240         flipView = !flipView; partnerUp = !partnerUp;\r
4241         HDCDrawPosition(hdc, 1, NULL);\r
4242         flipView = !flipView; partnerUp = !partnerUp;\r
4243       }\r
4244       oldFont =\r
4245         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4246       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4247                  ETO_CLIPPED|ETO_OPAQUE,\r
4248                  &messageRect, messageText, strlen(messageText), NULL);\r
4249       SelectObject(hdc, oldFont);\r
4250       DisplayBothClocks();\r
4251       DisplayLogos();\r
4252     }\r
4253     EndPaint(hwnd,&ps);\r
4254   }\r
4255 \r
4256   return;\r
4257 }\r
4258 \r
4259 \r
4260 /*\r
4261  * If the user selects on a border boundary, return -1; if off the board,\r
4262  *   return -2.  Otherwise map the event coordinate to the square.\r
4263  * The offset boardRect.left or boardRect.top must already have been\r
4264  *   subtracted from x.\r
4265  */\r
4266 int EventToSquare(x, limit)\r
4267      int x, limit;\r
4268 {\r
4269   if (x <= border)\r
4270     return -2;\r
4271   if (x < lineGap + border)\r
4272     return -1;\r
4273   x -= lineGap + border;\r
4274   if ((x % (squareSize + lineGap)) >= squareSize)\r
4275     return -1;\r
4276   x /= (squareSize + lineGap);\r
4277     if (x >= limit)\r
4278     return -2;\r
4279   return x;\r
4280 }\r
4281 \r
4282 typedef struct {\r
4283   char piece;\r
4284   int command;\r
4285   char* name;\r
4286 } DropEnable;\r
4287 \r
4288 DropEnable dropEnables[] = {\r
4289   { 'P', DP_Pawn, N_("Pawn") },\r
4290   { 'N', DP_Knight, N_("Knight") },\r
4291   { 'B', DP_Bishop, N_("Bishop") },\r
4292   { 'R', DP_Rook, N_("Rook") },\r
4293   { 'Q', DP_Queen, N_("Queen") },\r
4294 };\r
4295 \r
4296 VOID\r
4297 SetupDropMenu(HMENU hmenu)\r
4298 {\r
4299   int i, count, enable;\r
4300   char *p;\r
4301   extern char white_holding[], black_holding[];\r
4302   char item[MSG_SIZ];\r
4303 \r
4304   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4305     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4306                dropEnables[i].piece);\r
4307     count = 0;\r
4308     while (p && *p++ == dropEnables[i].piece) count++;\r
4309       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4310     enable = count > 0 || !appData.testLegality\r
4311       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4312                       && !appData.icsActive);\r
4313     ModifyMenu(hmenu, dropEnables[i].command,\r
4314                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4315                dropEnables[i].command, item);\r
4316   }\r
4317 }\r
4318 \r
4319 void DragPieceBegin(int x, int y, Boolean instantly)\r
4320 {\r
4321       dragInfo.lastpos.x = boardRect.left + x;\r
4322       dragInfo.lastpos.y = boardRect.top + y;\r
4323       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4324       dragInfo.from.x = fromX;\r
4325       dragInfo.from.y = fromY;\r
4326       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4327       dragInfo.start = dragInfo.from;\r
4328       SetCapture(hwndMain);\r
4329 }\r
4330 \r
4331 void DragPieceEnd(int x, int y)\r
4332 {\r
4333     ReleaseCapture();\r
4334     dragInfo.start.x = dragInfo.start.y = -1;\r
4335     dragInfo.from = dragInfo.start;\r
4336     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4337 }\r
4338 \r
4339 void ChangeDragPiece(ChessSquare piece)\r
4340 {\r
4341     dragInfo.piece = piece;\r
4342 }\r
4343 \r
4344 /* Event handler for mouse messages */\r
4345 VOID\r
4346 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4347 {\r
4348   int x, y, menuNr;\r
4349   POINT pt;\r
4350   static int recursive = 0;\r
4351   HMENU hmenu;\r
4352   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4353 \r
4354   if (recursive) {\r
4355     if (message == WM_MBUTTONUP) {\r
4356       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4357          to the middle button: we simulate pressing the left button too!\r
4358          */\r
4359       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4360       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4361     }\r
4362     return;\r
4363   }\r
4364   recursive++;\r
4365   \r
4366   pt.x = LOWORD(lParam);\r
4367   pt.y = HIWORD(lParam);\r
4368   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4369   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4370   if (!flipView && y >= 0) {\r
4371     y = BOARD_HEIGHT - 1 - y;\r
4372   }\r
4373   if (flipView && x >= 0) {\r
4374     x = BOARD_WIDTH - 1 - x;\r
4375   }\r
4376 \r
4377   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4378   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4379 \r
4380   switch (message) {\r
4381   case WM_LBUTTONDOWN:\r
4382       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4383         ClockClick(flipClock); break;\r
4384       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4385         ClockClick(!flipClock); break;\r
4386       }\r
4387     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4388       dragInfo.start.x = dragInfo.start.y = -1;\r
4389       dragInfo.from = dragInfo.start;\r
4390     }\r
4391     if(fromX == -1 && frozen) { // not sure where this is for\r
4392                 fromX = fromY = -1; \r
4393       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4394       break;\r
4395     }\r
4396       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4397       DrawPosition(TRUE, NULL);\r
4398     break;\r
4399 \r
4400   case WM_LBUTTONUP:\r
4401       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4402       DrawPosition(TRUE, NULL);\r
4403     break;\r
4404 \r
4405   case WM_MOUSEMOVE:\r
4406     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4407     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4408     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4409     if ((appData.animateDragging || appData.highlightDragging)\r
4410         && (wParam & MK_LBUTTON || dragging == 2)\r
4411         && dragInfo.from.x >= 0) \r
4412     {\r
4413       BOOL full_repaint = FALSE;\r
4414 \r
4415       if (appData.animateDragging) {\r
4416         dragInfo.pos = pt;\r
4417       }\r
4418       if (appData.highlightDragging) {\r
4419         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4420         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4421             full_repaint = TRUE;\r
4422         }\r
4423       }\r
4424       \r
4425       DrawPosition( full_repaint, NULL);\r
4426       \r
4427       dragInfo.lastpos = dragInfo.pos;\r
4428     }\r
4429     break;\r
4430 \r
4431   case WM_MOUSEWHEEL: // [DM]\r
4432     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4433        /* Mouse Wheel is being rolled forward\r
4434         * Play moves forward\r
4435         */\r
4436        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4437                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4438        /* Mouse Wheel is being rolled backward\r
4439         * Play moves backward\r
4440         */\r
4441        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4442                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4443     }\r
4444     break;\r
4445 \r
4446   case WM_MBUTTONUP:\r
4447   case WM_RBUTTONUP:\r
4448     ReleaseCapture();\r
4449     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4450     break;\r
4451  \r
4452   case WM_MBUTTONDOWN:\r
4453   case WM_RBUTTONDOWN:\r
4454     ErrorPopDown();\r
4455     ReleaseCapture();\r
4456     fromX = fromY = -1;\r
4457     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4458     dragInfo.start.x = dragInfo.start.y = -1;\r
4459     dragInfo.from = dragInfo.start;\r
4460     dragInfo.lastpos = dragInfo.pos;\r
4461     if (appData.highlightDragging) {\r
4462       ClearHighlights();\r
4463     }\r
4464     if(y == -2) {\r
4465       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4466       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4467           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4468       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4469           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4470       }\r
4471       break;\r
4472     }\r
4473     DrawPosition(TRUE, NULL);\r
4474 \r
4475     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4476     switch (menuNr) {\r
4477     case 0:\r
4478       if (message == WM_MBUTTONDOWN) {\r
4479         buttonCount = 3;  /* even if system didn't think so */\r
4480         if (wParam & MK_SHIFT) \r
4481           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4482         else\r
4483           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4484       } else { /* message == WM_RBUTTONDOWN */\r
4485         /* Just have one menu, on the right button.  Windows users don't\r
4486            think to try the middle one, and sometimes other software steals\r
4487            it, or it doesn't really exist. */\r
4488         if(gameInfo.variant != VariantShogi)\r
4489             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4490         else\r
4491             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4492       }\r
4493       break;\r
4494     case 2:\r
4495       SetCapture(hwndMain);\r
4496       break;\r
4497     case 1:\r
4498       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4499       SetupDropMenu(hmenu);\r
4500       MenuPopup(hwnd, pt, hmenu, -1);\r
4501     default:\r
4502       break;\r
4503     }\r
4504     break;\r
4505   }\r
4506 \r
4507   recursive--;\r
4508 }\r
4509 \r
4510 /* Preprocess messages for buttons in main window */\r
4511 LRESULT CALLBACK\r
4512 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4513 {\r
4514   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4515   int i, dir;\r
4516 \r
4517   for (i=0; i<N_BUTTONS; i++) {\r
4518     if (buttonDesc[i].id == id) break;\r
4519   }\r
4520   if (i == N_BUTTONS) return 0;\r
4521   switch (message) {\r
4522   case WM_KEYDOWN:\r
4523     switch (wParam) {\r
4524     case VK_LEFT:\r
4525     case VK_RIGHT:\r
4526       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4527       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4528       return TRUE;\r
4529     }\r
4530     break;\r
4531   case WM_CHAR:\r
4532     switch (wParam) {\r
4533     case '\r':\r
4534       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4535       return TRUE;\r
4536     default:\r
4537       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4538         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4539         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4540         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4541         SetFocus(h);\r
4542         SendMessage(h, WM_CHAR, wParam, lParam);\r
4543         return TRUE;\r
4544       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4545         TypeInEvent((char)wParam);\r
4546       }\r
4547       break;\r
4548     }\r
4549     break;\r
4550   }\r
4551   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4552 }\r
4553 \r
4554 static int promoStyle;\r
4555 \r
4556 /* Process messages for Promotion dialog box */\r
4557 LRESULT CALLBACK\r
4558 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4559 {\r
4560 \r
4561   char promoChar;\r
4562 \r
4563   switch (message) {\r
4564 \r
4565   case WM_INITDIALOG: /* message: initialize dialog box */\r
4566     /* Center the dialog over the application window */\r
4567     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4568     Translate(hDlg, DLG_PromotionKing);\r
4569     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4570       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4571        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4572        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4573                SW_SHOW : SW_HIDE);\r
4574     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4575     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4576        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4577          PieceToChar(WhiteAngel) != '~') ||\r
4578         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4579          PieceToChar(BlackAngel) != '~')   ) ?\r
4580                SW_SHOW : SW_HIDE);\r
4581     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4582        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4583          PieceToChar(WhiteMarshall) != '~') ||\r
4584         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4585          PieceToChar(BlackMarshall) != '~')   ) ?\r
4586                SW_SHOW : SW_HIDE);\r
4587     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4588     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4589     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4590     if(promoStyle) {\r
4591         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4592         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4593         SetWindowText(hDlg, "Promote?");\r
4594     }\r
4595     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4596        gameInfo.variant == VariantSuper ?\r
4597                SW_SHOW : SW_HIDE);\r
4598     return TRUE;\r
4599 \r
4600   case WM_COMMAND: /* message: received a command */\r
4601     switch (LOWORD(wParam)) {\r
4602     case IDCANCEL:\r
4603       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4604       ClearHighlights();\r
4605       DrawPosition(FALSE, NULL);\r
4606       return TRUE;\r
4607     case PB_King:\r
4608       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4609       break;\r
4610     case PB_Queen:\r
4611       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4612       break;\r
4613     case PB_Rook:\r
4614       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4615       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4616       break;\r
4617     case PB_Bishop:\r
4618       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4619       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4620       break;\r
4621     case PB_Chancellor:\r
4622       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4623       break;\r
4624     case PB_Archbishop:\r
4625       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4626       break;\r
4627     case PB_Knight:\r
4628       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4629                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4630       break;\r
4631     default:\r
4632       return FALSE;\r
4633     }\r
4634     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4635     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4636     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4637     fromX = fromY = -1;\r
4638     if (!appData.highlightLastMove) {\r
4639       ClearHighlights();\r
4640       DrawPosition(FALSE, NULL);\r
4641     }\r
4642     return TRUE;\r
4643   }\r
4644   return FALSE;\r
4645 }\r
4646 \r
4647 /* Pop up promotion dialog */\r
4648 VOID\r
4649 PromotionPopup(HWND hwnd)\r
4650 {\r
4651   FARPROC lpProc;\r
4652 \r
4653   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4654   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4655     hwnd, (DLGPROC)lpProc);\r
4656   FreeProcInstance(lpProc);\r
4657 }\r
4658 \r
4659 void\r
4660 PromotionPopUp(char choice)\r
4661 {\r
4662   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4663   DrawPosition(TRUE, NULL);\r
4664   PromotionPopup(hwndMain);\r
4665 }\r
4666 \r
4667 VOID\r
4668 LoadGameDialog(HWND hwnd, char* title)\r
4669 {\r
4670   UINT number = 0;\r
4671   FILE *f;\r
4672   char fileTitle[MSG_SIZ];\r
4673   f = OpenFileDialog(hwnd, "rb", "",\r
4674                      appData.oldSaveStyle ? "gam" : "pgn",\r
4675                      GAME_FILT,\r
4676                      title, &number, fileTitle, NULL);\r
4677   if (f != NULL) {\r
4678     cmailMsgLoaded = FALSE;\r
4679     if (number == 0) {\r
4680       int error = GameListBuild(f);\r
4681       if (error) {\r
4682         DisplayError(_("Cannot build game list"), error);\r
4683       } else if (!ListEmpty(&gameList) &&\r
4684                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4685         GameListPopUp(f, fileTitle);\r
4686         return;\r
4687       }\r
4688       GameListDestroy();\r
4689       number = 1;\r
4690     }\r
4691     LoadGame(f, number, fileTitle, FALSE);\r
4692   }\r
4693 }\r
4694 \r
4695 int get_term_width()\r
4696 {\r
4697     HDC hdc;\r
4698     TEXTMETRIC tm;\r
4699     RECT rc;\r
4700     HFONT hfont, hold_font;\r
4701     LOGFONT lf;\r
4702     HWND hText;\r
4703 \r
4704     if (hwndConsole)\r
4705         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4706     else\r
4707         return 79;\r
4708 \r
4709     // get the text metrics\r
4710     hdc = GetDC(hText);\r
4711     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4712     if (consoleCF.dwEffects & CFE_BOLD)\r
4713         lf.lfWeight = FW_BOLD;\r
4714     if (consoleCF.dwEffects & CFE_ITALIC)\r
4715         lf.lfItalic = TRUE;\r
4716     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4717         lf.lfStrikeOut = TRUE;\r
4718     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4719         lf.lfUnderline = TRUE;\r
4720     hfont = CreateFontIndirect(&lf);\r
4721     hold_font = SelectObject(hdc, hfont);\r
4722     GetTextMetrics(hdc, &tm);\r
4723     SelectObject(hdc, hold_font);\r
4724     DeleteObject(hfont);\r
4725     ReleaseDC(hText, hdc);\r
4726 \r
4727     // get the rectangle\r
4728     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4729 \r
4730     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4731 }\r
4732 \r
4733 void UpdateICSWidth(HWND hText)\r
4734 {\r
4735     LONG old_width, new_width;\r
4736 \r
4737     new_width = get_term_width(hText, FALSE);\r
4738     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4739     if (new_width != old_width)\r
4740     {\r
4741         ics_update_width(new_width);\r
4742         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4743     }\r
4744 }\r
4745 \r
4746 VOID\r
4747 ChangedConsoleFont()\r
4748 {\r
4749   CHARFORMAT cfmt;\r
4750   CHARRANGE tmpsel, sel;\r
4751   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4752   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4753   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4754   PARAFORMAT paraf;\r
4755 \r
4756   cfmt.cbSize = sizeof(CHARFORMAT);\r
4757   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4758     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4759                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4760   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4761    * size.  This was undocumented in the version of MSVC++ that I had\r
4762    * when I wrote the code, but is apparently documented now.\r
4763    */\r
4764   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4765   cfmt.bCharSet = f->lf.lfCharSet;\r
4766   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4767   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4768   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4769   /* Why are the following seemingly needed too? */\r
4770   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4771   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4772   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4773   tmpsel.cpMin = 0;\r
4774   tmpsel.cpMax = -1; /*999999?*/\r
4775   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4776   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4777   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4778    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4779    */\r
4780   paraf.cbSize = sizeof(paraf);\r
4781   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4782   paraf.dxStartIndent = 0;\r
4783   paraf.dxOffset = WRAP_INDENT;\r
4784   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4785   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4786   UpdateICSWidth(hText);\r
4787 }\r
4788 \r
4789 /*---------------------------------------------------------------------------*\\r
4790  *\r
4791  * Window Proc for main window\r
4792  *\r
4793 \*---------------------------------------------------------------------------*/\r
4794 \r
4795 /* Process messages for main window, etc. */\r
4796 LRESULT CALLBACK\r
4797 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4798 {\r
4799   FARPROC lpProc;\r
4800   int wmId;\r
4801   char *defName;\r
4802   FILE *f;\r
4803   UINT number;\r
4804   char fileTitle[MSG_SIZ];\r
4805   static SnapData sd;\r
4806   static int peek=0;\r
4807 \r
4808   switch (message) {\r
4809 \r
4810   case WM_PAINT: /* message: repaint portion of window */\r
4811     PaintProc(hwnd);\r
4812     break;\r
4813 \r
4814   case WM_ERASEBKGND:\r
4815     if (IsIconic(hwnd)) {\r
4816       /* Cheat; change the message */\r
4817       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4818     } else {\r
4819       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4820     }\r
4821     break;\r
4822 \r
4823   case WM_LBUTTONDOWN:\r
4824   case WM_MBUTTONDOWN:\r
4825   case WM_RBUTTONDOWN:\r
4826   case WM_LBUTTONUP:\r
4827   case WM_MBUTTONUP:\r
4828   case WM_RBUTTONUP:\r
4829   case WM_MOUSEMOVE:\r
4830   case WM_MOUSEWHEEL:\r
4831     MouseEvent(hwnd, message, wParam, lParam);\r
4832     break;\r
4833 \r
4834   case WM_KEYUP:\r
4835     if((char)wParam == '\b') {\r
4836       ForwardEvent(); peek = 0;\r
4837     }\r
4838 \r
4839     JAWS_KBUP_NAVIGATION\r
4840 \r
4841     break;\r
4842 \r
4843   case WM_KEYDOWN:\r
4844     if((char)wParam == '\b') {\r
4845       if(!peek) BackwardEvent(), peek = 1;\r
4846     }\r
4847 \r
4848     JAWS_KBDOWN_NAVIGATION\r
4849 \r
4850     break;\r
4851 \r
4852   case WM_CHAR:\r
4853     \r
4854     JAWS_ALT_INTERCEPT\r
4855 \r
4856     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4857         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4858         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4859         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4860         SetFocus(h);\r
4861         SendMessage(h, message, wParam, lParam);\r
4862     } else if(lParam != KF_REPEAT) {\r
4863         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4864                 TypeInEvent((char)wParam);\r
4865         } else if((char)wParam == 003) CopyGameToClipboard();\r
4866          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4867     }\r
4868 \r
4869     break;\r
4870 \r
4871   case WM_PALETTECHANGED:\r
4872     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4873       int nnew;\r
4874       HDC hdc = GetDC(hwndMain);\r
4875       SelectPalette(hdc, hPal, TRUE);\r
4876       nnew = RealizePalette(hdc);\r
4877       if (nnew > 0) {\r
4878         paletteChanged = TRUE;\r
4879 \r
4880         InvalidateRect(hwnd, &boardRect, FALSE);\r
4881       }\r
4882       ReleaseDC(hwnd, hdc);\r
4883     }\r
4884     break;\r
4885 \r
4886   case WM_QUERYNEWPALETTE:\r
4887     if (!appData.monoMode /*&& paletteChanged*/) {\r
4888       int nnew;\r
4889       HDC hdc = GetDC(hwndMain);\r
4890       paletteChanged = FALSE;\r
4891       SelectPalette(hdc, hPal, FALSE);\r
4892       nnew = RealizePalette(hdc);\r
4893       if (nnew > 0) {\r
4894         InvalidateRect(hwnd, &boardRect, FALSE);\r
4895       }\r
4896       ReleaseDC(hwnd, hdc);\r
4897       return TRUE;\r
4898     }\r
4899     return FALSE;\r
4900 \r
4901   case WM_COMMAND: /* message: command from application menu */\r
4902     wmId    = LOWORD(wParam);\r
4903 \r
4904     switch (wmId) {\r
4905     case IDM_NewGame:\r
4906       ResetGameEvent();\r
4907       SAY("new game enter a move to play against the computer with white");\r
4908       break;\r
4909 \r
4910     case IDM_NewGameFRC:\r
4911       if( NewGameFRC() == 0 ) {\r
4912         ResetGameEvent();\r
4913       }\r
4914       break;\r
4915 \r
4916     case IDM_NewVariant:\r
4917       NewVariantPopup(hwnd);\r
4918       break;\r
4919 \r
4920     case IDM_LoadGame:\r
4921       LoadGameDialog(hwnd, _("Load Game from File"));\r
4922       break;\r
4923 \r
4924     case IDM_LoadNextGame:\r
4925       ReloadGame(1);\r
4926       break;\r
4927 \r
4928     case IDM_LoadPrevGame:\r
4929       ReloadGame(-1);\r
4930       break;\r
4931 \r
4932     case IDM_ReloadGame:\r
4933       ReloadGame(0);\r
4934       break;\r
4935 \r
4936     case IDM_LoadPosition:\r
4937       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4938         Reset(FALSE, TRUE);\r
4939       }\r
4940       number = 1;\r
4941       f = OpenFileDialog(hwnd, "rb", "",\r
4942                          appData.oldSaveStyle ? "pos" : "fen",\r
4943                          POSITION_FILT,\r
4944                          _("Load Position from File"), &number, fileTitle, NULL);\r
4945       if (f != NULL) {\r
4946         LoadPosition(f, number, fileTitle);\r
4947       }\r
4948       break;\r
4949 \r
4950     case IDM_LoadNextPosition:\r
4951       ReloadPosition(1);\r
4952       break;\r
4953 \r
4954     case IDM_LoadPrevPosition:\r
4955       ReloadPosition(-1);\r
4956       break;\r
4957 \r
4958     case IDM_ReloadPosition:\r
4959       ReloadPosition(0);\r
4960       break;\r
4961 \r
4962     case IDM_SaveGame:\r
4963       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4964       f = OpenFileDialog(hwnd, "a", defName,\r
4965                          appData.oldSaveStyle ? "gam" : "pgn",\r
4966                          GAME_FILT,\r
4967                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4968       if (f != NULL) {\r
4969         SaveGame(f, 0, "");\r
4970       }\r
4971       break;\r
4972 \r
4973     case IDM_SavePosition:\r
4974       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4975       f = OpenFileDialog(hwnd, "a", defName,\r
4976                          appData.oldSaveStyle ? "pos" : "fen",\r
4977                          POSITION_FILT,\r
4978                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4979       if (f != NULL) {\r
4980         SavePosition(f, 0, "");\r
4981       }\r
4982       break;\r
4983 \r
4984     case IDM_SaveDiagram:\r
4985       defName = "diagram";\r
4986       f = OpenFileDialog(hwnd, "wb", defName,\r
4987                          "bmp",\r
4988                          DIAGRAM_FILT,\r
4989                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4990       if (f != NULL) {\r
4991         SaveDiagram(f);\r
4992       }\r
4993       break;\r
4994 \r
4995     case IDM_SaveSelected:\r
4996       f = OpenFileDialog(hwnd, "a", "",\r
4997                          "pgn",\r
4998                          GAME_FILT,\r
4999                          _("Save Game to File"), NULL, fileTitle, NULL);\r
5000       if (f != NULL) {\r
5001         SaveSelected(f, 0, "");\r
5002       }\r
5003       break;\r
5004 \r
5005     case IDM_CreateBook:\r
5006       CreateBookEvent();\r
5007       break;\r
5008 \r
5009     case IDM_CopyGame:\r
5010       CopyGameToClipboard();\r
5011       break;\r
5012 \r
5013     case IDM_PasteGame:\r
5014       PasteGameFromClipboard();\r
5015       break;\r
5016 \r
5017     case IDM_CopyGameListToClipboard:\r
5018       CopyGameListToClipboard();\r
5019       break;\r
5020 \r
5021     /* [AS] Autodetect FEN or PGN data */\r
5022     case IDM_PasteAny:\r
5023       PasteGameOrFENFromClipboard();\r
5024       break;\r
5025 \r
5026     /* [AS] Move history */\r
5027     case IDM_ShowMoveHistory:\r
5028         if( MoveHistoryIsUp() ) {\r
5029             MoveHistoryPopDown();\r
5030         }\r
5031         else {\r
5032             MoveHistoryPopUp();\r
5033         }\r
5034         break;\r
5035 \r
5036     /* [AS] Eval graph */\r
5037     case IDM_ShowEvalGraph:\r
5038         if( EvalGraphIsUp() ) {\r
5039             EvalGraphPopDown();\r
5040         }\r
5041         else {\r
5042             EvalGraphPopUp();\r
5043             SetFocus(hwndMain);\r
5044         }\r
5045         break;\r
5046 \r
5047     /* [AS] Engine output */\r
5048     case IDM_ShowEngineOutput:\r
5049         if( EngineOutputIsUp() ) {\r
5050             EngineOutputPopDown();\r
5051         }\r
5052         else {\r
5053             EngineOutputPopUp();\r
5054         }\r
5055         break;\r
5056 \r
5057     /* [AS] User adjudication */\r
5058     case IDM_UserAdjudication_White:\r
5059         UserAdjudicationEvent( +1 );\r
5060         break;\r
5061 \r
5062     case IDM_UserAdjudication_Black:\r
5063         UserAdjudicationEvent( -1 );\r
5064         break;\r
5065 \r
5066     case IDM_UserAdjudication_Draw:\r
5067         UserAdjudicationEvent( 0 );\r
5068         break;\r
5069 \r
5070     /* [AS] Game list options dialog */\r
5071     case IDM_GameListOptions:\r
5072       GameListOptions();\r
5073       break;\r
5074 \r
5075     case IDM_NewChat:\r
5076       ChatPopUp(NULL);\r
5077       break;\r
5078 \r
5079     case IDM_CopyPosition:\r
5080       CopyFENToClipboard();\r
5081       break;\r
5082 \r
5083     case IDM_PastePosition:\r
5084       PasteFENFromClipboard();\r
5085       break;\r
5086 \r
5087     case IDM_MailMove:\r
5088       MailMoveEvent();\r
5089       break;\r
5090 \r
5091     case IDM_ReloadCMailMsg:\r
5092       Reset(TRUE, TRUE);\r
5093       ReloadCmailMsgEvent(FALSE);\r
5094       break;\r
5095 \r
5096     case IDM_Minimize:\r
5097       ShowWindow(hwnd, SW_MINIMIZE);\r
5098       break;\r
5099 \r
5100     case IDM_Exit:\r
5101       ExitEvent(0);\r
5102       break;\r
5103 \r
5104     case IDM_MachineWhite:\r
5105       MachineWhiteEvent();\r
5106       /*\r
5107        * refresh the tags dialog only if it's visible\r
5108        */\r
5109       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5110           char *tags;\r
5111           tags = PGNTags(&gameInfo);\r
5112           TagsPopUp(tags, CmailMsg());\r
5113           free(tags);\r
5114       }\r
5115       SAY("computer starts playing white");\r
5116       break;\r
5117 \r
5118     case IDM_MachineBlack:\r
5119       MachineBlackEvent();\r
5120       /*\r
5121        * refresh the tags dialog only if it's visible\r
5122        */\r
5123       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5124           char *tags;\r
5125           tags = PGNTags(&gameInfo);\r
5126           TagsPopUp(tags, CmailMsg());\r
5127           free(tags);\r
5128       }\r
5129       SAY("computer starts playing black");\r
5130       break;\r
5131 \r
5132     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5133       if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
5134       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5135       break;\r
5136 \r
5137     case IDM_TwoMachines:\r
5138       TwoMachinesEvent();\r
5139       /*\r
5140 \r
5141        * refresh the tags dialog only if it's visible\r
5142        */\r
5143       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5144           char *tags;\r
5145           tags = PGNTags(&gameInfo);\r
5146           TagsPopUp(tags, CmailMsg());\r
5147           free(tags);\r
5148       }\r
5149       SAY("computer starts playing both sides");\r
5150       break;\r
5151 \r
5152     case IDM_AnalysisMode:\r
5153       if(AnalyzeModeEvent()) {\r
5154         SAY("analyzing current position");\r
5155       }\r
5156       break;\r
5157 \r
5158     case IDM_AnalyzeFile:\r
5159       AnalyzeFileEvent();\r
5160       break;\r
5161 \r
5162     case IDM_IcsClient:\r
5163       IcsClientEvent();\r
5164       break;\r
5165 \r
5166     case IDM_EditGame:\r
5167     case IDM_EditGame2:\r
5168       EditGameEvent();\r
5169       SAY("edit game");\r
5170       break;\r
5171 \r
5172     case IDM_EditPosition:\r
5173     case IDM_EditPosition2:\r
5174       EditPositionEvent();\r
5175       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5176       break;\r
5177 \r
5178     case IDM_Training:\r
5179       TrainingEvent();\r
5180       break;\r
5181 \r
5182     case IDM_ShowGameList:\r
5183       ShowGameListProc();\r
5184       break;\r
5185 \r
5186     case IDM_EditProgs1:\r
5187       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5188       break;\r
5189 \r
5190     case IDM_LoadProg1:\r
5191      LoadEnginePopUp(hwndMain, 0);\r
5192       break;\r
5193 \r
5194     case IDM_LoadProg2:\r
5195      LoadEnginePopUp(hwndMain, 1);\r
5196       break;\r
5197 \r
5198     case IDM_EditServers:\r
5199       EditTagsPopUp(icsNames, &icsNames);\r
5200       break;\r
5201 \r
5202     case IDM_EditTags:\r
5203     case IDM_Tags:\r
5204       EditTagsProc();\r
5205       break;\r
5206 \r
5207     case IDM_EditBook:\r
5208       EditBookEvent();\r
5209       break;\r
5210 \r
5211     case IDM_EditComment:\r
5212     case IDM_Comment:\r
5213       if (commentUp && editComment) {\r
5214         CommentPopDown();\r
5215       } else {\r
5216         EditCommentEvent();\r
5217       }\r
5218       break;\r
5219 \r
5220     case IDM_Pause:\r
5221       PauseEvent();\r
5222       break;\r
5223 \r
5224     case IDM_Accept:\r
5225       AcceptEvent();\r
5226       break;\r
5227 \r
5228     case IDM_Decline:\r
5229       DeclineEvent();\r
5230       break;\r
5231 \r
5232     case IDM_Rematch:\r
5233 \r
5234       RematchEvent();\r
5235       break;\r
5236 \r
5237     case IDM_CallFlag:\r
5238       CallFlagEvent();\r
5239       break;\r
5240 \r
5241     case IDM_Draw:\r
5242       DrawEvent();\r
5243       break;\r
5244 \r
5245     case IDM_Adjourn:\r
5246       AdjournEvent();\r
5247       break;\r
5248 \r
5249     case IDM_Abort:\r
5250       AbortEvent();\r
5251       break;\r
5252 \r
5253     case IDM_Resign:\r
5254       ResignEvent();\r
5255       break;\r
5256 \r
5257     case IDM_StopObserving:\r
5258       StopObservingEvent();\r
5259       break;\r
5260 \r
5261     case IDM_StopExamining:\r
5262       StopExaminingEvent();\r
5263       break;\r
5264 \r
5265     case IDM_Upload:\r
5266       UploadGameEvent();\r
5267       break;\r
5268 \r
5269     case IDM_TypeInMove:\r
5270       TypeInEvent('\000');\r
5271       break;\r
5272 \r
5273     case IDM_TypeInName:\r
5274       PopUpNameDialog('\000');\r
5275       break;\r
5276 \r
5277     case IDM_Backward:\r
5278       BackwardEvent();\r
5279       SetFocus(hwndMain);\r
5280       break;\r
5281 \r
5282     JAWS_MENU_ITEMS\r
5283 \r
5284     case IDM_Forward:\r
5285       ForwardEvent();\r
5286       SetFocus(hwndMain);\r
5287       break;\r
5288 \r
5289     case IDM_ToStart:\r
5290       ToStartEvent();\r
5291       SetFocus(hwndMain);\r
5292       break;\r
5293 \r
5294     case IDM_ToEnd:\r
5295       ToEndEvent();\r
5296       SetFocus(hwndMain);\r
5297       break;\r
5298 \r
5299     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5300     case OPT_GameListPrev:\r
5301       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5302       break;\r
5303 \r
5304     case IDM_Revert:\r
5305       RevertEvent(FALSE);\r
5306       break;\r
5307 \r
5308     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5309       RevertEvent(TRUE);\r
5310       break;\r
5311 \r
5312     case IDM_TruncateGame:\r
5313       TruncateGameEvent();\r
5314       break;\r
5315 \r
5316     case IDM_MoveNow:\r
5317       MoveNowEvent();\r
5318       break;\r
5319 \r
5320     case IDM_RetractMove:\r
5321       RetractMoveEvent();\r
5322       break;\r
5323 \r
5324     case IDM_FlipView:\r
5325       flipView = !flipView;\r
5326       DrawPosition(FALSE, NULL);\r
5327       break;\r
5328 \r
5329     case IDM_FlipClock:\r
5330       flipClock = !flipClock;\r
5331       DisplayBothClocks();\r
5332       DisplayLogos();\r
5333       break;\r
5334 \r
5335     case IDM_MuteSounds:\r
5336       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5337       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5338                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5339       break;\r
5340 \r
5341     case IDM_GeneralOptions:\r
5342       GeneralOptionsPopup(hwnd);\r
5343       DrawPosition(TRUE, NULL);\r
5344       break;\r
5345 \r
5346     case IDM_BoardOptions:\r
5347       BoardOptionsPopup(hwnd);\r
5348       break;\r
5349 \r
5350     case IDM_ThemeOptions:\r
5351       ThemeOptionsPopup(hwnd);\r
5352       break;\r
5353 \r
5354     case IDM_EnginePlayOptions:\r
5355       EnginePlayOptionsPopup(hwnd);\r
5356       break;\r
5357 \r
5358     case IDM_Engine1Options:\r
5359       EngineOptionsPopup(hwnd, &first);\r
5360       break;\r
5361 \r
5362     case IDM_Engine2Options:\r
5363       savedHwnd = hwnd;\r
5364       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5365       EngineOptionsPopup(hwnd, &second);\r
5366       break;\r
5367 \r
5368     case IDM_OptionsUCI:\r
5369       UciOptionsPopup(hwnd);\r
5370       break;\r
5371 \r
5372     case IDM_Tourney:\r
5373       TourneyPopup(hwnd);\r
5374       break;\r
5375 \r
5376     case IDM_IcsOptions:\r
5377       IcsOptionsPopup(hwnd);\r
5378       break;\r
5379 \r
5380     case IDM_Fonts:\r
5381       FontsOptionsPopup(hwnd);\r
5382       break;\r
5383 \r
5384     case IDM_Sounds:\r
5385       SoundOptionsPopup(hwnd);\r
5386       break;\r
5387 \r
5388     case IDM_CommPort:\r
5389       CommPortOptionsPopup(hwnd);\r
5390       break;\r
5391 \r
5392     case IDM_LoadOptions:\r
5393       LoadOptionsPopup(hwnd);\r
5394       break;\r
5395 \r
5396     case IDM_SaveOptions:\r
5397       SaveOptionsPopup(hwnd);\r
5398       break;\r
5399 \r
5400     case IDM_TimeControl:\r
5401       TimeControlOptionsPopup(hwnd);\r
5402       break;\r
5403 \r
5404     case IDM_SaveSettings:\r
5405       SaveSettings(settingsFileName);\r
5406       break;\r
5407 \r
5408     case IDM_SaveSettingsOnExit:\r
5409       saveSettingsOnExit = !saveSettingsOnExit;\r
5410       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5411                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5412                                          MF_CHECKED : MF_UNCHECKED));\r
5413       break;\r
5414 \r
5415     case IDM_Hint:\r
5416       HintEvent();\r
5417       break;\r
5418 \r
5419     case IDM_Book:\r
5420       BookEvent();\r
5421       break;\r
5422 \r
5423     case IDM_AboutGame:\r
5424       AboutGameEvent();\r
5425       break;\r
5426 \r
5427     case IDM_Debug:\r
5428       appData.debugMode = !appData.debugMode;\r
5429       if (appData.debugMode) {\r
5430         char dir[MSG_SIZ];\r
5431         GetCurrentDirectory(MSG_SIZ, dir);\r
5432         SetCurrentDirectory(installDir);\r
5433         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5434         SetCurrentDirectory(dir);\r
5435         setbuf(debugFP, NULL);\r
5436       } else {\r
5437         fclose(debugFP);\r
5438         debugFP = NULL;\r
5439       }\r
5440       break;\r
5441 \r
5442     case IDM_HELPCONTENTS:\r
5443       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5444           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5445           MessageBox (GetFocus(),\r
5446                     _("Unable to activate help"),\r
5447                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5448       }\r
5449       break;\r
5450 \r
5451     case IDM_HELPSEARCH:\r
5452         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5453             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5454         MessageBox (GetFocus(),\r
5455                     _("Unable to activate help"),\r
5456                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5457       }\r
5458       break;\r
5459 \r
5460     case IDM_HELPHELP:\r
5461       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5462         MessageBox (GetFocus(),\r
5463                     _("Unable to activate help"),\r
5464                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5465       }\r
5466       break;\r
5467 \r
5468     case IDM_ABOUT:\r
5469       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5470       DialogBox(hInst, \r
5471         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5472         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5473       FreeProcInstance(lpProc);\r
5474       break;\r
5475 \r
5476     case IDM_DirectCommand1:\r
5477       AskQuestionEvent(_("Direct Command"),\r
5478                        _("Send to chess program:"), "", "1");\r
5479       break;\r
5480     case IDM_DirectCommand2:\r
5481       AskQuestionEvent(_("Direct Command"),\r
5482                        _("Send to second chess program:"), "", "2");\r
5483       break;\r
5484 \r
5485     case EP_WhitePawn:\r
5486       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5487       fromX = fromY = -1;\r
5488       break;\r
5489 \r
5490     case EP_WhiteKnight:\r
5491       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5492       fromX = fromY = -1;\r
5493       break;\r
5494 \r
5495     case EP_WhiteBishop:\r
5496       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5497       fromX = fromY = -1;\r
5498       break;\r
5499 \r
5500     case EP_WhiteRook:\r
5501       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5502       fromX = fromY = -1;\r
5503       break;\r
5504 \r
5505     case EP_WhiteQueen:\r
5506       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5507       fromX = fromY = -1;\r
5508       break;\r
5509 \r
5510     case EP_WhiteFerz:\r
5511       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5512       fromX = fromY = -1;\r
5513       break;\r
5514 \r
5515     case EP_WhiteWazir:\r
5516       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5517       fromX = fromY = -1;\r
5518       break;\r
5519 \r
5520     case EP_WhiteAlfil:\r
5521       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5522       fromX = fromY = -1;\r
5523       break;\r
5524 \r
5525     case EP_WhiteCannon:\r
5526       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5527       fromX = fromY = -1;\r
5528       break;\r
5529 \r
5530     case EP_WhiteCardinal:\r
5531       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5532       fromX = fromY = -1;\r
5533       break;\r
5534 \r
5535     case EP_WhiteMarshall:\r
5536       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5537       fromX = fromY = -1;\r
5538       break;\r
5539 \r
5540     case EP_WhiteKing:\r
5541       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5542       fromX = fromY = -1;\r
5543       break;\r
5544 \r
5545     case EP_BlackPawn:\r
5546       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5547       fromX = fromY = -1;\r
5548       break;\r
5549 \r
5550     case EP_BlackKnight:\r
5551       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5552       fromX = fromY = -1;\r
5553       break;\r
5554 \r
5555     case EP_BlackBishop:\r
5556       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5557       fromX = fromY = -1;\r
5558       break;\r
5559 \r
5560     case EP_BlackRook:\r
5561       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5562       fromX = fromY = -1;\r
5563       break;\r
5564 \r
5565     case EP_BlackQueen:\r
5566       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5567       fromX = fromY = -1;\r
5568       break;\r
5569 \r
5570     case EP_BlackFerz:\r
5571       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5572       fromX = fromY = -1;\r
5573       break;\r
5574 \r
5575     case EP_BlackWazir:\r
5576       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5577       fromX = fromY = -1;\r
5578       break;\r
5579 \r
5580     case EP_BlackAlfil:\r
5581       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5582       fromX = fromY = -1;\r
5583       break;\r
5584 \r
5585     case EP_BlackCannon:\r
5586       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5587       fromX = fromY = -1;\r
5588       break;\r
5589 \r
5590     case EP_BlackCardinal:\r
5591       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5592       fromX = fromY = -1;\r
5593       break;\r
5594 \r
5595     case EP_BlackMarshall:\r
5596       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5597       fromX = fromY = -1;\r
5598       break;\r
5599 \r
5600     case EP_BlackKing:\r
5601       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5602       fromX = fromY = -1;\r
5603       break;\r
5604 \r
5605     case EP_EmptySquare:\r
5606       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5607       fromX = fromY = -1;\r
5608       break;\r
5609 \r
5610     case EP_ClearBoard:\r
5611       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5612       fromX = fromY = -1;\r
5613       break;\r
5614 \r
5615     case EP_White:\r
5616       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5617       fromX = fromY = -1;\r
5618       break;\r
5619 \r
5620     case EP_Black:\r
5621       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5622       fromX = fromY = -1;\r
5623       break;\r
5624 \r
5625     case EP_Promote:\r
5626       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5627       fromX = fromY = -1;\r
5628       break;\r
5629 \r
5630     case EP_Demote:\r
5631       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5632       fromX = fromY = -1;\r
5633       break;\r
5634 \r
5635     case DP_Pawn:\r
5636       DropMenuEvent(WhitePawn, fromX, fromY);\r
5637       fromX = fromY = -1;\r
5638       break;\r
5639 \r
5640     case DP_Knight:\r
5641       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5642       fromX = fromY = -1;\r
5643       break;\r
5644 \r
5645     case DP_Bishop:\r
5646       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5647       fromX = fromY = -1;\r
5648       break;\r
5649 \r
5650     case DP_Rook:\r
5651       DropMenuEvent(WhiteRook, fromX, fromY);\r
5652       fromX = fromY = -1;\r
5653       break;\r
5654 \r
5655     case DP_Queen:\r
5656       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5657       fromX = fromY = -1;\r
5658       break;\r
5659 \r
5660     case IDM_English:\r
5661       barbaric = 0; appData.language = "";\r
5662       TranslateMenus(0);\r
5663       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5664       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5665       lastChecked = wmId;\r
5666       break;\r
5667 \r
5668     default:\r
5669       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5670           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5671       else\r
5672       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5673           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5674           TranslateMenus(0);\r
5675           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5676           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5677           lastChecked = wmId;\r
5678           break;\r
5679       }\r
5680       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5681     }\r
5682     break;\r
5683 \r
5684   case WM_TIMER:\r
5685     switch (wParam) {\r
5686     case CLOCK_TIMER_ID:\r
5687       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5688       clockTimerEvent = 0;\r
5689       DecrementClocks(); /* call into back end */\r
5690       break;\r
5691     case LOAD_GAME_TIMER_ID:\r
5692       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5693       loadGameTimerEvent = 0;\r
5694       AutoPlayGameLoop(); /* call into back end */\r
5695       break;\r
5696     case ANALYSIS_TIMER_ID:\r
5697       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5698                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5699         AnalysisPeriodicEvent(0);\r
5700       } else {\r
5701         KillTimer(hwnd, analysisTimerEvent);\r
5702         analysisTimerEvent = 0;\r
5703       }\r
5704       break;\r
5705     case DELAYED_TIMER_ID:\r
5706       KillTimer(hwnd, delayedTimerEvent);\r
5707       delayedTimerEvent = 0;\r
5708       delayedTimerCallback();\r
5709       break;\r
5710     }\r
5711     break;\r
5712 \r
5713   case WM_USER_Input:\r
5714     InputEvent(hwnd, message, wParam, lParam);\r
5715     break;\r
5716 \r
5717   /* [AS] Also move "attached" child windows */\r
5718   case WM_WINDOWPOSCHANGING:\r
5719 \r
5720     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5721         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5722 \r
5723         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5724             /* Window is moving */\r
5725             RECT rcMain;\r
5726 \r
5727 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5728             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5729             rcMain.right  = wpMain.x + wpMain.width;\r
5730             rcMain.top    = wpMain.y;\r
5731             rcMain.bottom = wpMain.y + wpMain.height;\r
5732             \r
5733             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5734             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5735             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5736             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5737             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5738             wpMain.x = lpwp->x;\r
5739             wpMain.y = lpwp->y;\r
5740 \r
5741         }\r
5742     }\r
5743     break;\r
5744 \r
5745   /* [AS] Snapping */\r
5746   case WM_ENTERSIZEMOVE:\r
5747     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5748     if (hwnd == hwndMain) {\r
5749       doingSizing = TRUE;\r
5750       lastSizing = 0;\r
5751     }\r
5752     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5753     break;\r
5754 \r
5755   case WM_SIZING:\r
5756     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5757     if (hwnd == hwndMain) {\r
5758       lastSizing = wParam;\r
5759     }\r
5760     break;\r
5761 \r
5762   case WM_MOVING:\r
5763     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5764       return OnMoving( &sd, hwnd, wParam, lParam );\r
5765 \r
5766   case WM_EXITSIZEMOVE:\r
5767     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5768     if (hwnd == hwndMain) {\r
5769       RECT client;\r
5770       doingSizing = FALSE;\r
5771       InvalidateRect(hwnd, &boardRect, FALSE);\r
5772       GetClientRect(hwnd, &client);\r
5773       ResizeBoard(client.right, client.bottom, lastSizing);\r
5774       lastSizing = 0;\r
5775       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5776     }\r
5777     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5778     break;\r
5779 \r
5780   case WM_DESTROY: /* message: window being destroyed */\r
5781     PostQuitMessage(0);\r
5782     break;\r
5783 \r
5784   case WM_CLOSE:\r
5785     if (hwnd == hwndMain) {\r
5786       ExitEvent(0);\r
5787     }\r
5788     break;\r
5789 \r
5790   default:      /* Passes it on if unprocessed */\r
5791     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5792   }\r
5793 \r
5794 \r
5795   return 0;\r
5796 }\r
5797 \r
5798 /*---------------------------------------------------------------------------*\\r
5799  *\r
5800  * Misc utility routines\r
5801  *\r
5802 \*---------------------------------------------------------------------------*/\r
5803 \r
5804 /*\r
5805  * Decent random number generator, at least not as bad as Windows\r
5806  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5807  */\r
5808 unsigned int randstate;\r
5809 \r
5810 int\r
5811 myrandom(void)\r
5812 {\r
5813   randstate = randstate * 1664525 + 1013904223;\r
5814   return (int) randstate & 0x7fffffff;\r
5815 }\r
5816 \r
5817 void\r
5818 mysrandom(unsigned int seed)\r
5819 {\r
5820   randstate = seed;\r
5821 }\r
5822 \r
5823 \r
5824 /* \r
5825  * returns TRUE if user selects a different color, FALSE otherwise \r
5826  */\r
5827 \r
5828 BOOL\r
5829 ChangeColor(HWND hwnd, COLORREF *which)\r
5830 {\r
5831   static BOOL firstTime = TRUE;\r
5832   static DWORD customColors[16];\r
5833   CHOOSECOLOR cc;\r
5834   COLORREF newcolor;\r
5835   int i;\r
5836   ColorClass ccl;\r
5837 \r
5838   if (firstTime) {\r
5839     /* Make initial colors in use available as custom colors */\r
5840     /* Should we put the compiled-in defaults here instead? */\r
5841     i = 0;\r
5842     customColors[i++] = lightSquareColor & 0xffffff;\r
5843     customColors[i++] = darkSquareColor & 0xffffff;\r
5844     customColors[i++] = whitePieceColor & 0xffffff;\r
5845     customColors[i++] = blackPieceColor & 0xffffff;\r
5846     customColors[i++] = highlightSquareColor & 0xffffff;\r
5847     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5848 \r
5849     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5850       customColors[i++] = textAttribs[ccl].color;\r
5851     }\r
5852     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5853     firstTime = FALSE;\r
5854   }\r
5855 \r
5856   cc.lStructSize = sizeof(cc);\r
5857   cc.hwndOwner = hwnd;\r
5858   cc.hInstance = NULL;\r
5859   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5860   cc.lpCustColors = (LPDWORD) customColors;\r
5861   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5862 \r
5863   if (!ChooseColor(&cc)) return FALSE;\r
5864 \r
5865   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5866   if (newcolor == *which) return FALSE;\r
5867   *which = newcolor;\r
5868   return TRUE;\r
5869 \r
5870   /*\r
5871   InitDrawingColors();\r
5872   InvalidateRect(hwnd, &boardRect, FALSE);\r
5873   */\r
5874 }\r
5875 \r
5876 BOOLEAN\r
5877 MyLoadSound(MySound *ms)\r
5878 {\r
5879   BOOL ok = FALSE;\r
5880   struct stat st;\r
5881   FILE *f;\r
5882 \r
5883   if (ms->data && ms->flag) free(ms->data);\r
5884   ms->data = NULL;\r
5885 \r
5886   switch (ms->name[0]) {\r
5887   case NULLCHAR:\r
5888     /* Silence */\r
5889     ok = TRUE;\r
5890     break;\r
5891   case '$':\r
5892     /* System sound from Control Panel.  Don't preload here. */\r
5893     ok = TRUE;\r
5894     break;\r
5895   case '!':\r
5896     if (ms->name[1] == NULLCHAR) {\r
5897       /* "!" alone = silence */\r
5898       ok = TRUE;\r
5899     } else {\r
5900       /* Builtin wave resource.  Error if not found. */\r
5901       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5902       if (h == NULL) break;\r
5903       ms->data = (void *)LoadResource(hInst, h);\r
5904       ms->flag = 0; // not maloced, so cannot be freed!\r
5905       if (h == NULL) break;\r
5906       ok = TRUE;\r
5907     }\r
5908     break;\r
5909   default:\r
5910     /* .wav file.  Error if not found. */\r
5911     f = fopen(ms->name, "rb");\r
5912     if (f == NULL) break;\r
5913     if (fstat(fileno(f), &st) < 0) break;\r
5914     ms->data = malloc(st.st_size);\r
5915     ms->flag = 1;\r
5916     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5917     fclose(f);\r
5918     ok = TRUE;\r
5919     break;\r
5920   }\r
5921   if (!ok) {\r
5922     char buf[MSG_SIZ];\r
5923       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5924     DisplayError(buf, GetLastError());\r
5925   }\r
5926   return ok;\r
5927 }\r
5928 \r
5929 BOOLEAN\r
5930 MyPlaySound(MySound *ms)\r
5931 {\r
5932   BOOLEAN ok = FALSE;\r
5933 \r
5934   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5935   switch (ms->name[0]) {\r
5936   case NULLCHAR:\r
5937         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5938     /* Silence */\r
5939     ok = TRUE;\r
5940     break;\r
5941   case '$':\r
5942     /* System sound from Control Panel (deprecated feature).\r
5943        "$" alone or an unset sound name gets default beep (still in use). */\r
5944     if (ms->name[1]) {\r
5945       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5946     }\r
5947     if (!ok) ok = MessageBeep(MB_OK);\r
5948     break; \r
5949   case '!':\r
5950     /* Builtin wave resource, or "!" alone for silence */\r
5951     if (ms->name[1]) {\r
5952       if (ms->data == NULL) return FALSE;\r
5953       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5954     } else {\r
5955       ok = TRUE;\r
5956     }\r
5957     break;\r
5958   default:\r
5959     /* .wav file.  Error if not found. */\r
5960     if (ms->data == NULL) return FALSE;\r
5961     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5962     break;\r
5963   }\r
5964   /* Don't print an error: this can happen innocently if the sound driver\r
5965      is busy; for instance, if another instance of WinBoard is playing\r
5966      a sound at about the same time. */\r
5967   return ok;\r
5968 }\r
5969 \r
5970 \r
5971 LRESULT CALLBACK\r
5972 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5973 {\r
5974   BOOL ok;\r
5975   OPENFILENAME *ofn;\r
5976   static UINT *number; /* gross that this is static */\r
5977 \r
5978   switch (message) {\r
5979   case WM_INITDIALOG: /* message: initialize dialog box */\r
5980     /* Center the dialog over the application window */\r
5981     ofn = (OPENFILENAME *) lParam;\r
5982     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5983       number = (UINT *) ofn->lCustData;\r
5984       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5985     } else {\r
5986       number = NULL;\r
5987     }\r
5988     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5989     Translate(hDlg, 1536);\r
5990     return FALSE;  /* Allow for further processing */\r
5991 \r
5992   case WM_COMMAND:\r
5993     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5994       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5995     }\r
5996     return FALSE;  /* Allow for further processing */\r
5997   }\r
5998   return FALSE;\r
5999 }\r
6000 \r
6001 UINT APIENTRY\r
6002 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6003 {\r
6004   static UINT *number;\r
6005   OPENFILENAME *ofname;\r
6006   OFNOTIFY *ofnot;\r
6007   switch (uiMsg) {\r
6008   case WM_INITDIALOG:\r
6009     Translate(hdlg, DLG_IndexNumber);\r
6010     ofname = (OPENFILENAME *)lParam;\r
6011     number = (UINT *)(ofname->lCustData);\r
6012     break;\r
6013   case WM_NOTIFY:\r
6014     ofnot = (OFNOTIFY *)lParam;\r
6015     if (ofnot->hdr.code == CDN_FILEOK) {\r
6016       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6017     }\r
6018     break;\r
6019   }\r
6020   return 0;\r
6021 }\r
6022 \r
6023 \r
6024 FILE *\r
6025 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6026                char *nameFilt, char *dlgTitle, UINT *number,\r
6027                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6028 {\r
6029   OPENFILENAME openFileName;\r
6030   char buf1[MSG_SIZ];\r
6031   FILE *f;\r
6032 \r
6033   if (fileName == NULL) fileName = buf1;\r
6034   if (defName == NULL) {\r
6035     safeStrCpy(fileName, "*.", 3 );\r
6036     strcat(fileName, defExt);\r
6037   } else {\r
6038     safeStrCpy(fileName, defName, MSG_SIZ );\r
6039   }\r
6040     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
6041   if (number) *number = 0;\r
6042 \r
6043   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6044   openFileName.hwndOwner         = hwnd;\r
6045   openFileName.hInstance         = (HANDLE) hInst;\r
6046   openFileName.lpstrFilter       = nameFilt;\r
6047   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6048   openFileName.nMaxCustFilter    = 0L;\r
6049   openFileName.nFilterIndex      = 1L;\r
6050   openFileName.lpstrFile         = fileName;\r
6051   openFileName.nMaxFile          = MSG_SIZ;\r
6052   openFileName.lpstrFileTitle    = fileTitle;\r
6053   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6054   openFileName.lpstrInitialDir   = NULL;\r
6055   openFileName.lpstrTitle        = dlgTitle;\r
6056   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6057     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6058     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6059     | (oldDialog ? 0 : OFN_EXPLORER);\r
6060   openFileName.nFileOffset       = 0;\r
6061   openFileName.nFileExtension    = 0;\r
6062   openFileName.lpstrDefExt       = defExt;\r
6063   openFileName.lCustData         = (LONG) number;\r
6064   openFileName.lpfnHook          = oldDialog ?\r
6065     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6066   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6067 \r
6068   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6069                         GetOpenFileName(&openFileName)) {\r
6070     /* open the file */\r
6071     f = fopen(openFileName.lpstrFile, write);\r
6072     if (f == NULL) {\r
6073       MessageBox(hwnd, _("File open failed"), NULL,\r
6074                  MB_OK|MB_ICONEXCLAMATION);\r
6075       return NULL;\r
6076     }\r
6077   } else {\r
6078     int err = CommDlgExtendedError();\r
6079     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6080     return FALSE;\r
6081   }\r
6082   return f;\r
6083 }\r
6084 \r
6085 \r
6086 \r
6087 VOID APIENTRY\r
6088 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6089 {\r
6090   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6091 \r
6092   /*\r
6093    * Get the first pop-up menu in the menu template. This is the\r
6094    * menu that TrackPopupMenu displays.\r
6095    */\r
6096   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6097   TranslateOneMenu(10, hmenuTrackPopup);\r
6098 \r
6099   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6100 \r
6101   /*\r
6102    * TrackPopup uses screen coordinates, so convert the\r
6103    * coordinates of the mouse click to screen coordinates.\r
6104    */\r
6105   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6106 \r
6107   /* Draw and track the floating pop-up menu. */\r
6108   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6109                  pt.x, pt.y, 0, hwnd, NULL);\r
6110 \r
6111   /* Destroy the menu.*/\r
6112   DestroyMenu(hmenu);\r
6113 }\r
6114    \r
6115 typedef struct {\r
6116   HWND hDlg, hText;\r
6117   int sizeX, sizeY, newSizeX, newSizeY;\r
6118   HDWP hdwp;\r
6119 } ResizeEditPlusButtonsClosure;\r
6120 \r
6121 BOOL CALLBACK\r
6122 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6123 {\r
6124   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6125   RECT rect;\r
6126   POINT pt;\r
6127 \r
6128   if (hChild == cl->hText) return TRUE;\r
6129   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6130   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6131   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6132   ScreenToClient(cl->hDlg, &pt);\r
6133   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6134     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6135   return TRUE;\r
6136 }\r
6137 \r
6138 /* Resize a dialog that has a (rich) edit field filling most of\r
6139    the top, with a row of buttons below */\r
6140 VOID\r
6141 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6142 {\r
6143   RECT rectText;\r
6144   int newTextHeight, newTextWidth;\r
6145   ResizeEditPlusButtonsClosure cl;\r
6146   \r
6147   /*if (IsIconic(hDlg)) return;*/\r
6148   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6149   \r
6150   cl.hdwp = BeginDeferWindowPos(8);\r
6151 \r
6152   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6153   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6154   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6155   if (newTextHeight < 0) {\r
6156     newSizeY += -newTextHeight;\r
6157     newTextHeight = 0;\r
6158   }\r
6159   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6160     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6161 \r
6162   cl.hDlg = hDlg;\r
6163   cl.hText = hText;\r
6164   cl.sizeX = sizeX;\r
6165   cl.sizeY = sizeY;\r
6166   cl.newSizeX = newSizeX;\r
6167   cl.newSizeY = newSizeY;\r
6168   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6169 \r
6170   EndDeferWindowPos(cl.hdwp);\r
6171 }\r
6172 \r
6173 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6174 {\r
6175     RECT    rChild, rParent;\r
6176     int     wChild, hChild, wParent, hParent;\r
6177     int     wScreen, hScreen, xNew, yNew;\r
6178     HDC     hdc;\r
6179 \r
6180     /* Get the Height and Width of the child window */\r
6181     GetWindowRect (hwndChild, &rChild);\r
6182     wChild = rChild.right - rChild.left;\r
6183     hChild = rChild.bottom - rChild.top;\r
6184 \r
6185     /* Get the Height and Width of the parent window */\r
6186     GetWindowRect (hwndParent, &rParent);\r
6187     wParent = rParent.right - rParent.left;\r
6188     hParent = rParent.bottom - rParent.top;\r
6189 \r
6190     /* Get the display limits */\r
6191     hdc = GetDC (hwndChild);\r
6192     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6193     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6194     ReleaseDC(hwndChild, hdc);\r
6195 \r
6196     /* Calculate new X position, then adjust for screen */\r
6197     xNew = rParent.left + ((wParent - wChild) /2);\r
6198     if (xNew < 0) {\r
6199         xNew = 0;\r
6200     } else if ((xNew+wChild) > wScreen) {\r
6201         xNew = wScreen - wChild;\r
6202     }\r
6203 \r
6204     /* Calculate new Y position, then adjust for screen */\r
6205     if( mode == 0 ) {\r
6206         yNew = rParent.top  + ((hParent - hChild) /2);\r
6207     }\r
6208     else {\r
6209         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6210     }\r
6211 \r
6212     if (yNew < 0) {\r
6213         yNew = 0;\r
6214     } else if ((yNew+hChild) > hScreen) {\r
6215         yNew = hScreen - hChild;\r
6216     }\r
6217 \r
6218     /* Set it, and return */\r
6219     return SetWindowPos (hwndChild, NULL,\r
6220                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6221 }\r
6222 \r
6223 /* Center one window over another */\r
6224 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6225 {\r
6226     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6227 }\r
6228 \r
6229 /*---------------------------------------------------------------------------*\\r
6230  *\r
6231  * Startup Dialog functions\r
6232  *\r
6233 \*---------------------------------------------------------------------------*/\r
6234 void\r
6235 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6236 {\r
6237   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6238 \r
6239   while (*cd != NULL) {\r
6240     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6241     cd++;\r
6242   }\r
6243 }\r
6244 \r
6245 void\r
6246 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6247 {\r
6248   char buf1[MAX_ARG_LEN];\r
6249   int len;\r
6250 \r
6251   if (str[0] == '@') {\r
6252     FILE* f = fopen(str + 1, "r");\r
6253     if (f == NULL) {\r
6254       DisplayFatalError(str + 1, errno, 2);\r
6255       return;\r
6256     }\r
6257     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6258     fclose(f);\r
6259     buf1[len] = NULLCHAR;\r
6260     str = buf1;\r
6261   }\r
6262 \r
6263   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6264 \r
6265   for (;;) {\r
6266     char buf[MSG_SIZ];\r
6267     char *end = strchr(str, '\n');\r
6268     if (end == NULL) return;\r
6269     memcpy(buf, str, end - str);\r
6270     buf[end - str] = NULLCHAR;\r
6271     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6272     str = end + 1;\r
6273   }\r
6274 }\r
6275 \r
6276 void\r
6277 SetStartupDialogEnables(HWND hDlg)\r
6278 {\r
6279   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6280     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6281     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6282   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6283     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6284   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6285     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6286   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6287     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6288   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6289     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6290     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6291     IsDlgButtonChecked(hDlg, OPT_View));\r
6292 }\r
6293 \r
6294 char *\r
6295 QuoteForFilename(char *filename)\r
6296 {\r
6297   int dquote, space;\r
6298   dquote = strchr(filename, '"') != NULL;\r
6299   space = strchr(filename, ' ') != NULL;\r
6300   if (dquote || space) {\r
6301     if (dquote) {\r
6302       return "'";\r
6303     } else {\r
6304       return "\"";\r
6305     }\r
6306   } else {\r
6307     return "";\r
6308   }\r
6309 }\r
6310 \r
6311 VOID\r
6312 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6313 {\r
6314   char buf[MSG_SIZ];\r
6315   char *q;\r
6316 \r
6317   InitComboStringsFromOption(hwndCombo, nthnames);\r
6318   q = QuoteForFilename(nthcp);\r
6319     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6320   if (*nthdir != NULLCHAR) {\r
6321     q = QuoteForFilename(nthdir);\r
6322       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6323   }\r
6324   if (*nthcp == NULLCHAR) {\r
6325     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6326   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6327     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6328     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6329   }\r
6330 }\r
6331 \r
6332 LRESULT CALLBACK\r
6333 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6334 {\r
6335   char buf[MSG_SIZ];\r
6336   HANDLE hwndCombo;\r
6337   char *p;\r
6338 \r
6339   switch (message) {\r
6340   case WM_INITDIALOG:\r
6341     /* Center the dialog */\r
6342     CenterWindow (hDlg, GetDesktopWindow());\r
6343     Translate(hDlg, DLG_Startup);\r
6344     /* Initialize the dialog items */\r
6345     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6346                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6347                   firstChessProgramNames);\r
6348     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6349                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6350                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6351     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6352     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6353       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6354     if (*appData.icsHelper != NULLCHAR) {\r
6355       char *q = QuoteForFilename(appData.icsHelper);\r
6356       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6357     }\r
6358     if (*appData.icsHost == NULLCHAR) {\r
6359       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6360       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6361     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6362       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6363       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6364     }\r
6365 \r
6366     if (appData.icsActive) {\r
6367       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6368     }\r
6369     else if (appData.noChessProgram) {\r
6370       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6371     }\r
6372     else {\r
6373       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6374     }\r
6375 \r
6376     SetStartupDialogEnables(hDlg);\r
6377     return TRUE;\r
6378 \r
6379   case WM_COMMAND:\r
6380     switch (LOWORD(wParam)) {\r
6381     case IDOK:\r
6382       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6383         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6384         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6385         p = buf;\r
6386         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6387         ParseArgs(StringGet, &p);\r
6388         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6389         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6390         p = buf;\r
6391         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6392         ParseArgs(StringGet, &p);\r
6393         SwapEngines(singleList); // ... and then make it 'second'\r
6394 \r
6395         appData.noChessProgram = FALSE;\r
6396         appData.icsActive = FALSE;\r
6397       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6398         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6399         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6400         p = buf;\r
6401         ParseArgs(StringGet, &p);\r
6402         if (appData.zippyPlay) {\r
6403           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6404           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6405           p = buf;\r
6406           ParseArgs(StringGet, &p);\r
6407         }\r
6408       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6409         appData.noChessProgram = TRUE;\r
6410         appData.icsActive = FALSE;\r
6411       } else {\r
6412         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6413                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6414         return TRUE;\r
6415       }\r
6416       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6417         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6418         p = buf;\r
6419         ParseArgs(StringGet, &p);\r
6420       }\r
6421       EndDialog(hDlg, TRUE);\r
6422       return TRUE;\r
6423 \r
6424     case IDCANCEL:\r
6425       ExitEvent(0);\r
6426       return TRUE;\r
6427 \r
6428     case IDM_HELPCONTENTS:\r
6429       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6430         MessageBox (GetFocus(),\r
6431                     _("Unable to activate help"),\r
6432                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6433       }\r
6434       break;\r
6435 \r
6436     default:\r
6437       SetStartupDialogEnables(hDlg);\r
6438       break;\r
6439     }\r
6440     break;\r
6441   }\r
6442   return FALSE;\r
6443 }\r
6444 \r
6445 /*---------------------------------------------------------------------------*\\r
6446  *\r
6447  * About box dialog functions\r
6448  *\r
6449 \*---------------------------------------------------------------------------*/\r
6450 \r
6451 /* Process messages for "About" dialog box */\r
6452 LRESULT CALLBACK\r
6453 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6454 {\r
6455   switch (message) {\r
6456   case WM_INITDIALOG: /* message: initialize dialog box */\r
6457     /* Center the dialog over the application window */\r
6458     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6459     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6460     Translate(hDlg, ABOUTBOX);\r
6461     JAWS_COPYRIGHT\r
6462     return (TRUE);\r
6463 \r
6464   case WM_COMMAND: /* message: received a command */\r
6465     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6466         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6467       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6468       return (TRUE);\r
6469     }\r
6470     break;\r
6471   }\r
6472   return (FALSE);\r
6473 }\r
6474 \r
6475 /*---------------------------------------------------------------------------*\\r
6476  *\r
6477  * Comment Dialog functions\r
6478  *\r
6479 \*---------------------------------------------------------------------------*/\r
6480 \r
6481 LRESULT CALLBACK\r
6482 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6483 {\r
6484   static HANDLE hwndText = NULL;\r
6485   int len, newSizeX, newSizeY;\r
6486   static int sizeX, sizeY;\r
6487   char *str;\r
6488   RECT rect;\r
6489   MINMAXINFO *mmi;\r
6490 \r
6491   switch (message) {\r
6492   case WM_INITDIALOG: /* message: initialize dialog box */\r
6493     /* Initialize the dialog items */\r
6494     Translate(hDlg, DLG_EditComment);\r
6495     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6496     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6497     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6498     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6499     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6500     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6501     SetWindowText(hDlg, commentTitle);\r
6502     if (editComment) {\r
6503       SetFocus(hwndText);\r
6504     } else {\r
6505       SetFocus(GetDlgItem(hDlg, IDOK));\r
6506     }\r
6507     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6508                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6509                 MAKELPARAM(FALSE, 0));\r
6510     /* Size and position the dialog */\r
6511     if (!commentDialog) {\r
6512       commentDialog = hDlg;\r
6513       GetClientRect(hDlg, &rect);\r
6514       sizeX = rect.right;\r
6515       sizeY = rect.bottom;\r
6516       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6517           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6518         WINDOWPLACEMENT wp;\r
6519         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6520         wp.length = sizeof(WINDOWPLACEMENT);\r
6521         wp.flags = 0;\r
6522         wp.showCmd = SW_SHOW;\r
6523         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6524         wp.rcNormalPosition.left = wpComment.x;\r
6525         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6526         wp.rcNormalPosition.top = wpComment.y;\r
6527         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6528         SetWindowPlacement(hDlg, &wp);\r
6529 \r
6530         GetClientRect(hDlg, &rect);\r
6531         newSizeX = rect.right;\r
6532         newSizeY = rect.bottom;\r
6533         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6534                               newSizeX, newSizeY);\r
6535         sizeX = newSizeX;\r
6536         sizeY = newSizeY;\r
6537       }\r
6538     }\r
6539     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6540     return FALSE;\r
6541 \r
6542   case WM_COMMAND: /* message: received a command */\r
6543     switch (LOWORD(wParam)) {\r
6544     case IDOK:\r
6545       if (editComment) {\r
6546         char *p, *q;\r
6547         /* Read changed options from the dialog box */\r
6548         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6549         len = GetWindowTextLength(hwndText);\r
6550         str = (char *) malloc(len + 1);\r
6551         GetWindowText(hwndText, str, len + 1);\r
6552         p = q = str;\r
6553         while (*q) {\r
6554           if (*q == '\r')\r
6555             q++;\r
6556           else\r
6557             *p++ = *q++;\r
6558         }\r
6559         *p = NULLCHAR;\r
6560         ReplaceComment(commentIndex, str);\r
6561         free(str);\r
6562       }\r
6563       CommentPopDown();\r
6564       return TRUE;\r
6565 \r
6566     case IDCANCEL:\r
6567     case OPT_CancelComment:\r
6568       CommentPopDown();\r
6569       return TRUE;\r
6570 \r
6571     case OPT_ClearComment:\r
6572       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6573       break;\r
6574 \r
6575     case OPT_EditComment:\r
6576       EditCommentEvent();\r
6577       return TRUE;\r
6578 \r
6579     default:\r
6580       break;\r
6581     }\r
6582     break;\r
6583 \r
6584   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6585         if( wParam == OPT_CommentText ) {\r
6586             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6587 \r
6588             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6589                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6590                 POINTL pt;\r
6591                 LRESULT index;\r
6592 \r
6593                 pt.x = LOWORD( lpMF->lParam );\r
6594                 pt.y = HIWORD( lpMF->lParam );\r
6595 \r
6596                 if(lpMF->msg == WM_CHAR) {\r
6597                         CHARRANGE sel;\r
6598                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6599                         index = sel.cpMin;\r
6600                 } else\r
6601                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6602 \r
6603                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6604                 len = GetWindowTextLength(hwndText);\r
6605                 str = (char *) malloc(len + 1);\r
6606                 GetWindowText(hwndText, str, len + 1);\r
6607                 ReplaceComment(commentIndex, str);\r
6608                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6609                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6610                 free(str);\r
6611 \r
6612                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6613                 lpMF->msg = WM_USER;\r
6614 \r
6615                 return TRUE;\r
6616             }\r
6617         }\r
6618         break;\r
6619 \r
6620   case WM_SIZE:\r
6621     newSizeX = LOWORD(lParam);\r
6622     newSizeY = HIWORD(lParam);\r
6623     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6624     sizeX = newSizeX;\r
6625     sizeY = newSizeY;\r
6626     break;\r
6627 \r
6628   case WM_GETMINMAXINFO:\r
6629     /* Prevent resizing window too small */\r
6630     mmi = (MINMAXINFO *) lParam;\r
6631     mmi->ptMinTrackSize.x = 100;\r
6632     mmi->ptMinTrackSize.y = 100;\r
6633     break;\r
6634   }\r
6635   return FALSE;\r
6636 }\r
6637 \r
6638 VOID\r
6639 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6640 {\r
6641   FARPROC lpProc;\r
6642   char *p, *q;\r
6643 \r
6644   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6645 \r
6646   if (str == NULL) str = "";\r
6647   p = (char *) malloc(2 * strlen(str) + 2);\r
6648   q = p;\r
6649   while (*str) {\r
6650     if (*str == '\n') *q++ = '\r';\r
6651     *q++ = *str++;\r
6652   }\r
6653   *q = NULLCHAR;\r
6654   if (commentText != NULL) free(commentText);\r
6655 \r
6656   commentIndex = index;\r
6657   commentTitle = title;\r
6658   commentText = p;\r
6659   editComment = edit;\r
6660 \r
6661   if (commentDialog) {\r
6662     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6663     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6664   } else {\r
6665     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6666     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6667                  hwndMain, (DLGPROC)lpProc);\r
6668     FreeProcInstance(lpProc);\r
6669   }\r
6670   commentUp = TRUE;\r
6671 }\r
6672 \r
6673 \r
6674 /*---------------------------------------------------------------------------*\\r
6675  *\r
6676  * Type-in move dialog functions\r
6677  * \r
6678 \*---------------------------------------------------------------------------*/\r
6679 \r
6680 LRESULT CALLBACK\r
6681 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6682 {\r
6683   char move[MSG_SIZ];\r
6684   HWND hInput;\r
6685 \r
6686   switch (message) {\r
6687   case WM_INITDIALOG:\r
6688     move[0] = (char) lParam;\r
6689     move[1] = NULLCHAR;\r
6690     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6691     Translate(hDlg, DLG_TypeInMove);\r
6692     hInput = GetDlgItem(hDlg, OPT_Move);\r
6693     SetWindowText(hInput, move);\r
6694     SetFocus(hInput);\r
6695     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6696     return FALSE;\r
6697 \r
6698   case WM_COMMAND:\r
6699     switch (LOWORD(wParam)) {\r
6700     case IDOK:\r
6701 \r
6702       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6703       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6704       TypeInDoneEvent(move);\r
6705       EndDialog(hDlg, TRUE);\r
6706       return TRUE;\r
6707     case IDCANCEL:\r
6708       EndDialog(hDlg, FALSE);\r
6709       return TRUE;\r
6710     default:\r
6711       break;\r
6712     }\r
6713     break;\r
6714   }\r
6715   return FALSE;\r
6716 }\r
6717 \r
6718 VOID\r
6719 PopUpMoveDialog(char firstchar)\r
6720 {\r
6721     FARPROC lpProc;\r
6722 \r
6723       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6724       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6725         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6726       FreeProcInstance(lpProc);\r
6727 }\r
6728 \r
6729 /*---------------------------------------------------------------------------*\\r
6730  *\r
6731  * Type-in name dialog functions\r
6732  * \r
6733 \*---------------------------------------------------------------------------*/\r
6734 \r
6735 LRESULT CALLBACK\r
6736 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6737 {\r
6738   char move[MSG_SIZ];\r
6739   HWND hInput;\r
6740 \r
6741   switch (message) {\r
6742   case WM_INITDIALOG:\r
6743     move[0] = (char) lParam;\r
6744     move[1] = NULLCHAR;\r
6745     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6746     Translate(hDlg, DLG_TypeInName);\r
6747     hInput = GetDlgItem(hDlg, OPT_Name);\r
6748     SetWindowText(hInput, move);\r
6749     SetFocus(hInput);\r
6750     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6751     return FALSE;\r
6752 \r
6753   case WM_COMMAND:\r
6754     switch (LOWORD(wParam)) {\r
6755     case IDOK:\r
6756       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6757       appData.userName = strdup(move);\r
6758       SetUserLogo(); DisplayLogos();\r
6759       SetGameInfo();\r
6760       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6761         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6762         DisplayTitle(move);\r
6763       }\r
6764 \r
6765 \r
6766       EndDialog(hDlg, TRUE);\r
6767       return TRUE;\r
6768     case IDCANCEL:\r
6769       EndDialog(hDlg, FALSE);\r
6770       return TRUE;\r
6771     default:\r
6772       break;\r
6773     }\r
6774     break;\r
6775   }\r
6776   return FALSE;\r
6777 }\r
6778 \r
6779 VOID\r
6780 PopUpNameDialog(char firstchar)\r
6781 {\r
6782     FARPROC lpProc;\r
6783     \r
6784       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6785       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6786         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6787       FreeProcInstance(lpProc);\r
6788 }\r
6789 \r
6790 /*---------------------------------------------------------------------------*\\r
6791  *\r
6792  *  Error dialogs\r
6793  * \r
6794 \*---------------------------------------------------------------------------*/\r
6795 \r
6796 /* Nonmodal error box */\r
6797 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6798                              WPARAM wParam, LPARAM lParam);\r
6799 \r
6800 VOID\r
6801 ErrorPopUp(char *title, char *content)\r
6802 {\r
6803   FARPROC lpProc;\r
6804   char *p, *q;\r
6805   BOOLEAN modal = hwndMain == NULL;\r
6806 \r
6807   p = content;\r
6808   q = errorMessage;\r
6809   while (*p) {\r
6810     if (*p == '\n') {\r
6811       if (modal) {\r
6812         *q++ = ' ';\r
6813         p++;\r
6814       } else {\r
6815         *q++ = '\r';\r
6816         *q++ = *p++;\r
6817       }\r
6818     } else {\r
6819       *q++ = *p++;\r
6820     }\r
6821   }\r
6822   *q = NULLCHAR;\r
6823   strncpy(errorTitle, title, sizeof(errorTitle));\r
6824   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6825   \r
6826   if (modal) {\r
6827     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6828   } else {\r
6829     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6830     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6831                  hwndMain, (DLGPROC)lpProc);\r
6832     FreeProcInstance(lpProc);\r
6833   }\r
6834 }\r
6835 \r
6836 VOID\r
6837 ErrorPopDown()\r
6838 {\r
6839   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6840   if (errorDialog == NULL) return;\r
6841   DestroyWindow(errorDialog);\r
6842   errorDialog = NULL;\r
6843   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6844 }\r
6845 \r
6846 LRESULT CALLBACK\r
6847 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6848 {\r
6849   RECT rChild;\r
6850 \r
6851   switch (message) {\r
6852   case WM_INITDIALOG:\r
6853     GetWindowRect(hDlg, &rChild);\r
6854 \r
6855     /*\r
6856     SetWindowPos(hDlg, NULL, rChild.left,\r
6857       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6858       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6859     */\r
6860 \r
6861     /* \r
6862         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6863         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6864         and it doesn't work when you resize the dialog.\r
6865         For now, just give it a default position.\r
6866     */\r
6867     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6868     Translate(hDlg, DLG_Error);\r
6869 \r
6870     errorDialog = hDlg;\r
6871     SetWindowText(hDlg, errorTitle);\r
6872     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6873     return FALSE;\r
6874 \r
6875   case WM_COMMAND:\r
6876     switch (LOWORD(wParam)) {\r
6877     case IDOK:\r
6878     case IDCANCEL:\r
6879       if (errorDialog == hDlg) errorDialog = NULL;\r
6880       DestroyWindow(hDlg);\r
6881       return TRUE;\r
6882 \r
6883     default:\r
6884       break;\r
6885     }\r
6886     break;\r
6887   }\r
6888   return FALSE;\r
6889 }\r
6890 \r
6891 #ifdef GOTHIC\r
6892 HWND gothicDialog = NULL;\r
6893 \r
6894 LRESULT CALLBACK\r
6895 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6896 {\r
6897   RECT rChild;\r
6898   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6899 \r
6900   switch (message) {\r
6901   case WM_INITDIALOG:\r
6902     GetWindowRect(hDlg, &rChild);\r
6903 \r
6904     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6905                                                              SWP_NOZORDER);\r
6906 \r
6907     /* \r
6908         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6909         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6910         and it doesn't work when you resize the dialog.\r
6911         For now, just give it a default position.\r
6912     */\r
6913     gothicDialog = hDlg;\r
6914     SetWindowText(hDlg, errorTitle);\r
6915     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6916     return FALSE;\r
6917 \r
6918   case WM_COMMAND:\r
6919     switch (LOWORD(wParam)) {\r
6920     case IDOK:\r
6921     case IDCANCEL:\r
6922       if (errorDialog == hDlg) errorDialog = NULL;\r
6923       DestroyWindow(hDlg);\r
6924       return TRUE;\r
6925 \r
6926     default:\r
6927       break;\r
6928     }\r
6929     break;\r
6930   }\r
6931   return FALSE;\r
6932 }\r
6933 \r
6934 VOID\r
6935 GothicPopUp(char *title, VariantClass variant)\r
6936 {\r
6937   FARPROC lpProc;\r
6938   static char *lastTitle;\r
6939 \r
6940   strncpy(errorTitle, title, sizeof(errorTitle));\r
6941   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6942 \r
6943   if(lastTitle != title && gothicDialog != NULL) {\r
6944     DestroyWindow(gothicDialog);\r
6945     gothicDialog = NULL;\r
6946   }\r
6947   if(variant != VariantNormal && gothicDialog == NULL) {\r
6948     title = lastTitle;\r
6949     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6950     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6951                  hwndMain, (DLGPROC)lpProc);\r
6952     FreeProcInstance(lpProc);\r
6953   }\r
6954 }\r
6955 #endif\r
6956 \r
6957 /*---------------------------------------------------------------------------*\\r
6958  *\r
6959  *  Ics Interaction console functions\r
6960  *\r
6961 \*---------------------------------------------------------------------------*/\r
6962 \r
6963 #define HISTORY_SIZE 64\r
6964 static char *history[HISTORY_SIZE];\r
6965 int histIn = 0, histP = 0;\r
6966 \r
6967 \r
6968 VOID\r
6969 SaveInHistory(char *cmd)\r
6970 {\r
6971   if (history[histIn] != NULL) {\r
6972     free(history[histIn]);\r
6973     history[histIn] = NULL;\r
6974   }\r
6975   if (*cmd == NULLCHAR) return;\r
6976   history[histIn] = StrSave(cmd);\r
6977   histIn = (histIn + 1) % HISTORY_SIZE;\r
6978   if (history[histIn] != NULL) {\r
6979     free(history[histIn]);\r
6980 \r
6981     history[histIn] = NULL;\r
6982   }\r
6983   histP = histIn;\r
6984 }\r
6985 \r
6986 char *\r
6987 PrevInHistory(char *cmd)\r
6988 {\r
6989   int newhp;\r
6990   if (histP == histIn) {\r
6991     if (history[histIn] != NULL) free(history[histIn]);\r
6992     history[histIn] = StrSave(cmd);\r
6993   }\r
6994   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6995   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6996   histP = newhp;\r
6997   return history[histP];\r
6998 }\r
6999 \r
7000 char *\r
7001 NextInHistory()\r
7002 {\r
7003   if (histP == histIn) return NULL;\r
7004   histP = (histP + 1) % HISTORY_SIZE;\r
7005   return history[histP];   \r
7006 }\r
7007 \r
7008 HMENU\r
7009 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7010 {\r
7011   HMENU hmenu, h;\r
7012   int i = 0;\r
7013   hmenu = LoadMenu(hInst, "TextMenu");\r
7014   h = GetSubMenu(hmenu, 0);\r
7015   while (e->item) {\r
7016     if (strcmp(e->item, "-") == 0) {\r
7017       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7018     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
7019       int flags = MF_STRING, j = 0;\r
7020       if (e->item[0] == '|') {\r
7021         flags |= MF_MENUBARBREAK;\r
7022         j++;\r
7023       }\r
7024       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
7025       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
7026     }\r
7027     e++;\r
7028     i++;\r
7029   } \r
7030   return hmenu;\r
7031 }\r
7032 \r
7033 WNDPROC consoleTextWindowProc;\r
7034 \r
7035 void\r
7036 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7037 {\r
7038   char buf[MSG_SIZ], name[MSG_SIZ];\r
7039   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7040   CHARRANGE sel;\r
7041 \r
7042   if (!getname) {\r
7043     SetWindowText(hInput, command);\r
7044     if (immediate) {\r
7045       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7046     } else {\r
7047       sel.cpMin = 999999;\r
7048       sel.cpMax = 999999;\r
7049       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7050       SetFocus(hInput);\r
7051     }\r
7052     return;\r
7053   }    \r
7054   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7055   if (sel.cpMin == sel.cpMax) {\r
7056     /* Expand to surrounding word */\r
7057     TEXTRANGE tr;\r
7058     do {\r
7059       tr.chrg.cpMax = sel.cpMin;\r
7060       tr.chrg.cpMin = --sel.cpMin;\r
7061       if (sel.cpMin < 0) break;\r
7062       tr.lpstrText = name;\r
7063       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7064     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7065     sel.cpMin++;\r
7066 \r
7067     do {\r
7068       tr.chrg.cpMin = sel.cpMax;\r
7069       tr.chrg.cpMax = ++sel.cpMax;\r
7070       tr.lpstrText = name;\r
7071       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7072     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7073     sel.cpMax--;\r
7074 \r
7075     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7076       MessageBeep(MB_ICONEXCLAMATION);\r
7077       return;\r
7078     }\r
7079     tr.chrg = sel;\r
7080     tr.lpstrText = name;\r
7081     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7082   } else {\r
7083     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7084       MessageBeep(MB_ICONEXCLAMATION);\r
7085       return;\r
7086     }\r
7087     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7088   }\r
7089   if (immediate) {\r
7090     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7091     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7092     SetWindowText(hInput, buf);\r
7093     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7094   } else {\r
7095     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7096       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7097     SetWindowText(hInput, buf);\r
7098     sel.cpMin = 999999;\r
7099     sel.cpMax = 999999;\r
7100     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7101     SetFocus(hInput);\r
7102   }\r
7103 }\r
7104 \r
7105 LRESULT CALLBACK \r
7106 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7107 {\r
7108   HWND hInput;\r
7109   CHARRANGE sel;\r
7110 \r
7111   switch (message) {\r
7112   case WM_KEYDOWN:\r
7113     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7114     if(wParam=='R') return 0;\r
7115     switch (wParam) {\r
7116     case VK_PRIOR:\r
7117       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7118       return 0;\r
7119     case VK_NEXT:\r
7120       sel.cpMin = 999999;\r
7121       sel.cpMax = 999999;\r
7122       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7123       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7124       return 0;\r
7125     }\r
7126     break;\r
7127   case WM_CHAR:\r
7128    if(wParam != '\022') {\r
7129     if (wParam == '\t') {\r
7130       if (GetKeyState(VK_SHIFT) < 0) {\r
7131         /* shifted */\r
7132         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7133         if (buttonDesc[0].hwnd) {\r
7134           SetFocus(buttonDesc[0].hwnd);\r
7135         } else {\r
7136           SetFocus(hwndMain);\r
7137         }\r
7138       } else {\r
7139         /* unshifted */\r
7140         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7141       }\r
7142     } else {\r
7143       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7144       JAWS_DELETE( SetFocus(hInput); )\r
7145       SendMessage(hInput, message, wParam, lParam);\r
7146     }\r
7147     return 0;\r
7148    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7149    lParam = -1;\r
7150   case WM_RBUTTONDOWN:\r
7151     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7152       /* Move selection here if it was empty */\r
7153       POINT pt;\r
7154       pt.x = LOWORD(lParam);\r
7155       pt.y = HIWORD(lParam);\r
7156       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7157       if (sel.cpMin == sel.cpMax) {\r
7158         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7159         sel.cpMax = sel.cpMin;\r
7160         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7161       }\r
7162       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7163 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7164       POINT pt;\r
7165       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7166       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7167       if (sel.cpMin == sel.cpMax) {\r
7168         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7169         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7170       }\r
7171       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7172         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7173       }\r
7174       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7175       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7176       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7177       MenuPopup(hwnd, pt, hmenu, -1);\r
7178 }\r
7179     }\r
7180     return 0;\r
7181   case WM_RBUTTONUP:\r
7182     if (GetKeyState(VK_SHIFT) & ~1) {\r
7183       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7184         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7185     }\r
7186     return 0;\r
7187   case WM_PASTE:\r
7188     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7189     SetFocus(hInput);\r
7190     return SendMessage(hInput, message, wParam, lParam);\r
7191   case WM_MBUTTONDOWN:\r
7192     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7193   case WM_COMMAND:\r
7194     switch (LOWORD(wParam)) {\r
7195     case IDM_QuickPaste:\r
7196       {\r
7197         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7198         if (sel.cpMin == sel.cpMax) {\r
7199           MessageBeep(MB_ICONEXCLAMATION);\r
7200           return 0;\r
7201         }\r
7202         SendMessage(hwnd, WM_COPY, 0, 0);\r
7203         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7204         SendMessage(hInput, WM_PASTE, 0, 0);\r
7205         SetFocus(hInput);\r
7206         return 0;\r
7207       }\r
7208     case IDM_Cut:\r
7209       SendMessage(hwnd, WM_CUT, 0, 0);\r
7210       return 0;\r
7211     case IDM_Paste:\r
7212       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7213       return 0;\r
7214     case IDM_Copy:\r
7215       SendMessage(hwnd, WM_COPY, 0, 0);\r
7216       return 0;\r
7217     default:\r
7218       {\r
7219         int i = LOWORD(wParam) - IDM_CommandX;\r
7220         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7221             icsTextMenuEntry[i].command != NULL) {\r
7222           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7223                    icsTextMenuEntry[i].getname,\r
7224                    icsTextMenuEntry[i].immediate);\r
7225           return 0;\r
7226         }\r
7227       }\r
7228       break;\r
7229     }\r
7230     break;\r
7231   }\r
7232   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7233 }\r
7234 \r
7235 WNDPROC consoleInputWindowProc;\r
7236 \r
7237 LRESULT CALLBACK\r
7238 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7239 {\r
7240   char buf[MSG_SIZ];\r
7241   char *p;\r
7242   static BOOL sendNextChar = FALSE;\r
7243   static BOOL quoteNextChar = FALSE;\r
7244   InputSource *is = consoleInputSource;\r
7245   CHARFORMAT cf;\r
7246   CHARRANGE sel;\r
7247 \r
7248   switch (message) {\r
7249   case WM_CHAR:\r
7250     if (!appData.localLineEditing || sendNextChar) {\r
7251       is->buf[0] = (CHAR) wParam;\r
7252       is->count = 1;\r
7253       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7254       sendNextChar = FALSE;\r
7255       return 0;\r
7256     }\r
7257     if (quoteNextChar) {\r
7258       buf[0] = (char) wParam;\r
7259       buf[1] = NULLCHAR;\r
7260       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7261       quoteNextChar = FALSE;\r
7262       return 0;\r
7263     }\r
7264     switch (wParam) {\r
7265     case '\r':   /* Enter key */\r
7266       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7267       if (consoleEcho) SaveInHistory(is->buf);\r
7268       is->buf[is->count++] = '\n';\r
7269       is->buf[is->count] = NULLCHAR;\r
7270       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7271       if (consoleEcho) {\r
7272         ConsoleOutput(is->buf, is->count, TRUE);\r
7273       } else if (appData.localLineEditing) {\r
7274         ConsoleOutput("\n", 1, TRUE);\r
7275       }\r
7276       /* fall thru */\r
7277     case '\033': /* Escape key */\r
7278       SetWindowText(hwnd, "");\r
7279       cf.cbSize = sizeof(CHARFORMAT);\r
7280       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7281       if (consoleEcho) {\r
7282         cf.crTextColor = textAttribs[ColorNormal].color;\r
7283       } else {\r
7284         cf.crTextColor = COLOR_ECHOOFF;\r
7285       }\r
7286       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7287       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7288       return 0;\r
7289     case '\t':   /* Tab key */\r
7290       if (GetKeyState(VK_SHIFT) < 0) {\r
7291         /* shifted */\r
7292         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7293       } else {\r
7294         /* unshifted */\r
7295         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7296         if (buttonDesc[0].hwnd) {\r
7297           SetFocus(buttonDesc[0].hwnd);\r
7298         } else {\r
7299           SetFocus(hwndMain);\r
7300         }\r
7301       }\r
7302       return 0;\r
7303     case '\023': /* Ctrl+S */\r
7304       sendNextChar = TRUE;\r
7305       return 0;\r
7306     case '\021': /* Ctrl+Q */\r
7307       quoteNextChar = TRUE;\r
7308       return 0;\r
7309     JAWS_REPLAY\r
7310     default:\r
7311       break;\r
7312     }\r
7313     break;\r
7314   case WM_KEYDOWN:\r
7315     switch (wParam) {\r
7316     case VK_UP:\r
7317       GetWindowText(hwnd, buf, MSG_SIZ);\r
7318       p = PrevInHistory(buf);\r
7319       if (p != NULL) {\r
7320         SetWindowText(hwnd, p);\r
7321         sel.cpMin = 999999;\r
7322         sel.cpMax = 999999;\r
7323         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7324         return 0;\r
7325       }\r
7326       break;\r
7327     case VK_DOWN:\r
7328       p = NextInHistory();\r
7329       if (p != NULL) {\r
7330         SetWindowText(hwnd, p);\r
7331         sel.cpMin = 999999;\r
7332         sel.cpMax = 999999;\r
7333         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7334         return 0;\r
7335       }\r
7336       break;\r
7337     case VK_HOME:\r
7338     case VK_END:\r
7339       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7340       /* fall thru */\r
7341     case VK_PRIOR:\r
7342     case VK_NEXT:\r
7343       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7344       return 0;\r
7345     }\r
7346     break;\r
7347   case WM_MBUTTONDOWN:\r
7348     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7349       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7350     break;\r
7351   case WM_RBUTTONUP:\r
7352     if (GetKeyState(VK_SHIFT) & ~1) {\r
7353       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7354         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7355     } else {\r
7356       POINT pt;\r
7357       HMENU hmenu;\r
7358       hmenu = LoadMenu(hInst, "InputMenu");\r
7359       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7360       if (sel.cpMin == sel.cpMax) {\r
7361         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7362         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7363       }\r
7364       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7365         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7366       }\r
7367       pt.x = LOWORD(lParam);\r
7368       pt.y = HIWORD(lParam);\r
7369       MenuPopup(hwnd, pt, hmenu, -1);\r
7370     }\r
7371     return 0;\r
7372   case WM_COMMAND:\r
7373     switch (LOWORD(wParam)) { \r
7374     case IDM_Undo:\r
7375       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7376       return 0;\r
7377     case IDM_SelectAll:\r
7378       sel.cpMin = 0;\r
7379       sel.cpMax = -1; /*999999?*/\r
7380       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7381       return 0;\r
7382     case IDM_Cut:\r
7383       SendMessage(hwnd, WM_CUT, 0, 0);\r
7384       return 0;\r
7385     case IDM_Paste:\r
7386       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7387       return 0;\r
7388     case IDM_Copy:\r
7389       SendMessage(hwnd, WM_COPY, 0, 0);\r
7390       return 0;\r
7391     }\r
7392     break;\r
7393   }\r
7394   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7395 }\r
7396 \r
7397 #define CO_MAX  100000\r
7398 #define CO_TRIM   1000\r
7399 \r
7400 LRESULT CALLBACK\r
7401 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7402 {\r
7403   static SnapData sd;\r
7404   HWND hText, hInput;\r
7405   RECT rect;\r
7406   static int sizeX, sizeY;\r
7407   int newSizeX, newSizeY;\r
7408   MINMAXINFO *mmi;\r
7409   WORD wMask;\r
7410 \r
7411   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7412   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7413 \r
7414   switch (message) {\r
7415   case WM_NOTIFY:\r
7416     if (((NMHDR*)lParam)->code == EN_LINK)\r
7417     {\r
7418       ENLINK *pLink = (ENLINK*)lParam;\r
7419       if (pLink->msg == WM_LBUTTONUP)\r
7420       {\r
7421         TEXTRANGE tr;\r
7422 \r
7423         tr.chrg = pLink->chrg;\r
7424         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7425         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7426         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7427         free(tr.lpstrText);\r
7428       }\r
7429     }\r
7430     break;\r
7431   case WM_INITDIALOG: /* message: initialize dialog box */\r
7432     hwndConsole = hDlg;\r
7433     SetFocus(hInput);\r
7434     consoleTextWindowProc = (WNDPROC)\r
7435       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7436     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7437     consoleInputWindowProc = (WNDPROC)\r
7438       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7439     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7440     Colorize(ColorNormal, TRUE);\r
7441     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7442     ChangedConsoleFont();\r
7443     GetClientRect(hDlg, &rect);\r
7444     sizeX = rect.right;\r
7445     sizeY = rect.bottom;\r
7446     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7447         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7448       WINDOWPLACEMENT wp;\r
7449       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7450       wp.length = sizeof(WINDOWPLACEMENT);\r
7451       wp.flags = 0;\r
7452       wp.showCmd = SW_SHOW;\r
7453       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7454       wp.rcNormalPosition.left = wpConsole.x;\r
7455       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7456       wp.rcNormalPosition.top = wpConsole.y;\r
7457       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7458       SetWindowPlacement(hDlg, &wp);\r
7459     }\r
7460 \r
7461    // [HGM] Chessknight's change 2004-07-13\r
7462    else { /* Determine Defaults */\r
7463        WINDOWPLACEMENT wp;\r
7464        wpConsole.x = wpMain.width + 1;\r
7465        wpConsole.y = wpMain.y;\r
7466        wpConsole.width = screenWidth -  wpMain.width;\r
7467        wpConsole.height = wpMain.height;\r
7468        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7469        wp.length = sizeof(WINDOWPLACEMENT);\r
7470        wp.flags = 0;\r
7471        wp.showCmd = SW_SHOW;\r
7472        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7473        wp.rcNormalPosition.left = wpConsole.x;\r
7474        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7475        wp.rcNormalPosition.top = wpConsole.y;\r
7476        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7477        SetWindowPlacement(hDlg, &wp);\r
7478     }\r
7479 \r
7480    // Allow hText to highlight URLs and send notifications on them\r
7481    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7482    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7483    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7484    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7485 \r
7486     return FALSE;\r
7487 \r
7488   case WM_SETFOCUS:\r
7489     SetFocus(hInput);\r
7490     return 0;\r
7491 \r
7492   case WM_CLOSE:\r
7493     ExitEvent(0);\r
7494     /* not reached */\r
7495     break;\r
7496 \r
7497   case WM_SIZE:\r
7498     if (IsIconic(hDlg)) break;\r
7499     newSizeX = LOWORD(lParam);\r
7500     newSizeY = HIWORD(lParam);\r
7501     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7502       RECT rectText, rectInput;\r
7503       POINT pt;\r
7504       int newTextHeight, newTextWidth;\r
7505       GetWindowRect(hText, &rectText);\r
7506       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7507       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7508       if (newTextHeight < 0) {\r
7509         newSizeY += -newTextHeight;\r
7510         newTextHeight = 0;\r
7511       }\r
7512       SetWindowPos(hText, NULL, 0, 0,\r
7513         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7514       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7515       pt.x = rectInput.left;\r
7516       pt.y = rectInput.top + newSizeY - sizeY;\r
7517       ScreenToClient(hDlg, &pt);\r
7518       SetWindowPos(hInput, NULL, \r
7519         pt.x, pt.y, /* needs client coords */   \r
7520         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7521         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7522     }\r
7523     sizeX = newSizeX;\r
7524     sizeY = newSizeY;\r
7525     break;\r
7526 \r
7527   case WM_GETMINMAXINFO:\r
7528     /* Prevent resizing window too small */\r
7529     mmi = (MINMAXINFO *) lParam;\r
7530     mmi->ptMinTrackSize.x = 100;\r
7531     mmi->ptMinTrackSize.y = 100;\r
7532     break;\r
7533 \r
7534   /* [AS] Snapping */\r
7535   case WM_ENTERSIZEMOVE:\r
7536     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7537 \r
7538   case WM_SIZING:\r
7539     return OnSizing( &sd, hDlg, wParam, lParam );\r
7540 \r
7541   case WM_MOVING:\r
7542     return OnMoving( &sd, hDlg, wParam, lParam );\r
7543 \r
7544   case WM_EXITSIZEMOVE:\r
7545         UpdateICSWidth(hText);\r
7546     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7547   }\r
7548 \r
7549   return DefWindowProc(hDlg, message, wParam, lParam);\r
7550 }\r
7551 \r
7552 \r
7553 VOID\r
7554 ConsoleCreate()\r
7555 {\r
7556   HWND hCons;\r
7557   if (hwndConsole) return;\r
7558   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7559   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7560 }\r
7561 \r
7562 \r
7563 VOID\r
7564 ConsoleOutput(char* data, int length, int forceVisible)\r
7565 {\r
7566   HWND hText;\r
7567   int trim, exlen;\r
7568   char *p, *q;\r
7569   char buf[CO_MAX+1];\r
7570   POINT pEnd;\r
7571   RECT rect;\r
7572   static int delayLF = 0;\r
7573   CHARRANGE savesel, sel;\r
7574 \r
7575   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7576   p = data;\r
7577   q = buf;\r
7578   if (delayLF) {\r
7579     *q++ = '\r';\r
7580     *q++ = '\n';\r
7581     delayLF = 0;\r
7582   }\r
7583   while (length--) {\r
7584     if (*p == '\n') {\r
7585       if (*++p) {\r
7586         *q++ = '\r';\r
7587         *q++ = '\n';\r
7588       } else {\r
7589         delayLF = 1;\r
7590       }\r
7591     } else if (*p == '\007') {\r
7592        MyPlaySound(&sounds[(int)SoundBell]);\r
7593        p++;\r
7594     } else {\r
7595       *q++ = *p++;\r
7596     }\r
7597   }\r
7598   *q = NULLCHAR;\r
7599   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7600   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7601   /* Save current selection */\r
7602   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7603   exlen = GetWindowTextLength(hText);\r
7604   /* Find out whether current end of text is visible */\r
7605   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7606   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7607   /* Trim existing text if it's too long */\r
7608   if (exlen + (q - buf) > CO_MAX) {\r
7609     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7610     sel.cpMin = 0;\r
7611     sel.cpMax = trim;\r
7612     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7613     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7614     exlen -= trim;\r
7615     savesel.cpMin -= trim;\r
7616     savesel.cpMax -= trim;\r
7617     if (exlen < 0) exlen = 0;\r
7618     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7619     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7620   }\r
7621   /* Append the new text */\r
7622   sel.cpMin = exlen;\r
7623   sel.cpMax = exlen;\r
7624   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7625   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7626   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7627   if (forceVisible || exlen == 0 ||\r
7628       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7629        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7630     /* Scroll to make new end of text visible if old end of text\r
7631        was visible or new text is an echo of user typein */\r
7632     sel.cpMin = 9999999;\r
7633     sel.cpMax = 9999999;\r
7634     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7635     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7636     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7637     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7638   }\r
7639   if (savesel.cpMax == exlen || forceVisible) {\r
7640     /* Move insert point to new end of text if it was at the old\r
7641        end of text or if the new text is an echo of user typein */\r
7642     sel.cpMin = 9999999;\r
7643     sel.cpMax = 9999999;\r
7644     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7645   } else {\r
7646     /* Restore previous selection */\r
7647     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7648   }\r
7649   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7650 }\r
7651 \r
7652 /*---------*/\r
7653 \r
7654 \r
7655 void\r
7656 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7657 {\r
7658   char buf[100];\r
7659   char *str;\r
7660   COLORREF oldFg, oldBg;\r
7661   HFONT oldFont;\r
7662   RECT rect;\r
7663 \r
7664   if(copyNumber > 1)\r
7665     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7666 \r
7667   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7668   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7669   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7670 \r
7671   rect.left = x;\r
7672   rect.right = x + squareSize;\r
7673   rect.top  = y;\r
7674   rect.bottom = y + squareSize;\r
7675   str = buf;\r
7676 \r
7677   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7678                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7679              y, ETO_CLIPPED|ETO_OPAQUE,\r
7680              &rect, str, strlen(str), NULL);\r
7681 \r
7682   (void) SetTextColor(hdc, oldFg);\r
7683   (void) SetBkColor(hdc, oldBg);\r
7684   (void) SelectObject(hdc, oldFont);\r
7685 }\r
7686 \r
7687 void\r
7688 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7689               RECT *rect, char *color, char *flagFell)\r
7690 {\r
7691   char buf[100];\r
7692   char *str;\r
7693   COLORREF oldFg, oldBg;\r
7694   HFONT oldFont;\r
7695 \r
7696   if (twoBoards && partnerUp) return;\r
7697   if (appData.clockMode) {\r
7698     if (tinyLayout == 2)\r
7699       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7700     else\r
7701       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7702     str = buf;\r
7703   } else {\r
7704     str = color;\r
7705   }\r
7706 \r
7707   if (highlight) {\r
7708     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7709     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7710   } else {\r
7711     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7712     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7713   }\r
7714 \r
7715   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7716 \r
7717   JAWS_SILENCE\r
7718 \r
7719   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7720              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7721              rect, str, strlen(str), NULL);\r
7722   if(logoHeight > 0 && appData.clockMode) {\r
7723       RECT r;\r
7724       str += strlen(color)+2;\r
7725       r.top = rect->top + logoHeight/2;\r
7726       r.left = rect->left;\r
7727       r.right = rect->right;\r
7728       r.bottom = rect->bottom;\r
7729       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7730                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7731                  &r, str, strlen(str), NULL);\r
7732   }\r
7733   (void) SetTextColor(hdc, oldFg);\r
7734   (void) SetBkColor(hdc, oldBg);\r
7735   (void) SelectObject(hdc, oldFont);\r
7736 }\r
7737 \r
7738 \r
7739 int\r
7740 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7741            OVERLAPPED *ovl)\r
7742 {\r
7743   int ok, err;\r
7744 \r
7745   /* [AS]  */\r
7746   if( count <= 0 ) {\r
7747     if (appData.debugMode) {\r
7748       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7749     }\r
7750 \r
7751     return ERROR_INVALID_USER_BUFFER;\r
7752   }\r
7753 \r
7754   ResetEvent(ovl->hEvent);\r
7755   ovl->Offset = ovl->OffsetHigh = 0;\r
7756   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7757   if (ok) {\r
7758     err = NO_ERROR;\r
7759   } else {\r
7760     err = GetLastError();\r
7761     if (err == ERROR_IO_PENDING) {\r
7762       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7763       if (ok)\r
7764         err = NO_ERROR;\r
7765       else\r
7766         err = GetLastError();\r
7767     }\r
7768   }\r
7769   return err;\r
7770 }\r
7771 \r
7772 int\r
7773 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7774             OVERLAPPED *ovl)\r
7775 {\r
7776   int ok, err;\r
7777 \r
7778   ResetEvent(ovl->hEvent);\r
7779   ovl->Offset = ovl->OffsetHigh = 0;\r
7780   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7781   if (ok) {\r
7782     err = NO_ERROR;\r
7783   } else {\r
7784     err = GetLastError();\r
7785     if (err == ERROR_IO_PENDING) {\r
7786       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7787       if (ok)\r
7788         err = NO_ERROR;\r
7789       else\r
7790         err = GetLastError();\r
7791     }\r
7792 \r
7793   }\r
7794   return err;\r
7795 }\r
7796 \r
7797 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7798 void CheckForInputBufferFull( InputSource * is )\r
7799 {\r
7800     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7801         /* Look for end of line */\r
7802         char * p = is->buf;\r
7803         \r
7804         while( p < is->next && *p != '\n' ) {\r
7805             p++;\r
7806         }\r
7807 \r
7808         if( p >= is->next ) {\r
7809             if (appData.debugMode) {\r
7810                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7811             }\r
7812 \r
7813             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7814             is->count = (DWORD) -1;\r
7815             is->next = is->buf;\r
7816         }\r
7817     }\r
7818 }\r
7819 \r
7820 DWORD\r
7821 InputThread(LPVOID arg)\r
7822 {\r
7823   InputSource *is;\r
7824   OVERLAPPED ovl;\r
7825 \r
7826   is = (InputSource *) arg;\r
7827   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7828   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7829   while (is->hThread != NULL) {\r
7830     is->error = DoReadFile(is->hFile, is->next,\r
7831                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7832                            &is->count, &ovl);\r
7833     if (is->error == NO_ERROR) {\r
7834       is->next += is->count;\r
7835     } else {\r
7836       if (is->error == ERROR_BROKEN_PIPE) {\r
7837         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7838         is->count = 0;\r
7839       } else {\r
7840         is->count = (DWORD) -1;\r
7841         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7842         break; \r
7843       }\r
7844     }\r
7845 \r
7846     CheckForInputBufferFull( is );\r
7847 \r
7848     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7849 \r
7850     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7851 \r
7852     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7853   }\r
7854 \r
7855   CloseHandle(ovl.hEvent);\r
7856   CloseHandle(is->hFile);\r
7857 \r
7858   if (appData.debugMode) {\r
7859     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7860   }\r
7861 \r
7862   return 0;\r
7863 }\r
7864 \r
7865 \r
7866 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7867 DWORD\r
7868 NonOvlInputThread(LPVOID arg)\r
7869 {\r
7870   InputSource *is;\r
7871   char *p, *q;\r
7872   int i;\r
7873   char prev;\r
7874 \r
7875   is = (InputSource *) arg;\r
7876   while (is->hThread != NULL) {\r
7877     is->error = ReadFile(is->hFile, is->next,\r
7878                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7879                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7880     if (is->error == NO_ERROR) {\r
7881       /* Change CRLF to LF */\r
7882       if (is->next > is->buf) {\r
7883         p = is->next - 1;\r
7884         i = is->count + 1;\r
7885       } else {\r
7886         p = is->next;\r
7887         i = is->count;\r
7888       }\r
7889       q = p;\r
7890       prev = NULLCHAR;\r
7891       while (i > 0) {\r
7892         if (prev == '\r' && *p == '\n') {\r
7893           *(q-1) = '\n';\r
7894           is->count--;\r
7895         } else { \r
7896           *q++ = *p;\r
7897         }\r
7898         prev = *p++;\r
7899         i--;\r
7900       }\r
7901       *q = NULLCHAR;\r
7902       is->next = q;\r
7903     } else {\r
7904       if (is->error == ERROR_BROKEN_PIPE) {\r
7905         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7906         is->count = 0; \r
7907       } else {\r
7908         is->count = (DWORD) -1;\r
7909       }\r
7910     }\r
7911 \r
7912     CheckForInputBufferFull( is );\r
7913 \r
7914     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7915 \r
7916     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7917 \r
7918     if (is->count < 0) break;  /* Quit on error */\r
7919   }\r
7920   CloseHandle(is->hFile);\r
7921   return 0;\r
7922 }\r
7923 \r
7924 DWORD\r
7925 SocketInputThread(LPVOID arg)\r
7926 {\r
7927   InputSource *is;\r
7928 \r
7929   is = (InputSource *) arg;\r
7930   while (is->hThread != NULL) {\r
7931     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7932     if ((int)is->count == SOCKET_ERROR) {\r
7933       is->count = (DWORD) -1;\r
7934       is->error = WSAGetLastError();\r
7935     } else {\r
7936       is->error = NO_ERROR;\r
7937       is->next += is->count;\r
7938       if (is->count == 0 && is->second == is) {\r
7939         /* End of file on stderr; quit with no message */\r
7940         break;\r
7941       }\r
7942     }\r
7943     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7944 \r
7945     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7946 \r
7947     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7948   }\r
7949   return 0;\r
7950 }\r
7951 \r
7952 VOID\r
7953 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7954 {\r
7955   InputSource *is;\r
7956 \r
7957   is = (InputSource *) lParam;\r
7958   if (is->lineByLine) {\r
7959     /* Feed in lines one by one */\r
7960     char *p = is->buf;\r
7961     char *q = p;\r
7962     while (q < is->next) {\r
7963       if (*q++ == '\n') {\r
7964         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7965         p = q;\r
7966       }\r
7967     }\r
7968     \r
7969     /* Move any partial line to the start of the buffer */\r
7970     q = is->buf;\r
7971     while (p < is->next) {\r
7972       *q++ = *p++;\r
7973     }\r
7974     is->next = q;\r
7975 \r
7976     if (is->error != NO_ERROR || is->count == 0) {\r
7977       /* Notify backend of the error.  Note: If there was a partial\r
7978          line at the end, it is not flushed through. */\r
7979       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7980     }\r
7981   } else {\r
7982     /* Feed in the whole chunk of input at once */\r
7983     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7984     is->next = is->buf;\r
7985   }\r
7986 }\r
7987 \r
7988 /*---------------------------------------------------------------------------*\\r
7989  *\r
7990  *  Menu enables. Used when setting various modes.\r
7991  *\r
7992 \*---------------------------------------------------------------------------*/\r
7993 \r
7994 typedef struct {\r
7995   int item;\r
7996   int flags;\r
7997 } Enables;\r
7998 \r
7999 VOID\r
8000 GreyRevert(Boolean grey)\r
8001 { // [HGM] vari: for retracting variations in local mode\r
8002   HMENU hmenu = GetMenu(hwndMain);\r
8003   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8004   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8005 }\r
8006 \r
8007 VOID\r
8008 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8009 {\r
8010   while (enab->item > 0) {\r
8011     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8012     enab++;\r
8013   }\r
8014 }\r
8015 \r
8016 Enables gnuEnables[] = {\r
8017   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8018   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8019   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8020   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8021   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
8027   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8028   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8030 \r
8031 \r
8032   // Needed to switch from ncp to GNU mode on Engine Load\r
8033   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8034   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8035   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8042   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8043   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8044   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8045   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8046   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8047   { -1, -1 }\r
8048 };\r
8049 \r
8050 Enables icsEnables[] = {\r
8051   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8052   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8053   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8054   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8055   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8056   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8057   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8058   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8059   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8060   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8065   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8066   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8067   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8068   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8069   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8071   { -1, -1 }\r
8072 };\r
8073 \r
8074 #if ZIPPY\r
8075 Enables zippyEnables[] = {\r
8076   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8077   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8078   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8079   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8080   { -1, -1 }\r
8081 };\r
8082 #endif\r
8083 \r
8084 Enables ncpEnables[] = {\r
8085   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8086   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8087   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8088   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8089   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8090   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8091   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8092   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8093   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8094   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8095   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8096   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8104   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8105   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8107   { -1, -1 }\r
8108 };\r
8109 \r
8110 Enables trainingOnEnables[] = {\r
8111   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8112   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8113   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8114   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8115   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8116   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8117   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8118   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8119   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8120   { -1, -1 }\r
8121 };\r
8122 \r
8123 Enables trainingOffEnables[] = {\r
8124   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8125   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8126   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8127   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8128   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8129   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8130   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8131   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8132   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8133   { -1, -1 }\r
8134 };\r
8135 \r
8136 /* These modify either ncpEnables or gnuEnables */\r
8137 Enables cmailEnables[] = {\r
8138   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8139   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8140   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8141   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8142   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8143   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8144   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8145   { -1, -1 }\r
8146 };\r
8147 \r
8148 Enables machineThinkingEnables[] = {\r
8149   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8150   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8151   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8152   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8153   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8154   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8155   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8156   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8157   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8158   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8159   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8160   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8161   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8162 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8163   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8164   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8165   { -1, -1 }\r
8166 };\r
8167 \r
8168 Enables userThinkingEnables[] = {\r
8169   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8170   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8171   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8172   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8173   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8174   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8175   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8176   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8177   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8178   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8179   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8180   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8181   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8182 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8183   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8184   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8185   { -1, -1 }\r
8186 };\r
8187 \r
8188 /*---------------------------------------------------------------------------*\\r
8189  *\r
8190  *  Front-end interface functions exported by XBoard.\r
8191  *  Functions appear in same order as prototypes in frontend.h.\r
8192  * \r
8193 \*---------------------------------------------------------------------------*/\r
8194 VOID\r
8195 CheckMark(UINT item, int state)\r
8196 {\r
8197     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8198 }\r
8199 \r
8200 VOID\r
8201 ModeHighlight()\r
8202 {\r
8203   static UINT prevChecked = 0;\r
8204   static int prevPausing = 0;\r
8205   UINT nowChecked;\r
8206 \r
8207   if (pausing != prevPausing) {\r
8208     prevPausing = pausing;\r
8209     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8210                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8211     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8212   }\r
8213 \r
8214   switch (gameMode) {\r
8215   case BeginningOfGame:\r
8216     if (appData.icsActive)\r
8217       nowChecked = IDM_IcsClient;\r
8218     else if (appData.noChessProgram)\r
8219       nowChecked = IDM_EditGame;\r
8220     else\r
8221       nowChecked = IDM_MachineBlack;\r
8222     break;\r
8223   case MachinePlaysBlack:\r
8224     nowChecked = IDM_MachineBlack;\r
8225     break;\r
8226   case MachinePlaysWhite:\r
8227     nowChecked = IDM_MachineWhite;\r
8228     break;\r
8229   case TwoMachinesPlay:\r
8230     nowChecked = IDM_TwoMachines;\r
8231     break;\r
8232   case AnalyzeMode:\r
8233     nowChecked = IDM_AnalysisMode;\r
8234     break;\r
8235   case AnalyzeFile:\r
8236     nowChecked = IDM_AnalyzeFile;\r
8237     break;\r
8238   case EditGame:\r
8239     nowChecked = IDM_EditGame;\r
8240     break;\r
8241   case PlayFromGameFile:\r
8242     nowChecked = IDM_LoadGame;\r
8243     break;\r
8244   case EditPosition:\r
8245     nowChecked = IDM_EditPosition;\r
8246     break;\r
8247   case Training:\r
8248     nowChecked = IDM_Training;\r
8249     break;\r
8250   case IcsPlayingWhite:\r
8251   case IcsPlayingBlack:\r
8252   case IcsObserving:\r
8253   case IcsIdle:\r
8254     nowChecked = IDM_IcsClient;\r
8255     break;\r
8256   default:\r
8257   case EndOfGame:\r
8258     nowChecked = 0;\r
8259     break;\r
8260   }\r
8261   if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8262     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
8263   CheckMark(prevChecked, MF_UNCHECKED);\r
8264   CheckMark(nowChecked, MF_CHECKED);\r
8265   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8266 \r
8267   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8268     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8269                           MF_BYCOMMAND|MF_ENABLED);\r
8270   } else {\r
8271     (void) EnableMenuItem(GetMenu(hwndMain), \r
8272                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8273   }\r
8274 \r
8275   prevChecked = nowChecked;\r
8276 \r
8277   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8278   if (appData.icsActive) {\r
8279        if (appData.icsEngineAnalyze) {\r
8280                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8281        } else {\r
8282                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8283        }\r
8284   }\r
8285   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8286 }\r
8287 \r
8288 VOID\r
8289 SetICSMode()\r
8290 {\r
8291   HMENU hmenu = GetMenu(hwndMain);\r
8292   SetMenuEnables(hmenu, icsEnables);\r
8293   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8294     MF_BYCOMMAND|MF_ENABLED);\r
8295 #if ZIPPY\r
8296   if (appData.zippyPlay) {\r
8297     SetMenuEnables(hmenu, zippyEnables);\r
8298     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8299          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8300           MF_BYCOMMAND|MF_ENABLED);\r
8301   }\r
8302 #endif\r
8303 }\r
8304 \r
8305 VOID\r
8306 SetGNUMode()\r
8307 {\r
8308   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8309 }\r
8310 \r
8311 VOID\r
8312 SetNCPMode()\r
8313 {\r
8314   HMENU hmenu = GetMenu(hwndMain);\r
8315   SetMenuEnables(hmenu, ncpEnables);\r
8316     DrawMenuBar(hwndMain);\r
8317 }\r
8318 \r
8319 VOID\r
8320 SetCmailMode()\r
8321 {\r
8322   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8323 }\r
8324 \r
8325 VOID \r
8326 SetTrainingModeOn()\r
8327 {\r
8328   int i;\r
8329   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8330   for (i = 0; i < N_BUTTONS; i++) {\r
8331     if (buttonDesc[i].hwnd != NULL)\r
8332       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8333   }\r
8334   CommentPopDown();\r
8335 }\r
8336 \r
8337 VOID SetTrainingModeOff()\r
8338 {\r
8339   int i;\r
8340   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8341   for (i = 0; i < N_BUTTONS; i++) {\r
8342     if (buttonDesc[i].hwnd != NULL)\r
8343       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8344   }\r
8345 }\r
8346 \r
8347 \r
8348 VOID\r
8349 SetUserThinkingEnables()\r
8350 {\r
8351   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8352 }\r
8353 \r
8354 VOID\r
8355 SetMachineThinkingEnables()\r
8356 {\r
8357   HMENU hMenu = GetMenu(hwndMain);\r
8358   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8359 \r
8360   SetMenuEnables(hMenu, machineThinkingEnables);\r
8361 \r
8362   if (gameMode == MachinePlaysBlack) {\r
8363     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8364   } else if (gameMode == MachinePlaysWhite) {\r
8365     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8366   } else if (gameMode == TwoMachinesPlay) {\r
8367     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8368   }\r
8369 }\r
8370 \r
8371 \r
8372 VOID\r
8373 DisplayTitle(char *str)\r
8374 {\r
8375   char title[MSG_SIZ], *host;\r
8376   if (str[0] != NULLCHAR) {\r
8377     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8378   } else if (appData.icsActive) {\r
8379     if (appData.icsCommPort[0] != NULLCHAR)\r
8380       host = "ICS";\r
8381     else \r
8382       host = appData.icsHost;\r
8383       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8384   } else if (appData.noChessProgram) {\r
8385     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8386   } else {\r
8387     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8388     strcat(title, ": ");\r
8389     strcat(title, first.tidy);\r
8390   }\r
8391   SetWindowText(hwndMain, title);\r
8392 }\r
8393 \r
8394 \r
8395 VOID\r
8396 DisplayMessage(char *str1, char *str2)\r
8397 {\r
8398   HDC hdc;\r
8399   HFONT oldFont;\r
8400   int remain = MESSAGE_TEXT_MAX - 1;\r
8401   int len;\r
8402 \r
8403   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8404   messageText[0] = NULLCHAR;\r
8405   if (*str1) {\r
8406     len = strlen(str1);\r
8407     if (len > remain) len = remain;\r
8408     strncpy(messageText, str1, len);\r
8409     messageText[len] = NULLCHAR;\r
8410     remain -= len;\r
8411   }\r
8412   if (*str2 && remain >= 2) {\r
8413     if (*str1) {\r
8414       strcat(messageText, "  ");\r
8415       remain -= 2;\r
8416     }\r
8417     len = strlen(str2);\r
8418     if (len > remain) len = remain;\r
8419     strncat(messageText, str2, len);\r
8420   }\r
8421   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8422   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8423 \r
8424   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8425 \r
8426   SAYMACHINEMOVE();\r
8427 \r
8428   hdc = GetDC(hwndMain);\r
8429   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8430   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8431              &messageRect, messageText, strlen(messageText), NULL);\r
8432   (void) SelectObject(hdc, oldFont);\r
8433   (void) ReleaseDC(hwndMain, hdc);\r
8434 }\r
8435 \r
8436 VOID\r
8437 DisplayError(char *str, int error)\r
8438 {\r
8439   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8440   int len;\r
8441 \r
8442   if (error == 0) {\r
8443     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8444   } else {\r
8445     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8446                         NULL, error, LANG_NEUTRAL,\r
8447                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8448     if (len > 0) {\r
8449       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8450     } else {\r
8451       ErrorMap *em = errmap;\r
8452       while (em->err != 0 && em->err != error) em++;\r
8453       if (em->err != 0) {\r
8454         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8455       } else {\r
8456         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8457       }\r
8458     }\r
8459   }\r
8460   \r
8461   ErrorPopUp(_("Error"), buf);\r
8462 }\r
8463 \r
8464 \r
8465 VOID\r
8466 DisplayMoveError(char *str)\r
8467 {\r
8468   fromX = fromY = -1;\r
8469   ClearHighlights();\r
8470   DrawPosition(FALSE, NULL);\r
8471   if (appData.popupMoveErrors) {\r
8472     ErrorPopUp(_("Error"), str);\r
8473   } else {\r
8474     DisplayMessage(str, "");\r
8475     moveErrorMessageUp = TRUE;\r
8476   }\r
8477 }\r
8478 \r
8479 VOID\r
8480 DisplayFatalError(char *str, int error, int exitStatus)\r
8481 {\r
8482   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8483   int len;\r
8484   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8485 \r
8486   if (error != 0) {\r
8487     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8488                         NULL, error, LANG_NEUTRAL,\r
8489                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8490     if (len > 0) {\r
8491       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8492     } else {\r
8493       ErrorMap *em = errmap;\r
8494       while (em->err != 0 && em->err != error) em++;\r
8495       if (em->err != 0) {\r
8496         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8497       } else {\r
8498         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8499       }\r
8500     }\r
8501     str = buf;\r
8502   }\r
8503   if (appData.debugMode) {\r
8504     fprintf(debugFP, "%s: %s\n", label, str);\r
8505   }\r
8506   if (appData.popupExitMessage) {\r
8507     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8508                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8509   }\r
8510   ExitEvent(exitStatus);\r
8511 }\r
8512 \r
8513 \r
8514 VOID\r
8515 DisplayInformation(char *str)\r
8516 {\r
8517   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8518 }\r
8519 \r
8520 \r
8521 VOID\r
8522 DisplayNote(char *str)\r
8523 {\r
8524   ErrorPopUp(_("Note"), str);\r
8525 }\r
8526 \r
8527 \r
8528 typedef struct {\r
8529   char *title, *question, *replyPrefix;\r
8530   ProcRef pr;\r
8531 } QuestionParams;\r
8532 \r
8533 LRESULT CALLBACK\r
8534 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8535 {\r
8536   static QuestionParams *qp;\r
8537   char reply[MSG_SIZ];\r
8538   int len, err;\r
8539 \r
8540   switch (message) {\r
8541   case WM_INITDIALOG:\r
8542     qp = (QuestionParams *) lParam;\r
8543     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8544     Translate(hDlg, DLG_Question);\r
8545     SetWindowText(hDlg, qp->title);\r
8546     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8547     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8548     return FALSE;\r
8549 \r
8550   case WM_COMMAND:\r
8551     switch (LOWORD(wParam)) {\r
8552     case IDOK:\r
8553       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8554       if (*reply) strcat(reply, " ");\r
8555       len = strlen(reply);\r
8556       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8557       strcat(reply, "\n");\r
8558       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8559       EndDialog(hDlg, TRUE);\r
8560       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8561       return TRUE;\r
8562     case IDCANCEL:\r
8563       EndDialog(hDlg, FALSE);\r
8564       return TRUE;\r
8565     default:\r
8566       break;\r
8567     }\r
8568     break;\r
8569   }\r
8570   return FALSE;\r
8571 }\r
8572 \r
8573 VOID\r
8574 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8575 {\r
8576     QuestionParams qp;\r
8577     FARPROC lpProc;\r
8578     \r
8579     qp.title = title;\r
8580     qp.question = question;\r
8581     qp.replyPrefix = replyPrefix;\r
8582     qp.pr = pr;\r
8583     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8584     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8585       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8586     FreeProcInstance(lpProc);\r
8587 }\r
8588 \r
8589 /* [AS] Pick FRC position */\r
8590 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8591 {\r
8592     static int * lpIndexFRC;\r
8593     BOOL index_is_ok;\r
8594     char buf[16];\r
8595 \r
8596     switch( message )\r
8597     {\r
8598     case WM_INITDIALOG:\r
8599         lpIndexFRC = (int *) lParam;\r
8600 \r
8601         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8602         Translate(hDlg, DLG_NewGameFRC);\r
8603 \r
8604         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8605         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8606         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8607         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8608 \r
8609         break;\r
8610 \r
8611     case WM_COMMAND:\r
8612         switch( LOWORD(wParam) ) {\r
8613         case IDOK:\r
8614             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8615             EndDialog( hDlg, 0 );\r
8616             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8617             return TRUE;\r
8618         case IDCANCEL:\r
8619             EndDialog( hDlg, 1 );   \r
8620             return TRUE;\r
8621         case IDC_NFG_Edit:\r
8622             if( HIWORD(wParam) == EN_CHANGE ) {\r
8623                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8624 \r
8625                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8626             }\r
8627             return TRUE;\r
8628         case IDC_NFG_Random:\r
8629           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8630             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8631             return TRUE;\r
8632         }\r
8633 \r
8634         break;\r
8635     }\r
8636 \r
8637     return FALSE;\r
8638 }\r
8639 \r
8640 int NewGameFRC()\r
8641 {\r
8642     int result;\r
8643     int index = appData.defaultFrcPosition;\r
8644     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8645 \r
8646     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8647 \r
8648     if( result == 0 ) {\r
8649         appData.defaultFrcPosition = index;\r
8650     }\r
8651 \r
8652     return result;\r
8653 }\r
8654 \r
8655 /* [AS] Game list options. Refactored by HGM */\r
8656 \r
8657 HWND gameListOptionsDialog;\r
8658 \r
8659 // low-level front-end: clear text edit / list widget\r
8660 void\r
8661 \r
8662 GLT_ClearList()\r
8663 {\r
8664     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8665 }\r
8666 \r
8667 // low-level front-end: clear text edit / list widget\r
8668 void\r
8669 GLT_DeSelectList()\r
8670 {\r
8671     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8672 }\r
8673 \r
8674 // low-level front-end: append line to text edit / list widget\r
8675 void\r
8676 GLT_AddToList( char *name )\r
8677 {\r
8678     if( name != 0 ) {\r
8679             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8680     }\r
8681 }\r
8682 \r
8683 // low-level front-end: get line from text edit / list widget\r
8684 Boolean\r
8685 GLT_GetFromList( int index, char *name )\r
8686 {\r
8687     if( name != 0 ) {\r
8688             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8689                 return TRUE;\r
8690     }\r
8691     return FALSE;\r
8692 }\r
8693 \r
8694 void GLT_MoveSelection( HWND hDlg, int delta )\r
8695 {\r
8696     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8697     int idx2 = idx1 + delta;\r
8698     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8699 \r
8700     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8701         char buf[128];\r
8702 \r
8703         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8704         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8705         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8706         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8707     }\r
8708 }\r
8709 \r
8710 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8711 {\r
8712     switch( message )\r
8713     {\r
8714     case WM_INITDIALOG:\r
8715         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8716         \r
8717         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8718         Translate(hDlg, DLG_GameListOptions);\r
8719 \r
8720         /* Initialize list */\r
8721         GLT_TagsToList( lpUserGLT );\r
8722 \r
8723         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8724 \r
8725         break;\r
8726 \r
8727     case WM_COMMAND:\r
8728         switch( LOWORD(wParam) ) {\r
8729         case IDOK:\r
8730             GLT_ParseList();\r
8731             EndDialog( hDlg, 0 );\r
8732             return TRUE;\r
8733         case IDCANCEL:\r
8734             EndDialog( hDlg, 1 );\r
8735             return TRUE;\r
8736 \r
8737         case IDC_GLT_Default:\r
8738             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8739             return TRUE;\r
8740 \r
8741         case IDC_GLT_Restore:\r
8742             GLT_TagsToList( appData.gameListTags );\r
8743             return TRUE;\r
8744 \r
8745         case IDC_GLT_Up:\r
8746             GLT_MoveSelection( hDlg, -1 );\r
8747             return TRUE;\r
8748 \r
8749         case IDC_GLT_Down:\r
8750             GLT_MoveSelection( hDlg, +1 );\r
8751             return TRUE;\r
8752         }\r
8753 \r
8754         break;\r
8755     }\r
8756 \r
8757     return FALSE;\r
8758 }\r
8759 \r
8760 int GameListOptions()\r
8761 {\r
8762     int result;\r
8763     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8764 \r
8765       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8766 \r
8767     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8768 \r
8769     if( result == 0 ) {\r
8770         char *oldTags = appData.gameListTags;\r
8771         /* [AS] Memory leak here! */\r
8772         appData.gameListTags = strdup( lpUserGLT ); \r
8773         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8774             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8775     }\r
8776 \r
8777     return result;\r
8778 }\r
8779 \r
8780 VOID\r
8781 DisplayIcsInteractionTitle(char *str)\r
8782 {\r
8783   char consoleTitle[MSG_SIZ];\r
8784 \r
8785     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8786     SetWindowText(hwndConsole, consoleTitle);\r
8787 \r
8788     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8789       char buf[MSG_SIZ], *p = buf, *q;\r
8790         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8791       do {\r
8792         q = strchr(p, ';');\r
8793         if(q) *q++ = 0;\r
8794         if(*p) ChatPopUp(p);\r
8795       } while(p=q);\r
8796     }\r
8797 \r
8798     SetActiveWindow(hwndMain);\r
8799 }\r
8800 \r
8801 void\r
8802 DrawPosition(int fullRedraw, Board board)\r
8803 {\r
8804   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8805 }\r
8806 \r
8807 void NotifyFrontendLogin()\r
8808 {\r
8809         if (hwndConsole)\r
8810                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8811 }\r
8812 \r
8813 VOID\r
8814 ResetFrontEnd()\r
8815 {\r
8816   fromX = fromY = -1;\r
8817   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8818     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8819     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8820     dragInfo.lastpos = dragInfo.pos;\r
8821     dragInfo.start.x = dragInfo.start.y = -1;\r
8822     dragInfo.from = dragInfo.start;\r
8823     ReleaseCapture();\r
8824     DrawPosition(TRUE, NULL);\r
8825   }\r
8826   TagsPopDown();\r
8827 }\r
8828 \r
8829 \r
8830 VOID\r
8831 CommentPopUp(char *title, char *str)\r
8832 {\r
8833   HWND hwnd = GetActiveWindow();\r
8834   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8835   SAY(str);\r
8836   SetActiveWindow(hwnd);\r
8837 }\r
8838 \r
8839 VOID\r
8840 CommentPopDown(void)\r
8841 {\r
8842   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8843   if (commentDialog) {\r
8844     ShowWindow(commentDialog, SW_HIDE);\r
8845   }\r
8846   commentUp = FALSE;\r
8847 }\r
8848 \r
8849 VOID\r
8850 EditCommentPopUp(int index, char *title, char *str)\r
8851 {\r
8852   EitherCommentPopUp(index, title, str, TRUE);\r
8853 }\r
8854 \r
8855 \r
8856 int\r
8857 Roar()\r
8858 {\r
8859   MyPlaySound(&sounds[(int)SoundRoar]);\r
8860   return 1;\r
8861 }\r
8862 \r
8863 VOID\r
8864 RingBell()\r
8865 {\r
8866   MyPlaySound(&sounds[(int)SoundMove]);\r
8867 }\r
8868 \r
8869 VOID PlayIcsWinSound()\r
8870 {\r
8871   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8872 }\r
8873 \r
8874 VOID PlayIcsLossSound()\r
8875 {\r
8876   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8877 }\r
8878 \r
8879 VOID PlayIcsDrawSound()\r
8880 {\r
8881   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8882 }\r
8883 \r
8884 VOID PlayIcsUnfinishedSound()\r
8885 {\r
8886   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8887 }\r
8888 \r
8889 VOID\r
8890 PlayAlarmSound()\r
8891 {\r
8892   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8893 }\r
8894 \r
8895 VOID\r
8896 PlayTellSound()\r
8897 {\r
8898   MyPlaySound(&textAttribs[ColorTell].sound);\r
8899 }\r
8900 \r
8901 \r
8902 VOID\r
8903 EchoOn()\r
8904 {\r
8905   HWND hInput;\r
8906   consoleEcho = TRUE;\r
8907   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8908   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8909   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8910 }\r
8911 \r
8912 \r
8913 VOID\r
8914 EchoOff()\r
8915 {\r
8916   CHARFORMAT cf;\r
8917   HWND hInput;\r
8918   consoleEcho = FALSE;\r
8919   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8920   /* This works OK: set text and background both to the same color */\r
8921   cf = consoleCF;\r
8922   cf.crTextColor = COLOR_ECHOOFF;\r
8923   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8924   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8925 }\r
8926 \r
8927 /* No Raw()...? */\r
8928 \r
8929 void Colorize(ColorClass cc, int continuation)\r
8930 {\r
8931   currentColorClass = cc;\r
8932   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8933   consoleCF.crTextColor = textAttribs[cc].color;\r
8934   consoleCF.dwEffects = textAttribs[cc].effects;\r
8935   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8936 }\r
8937 \r
8938 char *\r
8939 UserName()\r
8940 {\r
8941   static char buf[MSG_SIZ];\r
8942   DWORD bufsiz = MSG_SIZ;\r
8943 \r
8944   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8945         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8946   }\r
8947   if (!GetUserName(buf, &bufsiz)) {\r
8948     /*DisplayError("Error getting user name", GetLastError());*/\r
8949     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8950   }\r
8951   return buf;\r
8952 }\r
8953 \r
8954 char *\r
8955 HostName()\r
8956 {\r
8957   static char buf[MSG_SIZ];\r
8958   DWORD bufsiz = MSG_SIZ;\r
8959 \r
8960   if (!GetComputerName(buf, &bufsiz)) {\r
8961     /*DisplayError("Error getting host name", GetLastError());*/\r
8962     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8963   }\r
8964   return buf;\r
8965 }\r
8966 \r
8967 \r
8968 int\r
8969 ClockTimerRunning()\r
8970 {\r
8971   return clockTimerEvent != 0;\r
8972 }\r
8973 \r
8974 int\r
8975 StopClockTimer()\r
8976 {\r
8977   if (clockTimerEvent == 0) return FALSE;\r
8978   KillTimer(hwndMain, clockTimerEvent);\r
8979   clockTimerEvent = 0;\r
8980   return TRUE;\r
8981 }\r
8982 \r
8983 void\r
8984 StartClockTimer(long millisec)\r
8985 {\r
8986   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8987                              (UINT) millisec, NULL);\r
8988 }\r
8989 \r
8990 void\r
8991 DisplayWhiteClock(long timeRemaining, int highlight)\r
8992 {\r
8993   HDC hdc;\r
8994   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8995 \r
8996   if(appData.noGUI) return;\r
8997   hdc = GetDC(hwndMain);\r
8998   if (!IsIconic(hwndMain)) {\r
8999     DisplayAClock(hdc, timeRemaining, highlight, \r
9000                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
9001   }\r
9002   if (highlight && iconCurrent == iconBlack) {\r
9003     iconCurrent = iconWhite;\r
9004     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9005     if (IsIconic(hwndMain)) {\r
9006       DrawIcon(hdc, 2, 2, iconCurrent);\r
9007     }\r
9008   }\r
9009   (void) ReleaseDC(hwndMain, hdc);\r
9010   if (hwndConsole)\r
9011     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9012 }\r
9013 \r
9014 void\r
9015 DisplayBlackClock(long timeRemaining, int highlight)\r
9016 {\r
9017   HDC hdc;\r
9018   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9019 \r
9020 \r
9021   if(appData.noGUI) return;\r
9022   hdc = GetDC(hwndMain);\r
9023   if (!IsIconic(hwndMain)) {\r
9024     DisplayAClock(hdc, timeRemaining, highlight, \r
9025                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
9026   }\r
9027   if (highlight && iconCurrent == iconWhite) {\r
9028     iconCurrent = iconBlack;\r
9029     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9030     if (IsIconic(hwndMain)) {\r
9031       DrawIcon(hdc, 2, 2, iconCurrent);\r
9032     }\r
9033   }\r
9034   (void) ReleaseDC(hwndMain, hdc);\r
9035   if (hwndConsole)\r
9036     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9037 }\r
9038 \r
9039 \r
9040 int\r
9041 LoadGameTimerRunning()\r
9042 {\r
9043   return loadGameTimerEvent != 0;\r
9044 }\r
9045 \r
9046 int\r
9047 StopLoadGameTimer()\r
9048 {\r
9049   if (loadGameTimerEvent == 0) return FALSE;\r
9050   KillTimer(hwndMain, loadGameTimerEvent);\r
9051   loadGameTimerEvent = 0;\r
9052   return TRUE;\r
9053 }\r
9054 \r
9055 void\r
9056 StartLoadGameTimer(long millisec)\r
9057 {\r
9058   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9059                                 (UINT) millisec, NULL);\r
9060 }\r
9061 \r
9062 void\r
9063 AutoSaveGame()\r
9064 {\r
9065   char *defName;\r
9066   FILE *f;\r
9067   char fileTitle[MSG_SIZ];\r
9068 \r
9069   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9070   f = OpenFileDialog(hwndMain, "a", defName,\r
9071                      appData.oldSaveStyle ? "gam" : "pgn",\r
9072                      GAME_FILT, \r
9073                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9074   if (f != NULL) {\r
9075     SaveGame(f, 0, "");\r
9076     fclose(f);\r
9077   }\r
9078 }\r
9079 \r
9080 \r
9081 void\r
9082 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9083 {\r
9084   if (delayedTimerEvent != 0) {\r
9085     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9086       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9087     }\r
9088     KillTimer(hwndMain, delayedTimerEvent);\r
9089     delayedTimerEvent = 0;\r
9090     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9091     delayedTimerCallback();\r
9092   }\r
9093   delayedTimerCallback = cb;\r
9094   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9095                                 (UINT) millisec, NULL);\r
9096 }\r
9097 \r
9098 DelayedEventCallback\r
9099 GetDelayedEvent()\r
9100 {\r
9101   if (delayedTimerEvent) {\r
9102     return delayedTimerCallback;\r
9103   } else {\r
9104     return NULL;\r
9105   }\r
9106 }\r
9107 \r
9108 void\r
9109 CancelDelayedEvent()\r
9110 {\r
9111   if (delayedTimerEvent) {\r
9112     KillTimer(hwndMain, delayedTimerEvent);\r
9113     delayedTimerEvent = 0;\r
9114   }\r
9115 }\r
9116 \r
9117 DWORD GetWin32Priority(int nice)\r
9118 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9119 /*\r
9120 REALTIME_PRIORITY_CLASS     0x00000100\r
9121 HIGH_PRIORITY_CLASS         0x00000080\r
9122 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9123 NORMAL_PRIORITY_CLASS       0x00000020\r
9124 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9125 IDLE_PRIORITY_CLASS         0x00000040\r
9126 */\r
9127         if (nice < -15) return 0x00000080;\r
9128         if (nice < 0)   return 0x00008000;\r
9129         if (nice == 0)  return 0x00000020;\r
9130         if (nice < 15)  return 0x00004000;\r
9131         return 0x00000040;\r
9132 }\r
9133 \r
9134 void RunCommand(char *cmdLine)\r
9135 {\r
9136   /* Now create the child process. */\r
9137   STARTUPINFO siStartInfo;\r
9138   PROCESS_INFORMATION piProcInfo;\r
9139 \r
9140   siStartInfo.cb = sizeof(STARTUPINFO);\r
9141   siStartInfo.lpReserved = NULL;\r
9142   siStartInfo.lpDesktop = NULL;\r
9143   siStartInfo.lpTitle = NULL;\r
9144   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9145   siStartInfo.cbReserved2 = 0;\r
9146   siStartInfo.lpReserved2 = NULL;\r
9147   siStartInfo.hStdInput = NULL;\r
9148   siStartInfo.hStdOutput = NULL;\r
9149   siStartInfo.hStdError = NULL;\r
9150 \r
9151   CreateProcess(NULL,\r
9152                 cmdLine,           /* command line */\r
9153                 NULL,      /* process security attributes */\r
9154                 NULL,      /* primary thread security attrs */\r
9155                 TRUE,      /* handles are inherited */\r
9156                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9157                 NULL,      /* use parent's environment */\r
9158                 NULL,\r
9159                 &siStartInfo, /* STARTUPINFO pointer */\r
9160                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9161 \r
9162   CloseHandle(piProcInfo.hThread);\r
9163 }\r
9164 \r
9165 /* Start a child process running the given program.\r
9166    The process's standard output can be read from "from", and its\r
9167    standard input can be written to "to".\r
9168    Exit with fatal error if anything goes wrong.\r
9169    Returns an opaque pointer that can be used to destroy the process\r
9170    later.\r
9171 */\r
9172 int\r
9173 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9174 {\r
9175 #define BUFSIZE 4096\r
9176 \r
9177   HANDLE hChildStdinRd, hChildStdinWr,\r
9178     hChildStdoutRd, hChildStdoutWr;\r
9179   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9180   SECURITY_ATTRIBUTES saAttr;\r
9181   BOOL fSuccess;\r
9182   PROCESS_INFORMATION piProcInfo;\r
9183   STARTUPINFO siStartInfo;\r
9184   ChildProc *cp;\r
9185   char buf[MSG_SIZ];\r
9186   DWORD err;\r
9187 \r
9188   if (appData.debugMode) {\r
9189     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9190   }\r
9191 \r
9192   *pr = NoProc;\r
9193 \r
9194   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9195   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9196   saAttr.bInheritHandle = TRUE;\r
9197   saAttr.lpSecurityDescriptor = NULL;\r
9198 \r
9199   /*\r
9200    * The steps for redirecting child's STDOUT:\r
9201    *     1. Create anonymous pipe to be STDOUT for child.\r
9202    *     2. Create a noninheritable duplicate of read handle,\r
9203    *         and close the inheritable read handle.\r
9204    */\r
9205 \r
9206   /* Create a pipe for the child's STDOUT. */\r
9207   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9208     return GetLastError();\r
9209   }\r
9210 \r
9211   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9212   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9213                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9214                              FALSE,     /* not inherited */\r
9215                              DUPLICATE_SAME_ACCESS);\r
9216   if (! fSuccess) {\r
9217     return GetLastError();\r
9218   }\r
9219   CloseHandle(hChildStdoutRd);\r
9220 \r
9221   /*\r
9222    * The steps for redirecting child's STDIN:\r
9223    *     1. Create anonymous pipe to be STDIN for child.\r
9224    *     2. Create a noninheritable duplicate of write handle,\r
9225    *         and close the inheritable write handle.\r
9226    */\r
9227 \r
9228   /* Create a pipe for the child's STDIN. */\r
9229   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9230     return GetLastError();\r
9231   }\r
9232 \r
9233   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9234   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9235                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9236                              FALSE,     /* not inherited */\r
9237                              DUPLICATE_SAME_ACCESS);\r
9238   if (! fSuccess) {\r
9239     return GetLastError();\r
9240   }\r
9241   CloseHandle(hChildStdinWr);\r
9242 \r
9243   /* Arrange to (1) look in dir for the child .exe file, and\r
9244    * (2) have dir be the child's working directory.  Interpret\r
9245    * dir relative to the directory WinBoard loaded from. */\r
9246   GetCurrentDirectory(MSG_SIZ, buf);\r
9247   SetCurrentDirectory(installDir);\r
9248   SetCurrentDirectory(dir);\r
9249 \r
9250   /* Now create the child process. */\r
9251 \r
9252   siStartInfo.cb = sizeof(STARTUPINFO);\r
9253   siStartInfo.lpReserved = NULL;\r
9254   siStartInfo.lpDesktop = NULL;\r
9255   siStartInfo.lpTitle = NULL;\r
9256   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9257   siStartInfo.cbReserved2 = 0;\r
9258   siStartInfo.lpReserved2 = NULL;\r
9259   siStartInfo.hStdInput = hChildStdinRd;\r
9260   siStartInfo.hStdOutput = hChildStdoutWr;\r
9261   siStartInfo.hStdError = hChildStdoutWr;\r
9262 \r
9263   fSuccess = CreateProcess(NULL,\r
9264                            cmdLine,        /* command line */\r
9265                            NULL,           /* process security attributes */\r
9266                            NULL,           /* primary thread security attrs */\r
9267                            TRUE,           /* handles are inherited */\r
9268                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9269                            NULL,           /* use parent's environment */\r
9270                            NULL,\r
9271                            &siStartInfo, /* STARTUPINFO pointer */\r
9272                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9273 \r
9274   err = GetLastError();\r
9275   SetCurrentDirectory(buf); /* return to prev directory */\r
9276   if (! fSuccess) {\r
9277     return err;\r
9278   }\r
9279 \r
9280   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9281     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9282     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9283   }\r
9284 \r
9285   /* Close the handles we don't need in the parent */\r
9286   CloseHandle(piProcInfo.hThread);\r
9287   CloseHandle(hChildStdinRd);\r
9288   CloseHandle(hChildStdoutWr);\r
9289 \r
9290   /* Prepare return value */\r
9291   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9292   cp->kind = CPReal;\r
9293   cp->hProcess = piProcInfo.hProcess;\r
9294   cp->pid = piProcInfo.dwProcessId;\r
9295   cp->hFrom = hChildStdoutRdDup;\r
9296   cp->hTo = hChildStdinWrDup;\r
9297 \r
9298   *pr = (void *) cp;\r
9299 \r
9300   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9301      2000 where engines sometimes don't see the initial command(s)\r
9302      from WinBoard and hang.  I don't understand how that can happen,\r
9303      but the Sleep is harmless, so I've put it in.  Others have also\r
9304      reported what may be the same problem, so hopefully this will fix\r
9305      it for them too.  */\r
9306   Sleep(500);\r
9307 \r
9308   return NO_ERROR;\r
9309 }\r
9310 \r
9311 \r
9312 void\r
9313 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9314 {\r
9315   ChildProc *cp; int result;\r
9316 \r
9317   cp = (ChildProc *) pr;\r
9318   if (cp == NULL) return;\r
9319 \r
9320   switch (cp->kind) {\r
9321   case CPReal:\r
9322     /* TerminateProcess is considered harmful, so... */\r
9323     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9324     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9325     /* The following doesn't work because the chess program\r
9326        doesn't "have the same console" as WinBoard.  Maybe\r
9327        we could arrange for this even though neither WinBoard\r
9328        nor the chess program uses a console for stdio? */\r
9329     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9330 \r
9331     /* [AS] Special termination modes for misbehaving programs... */\r
9332     if( signal & 8 ) { \r
9333         result = TerminateProcess( cp->hProcess, 0 );\r
9334 \r
9335         if ( appData.debugMode) {\r
9336             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9337         }\r
9338     }\r
9339     else if( signal & 4 ) {\r
9340         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9341 \r
9342         if( dw != WAIT_OBJECT_0 ) {\r
9343             result = TerminateProcess( cp->hProcess, 0 );\r
9344 \r
9345             if ( appData.debugMode) {\r
9346                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9347             }\r
9348 \r
9349         }\r
9350     }\r
9351 \r
9352     CloseHandle(cp->hProcess);\r
9353     break;\r
9354 \r
9355   case CPComm:\r
9356     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9357     break;\r
9358 \r
9359   case CPSock:\r
9360     closesocket(cp->sock);\r
9361     WSACleanup();\r
9362     break;\r
9363 \r
9364   case CPRcmd:\r
9365     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9366     closesocket(cp->sock);\r
9367     closesocket(cp->sock2);\r
9368     WSACleanup();\r
9369     break;\r
9370   }\r
9371   free(cp);\r
9372 }\r
9373 \r
9374 void\r
9375 InterruptChildProcess(ProcRef pr)\r
9376 {\r
9377   ChildProc *cp;\r
9378 \r
9379   cp = (ChildProc *) pr;\r
9380   if (cp == NULL) return;\r
9381   switch (cp->kind) {\r
9382   case CPReal:\r
9383     /* The following doesn't work because the chess program\r
9384        doesn't "have the same console" as WinBoard.  Maybe\r
9385        we could arrange for this even though neither WinBoard\r
9386        nor the chess program uses a console for stdio */\r
9387     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9388     break;\r
9389 \r
9390   case CPComm:\r
9391   case CPSock:\r
9392     /* Can't interrupt */\r
9393     break;\r
9394 \r
9395   case CPRcmd:\r
9396     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9397     break;\r
9398   }\r
9399 }\r
9400 \r
9401 \r
9402 int\r
9403 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9404 {\r
9405   char cmdLine[MSG_SIZ];\r
9406 \r
9407   if (port[0] == NULLCHAR) {\r
9408     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9409   } else {\r
9410     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9411   }\r
9412   return StartChildProcess(cmdLine, "", pr);\r
9413 }\r
9414 \r
9415 \r
9416 /* Code to open TCP sockets */\r
9417 \r
9418 int\r
9419 OpenTCP(char *host, char *port, ProcRef *pr)\r
9420 {\r
9421   ChildProc *cp;\r
9422   int err;\r
9423   SOCKET s;\r
9424 \r
9425   struct sockaddr_in sa, mysa;\r
9426   struct hostent FAR *hp;\r
9427   unsigned short uport;\r
9428   WORD wVersionRequested;\r
9429   WSADATA wsaData;\r
9430 \r
9431   /* Initialize socket DLL */\r
9432   wVersionRequested = MAKEWORD(1, 1);\r
9433   err = WSAStartup(wVersionRequested, &wsaData);\r
9434   if (err != 0) return err;\r
9435 \r
9436   /* Make socket */\r
9437   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9438     err = WSAGetLastError();\r
9439     WSACleanup();\r
9440     return err;\r
9441   }\r
9442 \r
9443   /* Bind local address using (mostly) don't-care values.\r
9444    */\r
9445   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9446   mysa.sin_family = AF_INET;\r
9447   mysa.sin_addr.s_addr = INADDR_ANY;\r
9448   uport = (unsigned short) 0;\r
9449   mysa.sin_port = htons(uport);\r
9450   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9451       == SOCKET_ERROR) {\r
9452     err = WSAGetLastError();\r
9453     WSACleanup();\r
9454     return err;\r
9455   }\r
9456 \r
9457   /* Resolve remote host name */\r
9458   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9459   if (!(hp = gethostbyname(host))) {\r
9460     unsigned int b0, b1, b2, b3;\r
9461 \r
9462     err = WSAGetLastError();\r
9463 \r
9464     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9465       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9466       hp->h_addrtype = AF_INET;\r
9467       hp->h_length = 4;\r
9468       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9469       hp->h_addr_list[0] = (char *) malloc(4);\r
9470       hp->h_addr_list[0][0] = (char) b0;\r
9471       hp->h_addr_list[0][1] = (char) b1;\r
9472       hp->h_addr_list[0][2] = (char) b2;\r
9473       hp->h_addr_list[0][3] = (char) b3;\r
9474     } else {\r
9475       WSACleanup();\r
9476       return err;\r
9477     }\r
9478   }\r
9479   sa.sin_family = hp->h_addrtype;\r
9480   uport = (unsigned short) atoi(port);\r
9481   sa.sin_port = htons(uport);\r
9482   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9483 \r
9484   /* Make connection */\r
9485   if (connect(s, (struct sockaddr *) &sa,\r
9486               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9487     err = WSAGetLastError();\r
9488     WSACleanup();\r
9489     return err;\r
9490   }\r
9491 \r
9492   /* Prepare return value */\r
9493   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9494   cp->kind = CPSock;\r
9495   cp->sock = s;\r
9496   *pr = (ProcRef *) cp;\r
9497 \r
9498   return NO_ERROR;\r
9499 }\r
9500 \r
9501 int\r
9502 OpenCommPort(char *name, ProcRef *pr)\r
9503 {\r
9504   HANDLE h;\r
9505   COMMTIMEOUTS ct;\r
9506   ChildProc *cp;\r
9507   char fullname[MSG_SIZ];\r
9508 \r
9509   if (*name != '\\')\r
9510     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9511   else\r
9512     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9513 \r
9514   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9515                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9516   if (h == (HANDLE) -1) {\r
9517     return GetLastError();\r
9518   }\r
9519   hCommPort = h;\r
9520 \r
9521   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9522 \r
9523   /* Accumulate characters until a 100ms pause, then parse */\r
9524   ct.ReadIntervalTimeout = 100;\r
9525   ct.ReadTotalTimeoutMultiplier = 0;\r
9526   ct.ReadTotalTimeoutConstant = 0;\r
9527   ct.WriteTotalTimeoutMultiplier = 0;\r
9528   ct.WriteTotalTimeoutConstant = 0;\r
9529   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9530 \r
9531   /* Prepare return value */\r
9532   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9533   cp->kind = CPComm;\r
9534   cp->hFrom = h;\r
9535   cp->hTo = h;\r
9536   *pr = (ProcRef *) cp;\r
9537 \r
9538   return NO_ERROR;\r
9539 }\r
9540 \r
9541 int\r
9542 OpenLoopback(ProcRef *pr)\r
9543 {\r
9544   DisplayFatalError(_("Not implemented"), 0, 1);\r
9545   return NO_ERROR;\r
9546 }\r
9547 \r
9548 \r
9549 int\r
9550 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9551 {\r
9552   ChildProc *cp;\r
9553   int err;\r
9554   SOCKET s, s2, s3;\r
9555   struct sockaddr_in sa, mysa;\r
9556   struct hostent FAR *hp;\r
9557   unsigned short uport;\r
9558   WORD wVersionRequested;\r
9559   WSADATA wsaData;\r
9560   int fromPort;\r
9561   char stderrPortStr[MSG_SIZ];\r
9562 \r
9563   /* Initialize socket DLL */\r
9564   wVersionRequested = MAKEWORD(1, 1);\r
9565   err = WSAStartup(wVersionRequested, &wsaData);\r
9566   if (err != 0) return err;\r
9567 \r
9568   /* Resolve remote host name */\r
9569   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9570   if (!(hp = gethostbyname(host))) {\r
9571     unsigned int b0, b1, b2, b3;\r
9572 \r
9573     err = WSAGetLastError();\r
9574 \r
9575     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9576       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9577       hp->h_addrtype = AF_INET;\r
9578       hp->h_length = 4;\r
9579       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9580       hp->h_addr_list[0] = (char *) malloc(4);\r
9581       hp->h_addr_list[0][0] = (char) b0;\r
9582       hp->h_addr_list[0][1] = (char) b1;\r
9583       hp->h_addr_list[0][2] = (char) b2;\r
9584       hp->h_addr_list[0][3] = (char) b3;\r
9585     } else {\r
9586       WSACleanup();\r
9587       return err;\r
9588     }\r
9589   }\r
9590   sa.sin_family = hp->h_addrtype;\r
9591   uport = (unsigned short) 514;\r
9592   sa.sin_port = htons(uport);\r
9593   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9594 \r
9595   /* Bind local socket to unused "privileged" port address\r
9596    */\r
9597   s = INVALID_SOCKET;\r
9598   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9599   mysa.sin_family = AF_INET;\r
9600   mysa.sin_addr.s_addr = INADDR_ANY;\r
9601   for (fromPort = 1023;; fromPort--) {\r
9602     if (fromPort < 0) {\r
9603       WSACleanup();\r
9604       return WSAEADDRINUSE;\r
9605     }\r
9606     if (s == INVALID_SOCKET) {\r
9607       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9608         err = WSAGetLastError();\r
9609         WSACleanup();\r
9610         return err;\r
9611       }\r
9612     }\r
9613     uport = (unsigned short) fromPort;\r
9614     mysa.sin_port = htons(uport);\r
9615     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9616         == SOCKET_ERROR) {\r
9617       err = WSAGetLastError();\r
9618       if (err == WSAEADDRINUSE) continue;\r
9619       WSACleanup();\r
9620       return err;\r
9621     }\r
9622     if (connect(s, (struct sockaddr *) &sa,\r
9623       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9624       err = WSAGetLastError();\r
9625       if (err == WSAEADDRINUSE) {\r
9626         closesocket(s);\r
9627         s = -1;\r
9628         continue;\r
9629       }\r
9630       WSACleanup();\r
9631       return err;\r
9632     }\r
9633     break;\r
9634   }\r
9635 \r
9636   /* Bind stderr local socket to unused "privileged" port address\r
9637    */\r
9638   s2 = INVALID_SOCKET;\r
9639   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9640   mysa.sin_family = AF_INET;\r
9641   mysa.sin_addr.s_addr = INADDR_ANY;\r
9642   for (fromPort = 1023;; fromPort--) {\r
9643     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9644     if (fromPort < 0) {\r
9645       (void) closesocket(s);\r
9646       WSACleanup();\r
9647       return WSAEADDRINUSE;\r
9648     }\r
9649     if (s2 == INVALID_SOCKET) {\r
9650       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9651         err = WSAGetLastError();\r
9652         closesocket(s);\r
9653         WSACleanup();\r
9654         return err;\r
9655       }\r
9656     }\r
9657     uport = (unsigned short) fromPort;\r
9658     mysa.sin_port = htons(uport);\r
9659     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9660         == SOCKET_ERROR) {\r
9661       err = WSAGetLastError();\r
9662       if (err == WSAEADDRINUSE) continue;\r
9663       (void) closesocket(s);\r
9664       WSACleanup();\r
9665       return err;\r
9666     }\r
9667     if (listen(s2, 1) == SOCKET_ERROR) {\r
9668       err = WSAGetLastError();\r
9669       if (err == WSAEADDRINUSE) {\r
9670         closesocket(s2);\r
9671         s2 = INVALID_SOCKET;\r
9672         continue;\r
9673       }\r
9674       (void) closesocket(s);\r
9675       (void) closesocket(s2);\r
9676       WSACleanup();\r
9677       return err;\r
9678     }\r
9679     break;\r
9680   }\r
9681   prevStderrPort = fromPort; // remember port used\r
9682   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9683 \r
9684   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9685     err = WSAGetLastError();\r
9686     (void) closesocket(s);\r
9687     (void) closesocket(s2);\r
9688     WSACleanup();\r
9689     return err;\r
9690   }\r
9691 \r
9692   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9693     err = WSAGetLastError();\r
9694     (void) closesocket(s);\r
9695     (void) closesocket(s2);\r
9696     WSACleanup();\r
9697     return err;\r
9698   }\r
9699   if (*user == NULLCHAR) user = UserName();\r
9700   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9701     err = WSAGetLastError();\r
9702     (void) closesocket(s);\r
9703     (void) closesocket(s2);\r
9704     WSACleanup();\r
9705     return err;\r
9706   }\r
9707   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9708     err = WSAGetLastError();\r
9709     (void) closesocket(s);\r
9710     (void) closesocket(s2);\r
9711     WSACleanup();\r
9712     return err;\r
9713   }\r
9714 \r
9715   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9716     err = WSAGetLastError();\r
9717     (void) closesocket(s);\r
9718     (void) closesocket(s2);\r
9719     WSACleanup();\r
9720     return err;\r
9721   }\r
9722   (void) closesocket(s2);  /* Stop listening */\r
9723 \r
9724   /* Prepare return value */\r
9725   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9726   cp->kind = CPRcmd;\r
9727   cp->sock = s;\r
9728   cp->sock2 = s3;\r
9729   *pr = (ProcRef *) cp;\r
9730 \r
9731   return NO_ERROR;\r
9732 }\r
9733 \r
9734 \r
9735 InputSourceRef\r
9736 AddInputSource(ProcRef pr, int lineByLine,\r
9737                InputCallback func, VOIDSTAR closure)\r
9738 {\r
9739   InputSource *is, *is2 = NULL;\r
9740   ChildProc *cp = (ChildProc *) pr;\r
9741 \r
9742   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9743   is->lineByLine = lineByLine;\r
9744   is->func = func;\r
9745   is->closure = closure;\r
9746   is->second = NULL;\r
9747   is->next = is->buf;\r
9748   if (pr == NoProc) {\r
9749     is->kind = CPReal;\r
9750     consoleInputSource = is;\r
9751   } else {\r
9752     is->kind = cp->kind;\r
9753     /* \r
9754         [AS] Try to avoid a race condition if the thread is given control too early:\r
9755         we create all threads suspended so that the is->hThread variable can be\r
9756         safely assigned, then let the threads start with ResumeThread.\r
9757     */\r
9758     switch (cp->kind) {\r
9759     case CPReal:\r
9760       is->hFile = cp->hFrom;\r
9761       cp->hFrom = NULL; /* now owned by InputThread */\r
9762       is->hThread =\r
9763         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9764                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9765       break;\r
9766 \r
9767     case CPComm:\r
9768       is->hFile = cp->hFrom;\r
9769       cp->hFrom = NULL; /* now owned by InputThread */\r
9770       is->hThread =\r
9771         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9772                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9773       break;\r
9774 \r
9775     case CPSock:\r
9776       is->sock = cp->sock;\r
9777       is->hThread =\r
9778         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9779                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9780       break;\r
9781 \r
9782     case CPRcmd:\r
9783       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9784       *is2 = *is;\r
9785       is->sock = cp->sock;\r
9786       is->second = is2;\r
9787       is2->sock = cp->sock2;\r
9788       is2->second = is2;\r
9789       is->hThread =\r
9790         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9791                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9792       is2->hThread =\r
9793         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9794                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9795       break;\r
9796     }\r
9797 \r
9798     if( is->hThread != NULL ) {\r
9799         ResumeThread( is->hThread );\r
9800     }\r
9801 \r
9802     if( is2 != NULL && is2->hThread != NULL ) {\r
9803         ResumeThread( is2->hThread );\r
9804     }\r
9805   }\r
9806 \r
9807   return (InputSourceRef) is;\r
9808 }\r
9809 \r
9810 void\r
9811 RemoveInputSource(InputSourceRef isr)\r
9812 {\r
9813   InputSource *is;\r
9814 \r
9815   is = (InputSource *) isr;\r
9816   is->hThread = NULL;  /* tell thread to stop */\r
9817   CloseHandle(is->hThread);\r
9818   if (is->second != NULL) {\r
9819     is->second->hThread = NULL;\r
9820     CloseHandle(is->second->hThread);\r
9821   }\r
9822 }\r
9823 \r
9824 int no_wrap(char *message, int count)\r
9825 {\r
9826     ConsoleOutput(message, count, FALSE);\r
9827     return count;\r
9828 }\r
9829 \r
9830 int\r
9831 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9832 {\r
9833   DWORD dOutCount;\r
9834   int outCount = SOCKET_ERROR;\r
9835   ChildProc *cp = (ChildProc *) pr;\r
9836   static OVERLAPPED ovl;\r
9837 \r
9838   static int line = 0;\r
9839 \r
9840   if (pr == NoProc)\r
9841   {\r
9842     if (appData.noJoin || !appData.useInternalWrap)\r
9843       return no_wrap(message, count);\r
9844     else\r
9845     {\r
9846       int width = get_term_width();\r
9847       int len = wrap(NULL, message, count, width, &line);\r
9848       char *msg = malloc(len);\r
9849       int dbgchk;\r
9850 \r
9851       if (!msg)\r
9852         return no_wrap(message, count);\r
9853       else\r
9854       {\r
9855         dbgchk = wrap(msg, message, count, width, &line);\r
9856         if (dbgchk != len && appData.debugMode)\r
9857             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9858         ConsoleOutput(msg, len, FALSE);\r
9859         free(msg);\r
9860         return len;\r
9861       }\r
9862     }\r
9863   }\r
9864 \r
9865   if (ovl.hEvent == NULL) {\r
9866     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9867   }\r
9868   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9869 \r
9870   switch (cp->kind) {\r
9871   case CPSock:\r
9872   case CPRcmd:\r
9873     outCount = send(cp->sock, message, count, 0);\r
9874     if (outCount == SOCKET_ERROR) {\r
9875       *outError = WSAGetLastError();\r
9876     } else {\r
9877       *outError = NO_ERROR;\r
9878     }\r
9879     break;\r
9880 \r
9881   case CPReal:\r
9882     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9883                   &dOutCount, NULL)) {\r
9884       *outError = NO_ERROR;\r
9885       outCount = (int) dOutCount;\r
9886     } else {\r
9887       *outError = GetLastError();\r
9888     }\r
9889     break;\r
9890 \r
9891   case CPComm:\r
9892     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9893                             &dOutCount, &ovl);\r
9894     if (*outError == NO_ERROR) {\r
9895       outCount = (int) dOutCount;\r
9896     }\r
9897     break;\r
9898   }\r
9899   return outCount;\r
9900 }\r
9901 \r
9902 void\r
9903 DoSleep(int n)\r
9904 {\r
9905     if(n != 0) Sleep(n);\r
9906 }\r
9907 \r
9908 int\r
9909 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9910                        long msdelay)\r
9911 {\r
9912   /* Ignore delay, not implemented for WinBoard */\r
9913   return OutputToProcess(pr, message, count, outError);\r
9914 }\r
9915 \r
9916 \r
9917 void\r
9918 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9919                         char *buf, int count, int error)\r
9920 {\r
9921   DisplayFatalError(_("Not implemented"), 0, 1);\r
9922 }\r
9923 \r
9924 /* see wgamelist.c for Game List functions */\r
9925 /* see wedittags.c for Edit Tags functions */\r
9926 \r
9927 \r
9928 int\r
9929 ICSInitScript()\r
9930 {\r
9931   FILE *f;\r
9932   char buf[MSG_SIZ];\r
9933   char *dummy;\r
9934 \r
9935   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9936     f = fopen(buf, "r");\r
9937     if (f != NULL) {\r
9938       ProcessICSInitScript(f);\r
9939       fclose(f);\r
9940       return TRUE;\r
9941     }\r
9942   }\r
9943   return FALSE;\r
9944 }\r
9945 \r
9946 \r
9947 VOID\r
9948 StartAnalysisClock()\r
9949 {\r
9950   if (analysisTimerEvent) return;\r
9951   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9952                                         (UINT) 2000, NULL);\r
9953 }\r
9954 \r
9955 VOID\r
9956 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9957 {\r
9958   highlightInfo.sq[0].x = fromX;\r
9959   highlightInfo.sq[0].y = fromY;\r
9960   highlightInfo.sq[1].x = toX;\r
9961   highlightInfo.sq[1].y = toY;\r
9962 }\r
9963 \r
9964 VOID\r
9965 ClearHighlights()\r
9966 {\r
9967   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9968     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9969 }\r
9970 \r
9971 VOID\r
9972 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9973 {\r
9974   premoveHighlightInfo.sq[0].x = fromX;\r
9975   premoveHighlightInfo.sq[0].y = fromY;\r
9976   premoveHighlightInfo.sq[1].x = toX;\r
9977   premoveHighlightInfo.sq[1].y = toY;\r
9978 }\r
9979 \r
9980 VOID\r
9981 ClearPremoveHighlights()\r
9982 {\r
9983   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9984     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9985 }\r
9986 \r
9987 VOID\r
9988 ShutDownFrontEnd()\r
9989 {\r
9990   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9991   DeleteClipboardTempFiles();\r
9992 }\r
9993 \r
9994 void\r
9995 BoardToTop()\r
9996 {\r
9997     if (IsIconic(hwndMain))\r
9998       ShowWindow(hwndMain, SW_RESTORE);\r
9999 \r
10000     SetActiveWindow(hwndMain);\r
10001 }\r
10002 \r
10003 /*\r
10004  * Prototypes for animation support routines\r
10005  */\r
10006 static void ScreenSquare(int column, int row, POINT * pt);\r
10007 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10008      POINT frames[], int * nFrames);\r
10009 \r
10010 \r
10011 #define kFactor 4\r
10012 \r
10013 void\r
10014 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
10015 {       // [HGM] atomic: animate blast wave\r
10016         int i;\r
10017 \r
10018         explodeInfo.fromX = fromX;\r
10019         explodeInfo.fromY = fromY;\r
10020         explodeInfo.toX = toX;\r
10021         explodeInfo.toY = toY;\r
10022         for(i=1; i<4*kFactor; i++) {\r
10023             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
10024             DrawPosition(FALSE, board);\r
10025             Sleep(appData.animSpeed);\r
10026         }\r
10027         explodeInfo.radius = 0;\r
10028         DrawPosition(TRUE, board);\r
10029 }\r
10030 \r
10031 void\r
10032 AnimateMove(board, fromX, fromY, toX, toY)\r
10033      Board board;\r
10034      int fromX;\r
10035      int fromY;\r
10036      int toX;\r
10037      int toY;\r
10038 {\r
10039   ChessSquare piece;\r
10040   int x = toX, y = toY;\r
10041   POINT start, finish, mid;\r
10042   POINT frames[kFactor * 2 + 1];\r
10043   int nFrames, n;\r
10044 \r
10045   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10046 \r
10047   if (!appData.animate) return;\r
10048   if (doingSizing) return;\r
10049   if (fromY < 0 || fromX < 0) return;\r
10050   piece = board[fromY][fromX];\r
10051   if (piece >= EmptySquare) return;\r
10052 \r
10053   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
10054 \r
10055 again:\r
10056 \r
10057   ScreenSquare(fromX, fromY, &start);\r
10058   ScreenSquare(toX, toY, &finish);\r
10059 \r
10060   /* All moves except knight jumps move in straight line */\r
10061   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10062     mid.x = start.x + (finish.x - start.x) / 2;\r
10063     mid.y = start.y + (finish.y - start.y) / 2;\r
10064   } else {\r
10065     /* Knight: make straight movement then diagonal */\r
10066     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10067        mid.x = start.x + (finish.x - start.x) / 2;\r
10068        mid.y = start.y;\r
10069      } else {\r
10070        mid.x = start.x;\r
10071        mid.y = start.y + (finish.y - start.y) / 2;\r
10072      }\r
10073   }\r
10074   \r
10075   /* Don't use as many frames for very short moves */\r
10076   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10077     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10078   else\r
10079     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10080 \r
10081   animInfo.from.x = fromX;\r
10082   animInfo.from.y = fromY;\r
10083   animInfo.to.x = toX;\r
10084   animInfo.to.y = toY;\r
10085   animInfo.lastpos = start;\r
10086   animInfo.piece = piece;\r
10087   for (n = 0; n < nFrames; n++) {\r
10088     animInfo.pos = frames[n];\r
10089     DrawPosition(FALSE, NULL);\r
10090     animInfo.lastpos = animInfo.pos;\r
10091     Sleep(appData.animSpeed);\r
10092   }\r
10093   animInfo.pos = finish;\r
10094   DrawPosition(FALSE, NULL);\r
10095 \r
10096   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10097 \r
10098   animInfo.piece = EmptySquare;\r
10099   Explode(board, fromX, fromY, toX, toY);\r
10100 }\r
10101 \r
10102 /*      Convert board position to corner of screen rect and color       */\r
10103 \r
10104 static void\r
10105 ScreenSquare(column, row, pt)\r
10106      int column; int row; POINT * pt;\r
10107 {\r
10108   if (flipView) {\r
10109     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10110     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10111   } else {\r
10112     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10113     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10114   }\r
10115 }\r
10116 \r
10117 /*      Generate a series of frame coords from start->mid->finish.\r
10118         The movement rate doubles until the half way point is\r
10119         reached, then halves back down to the final destination,\r
10120         which gives a nice slow in/out effect. The algorithmn\r
10121         may seem to generate too many intermediates for short\r
10122         moves, but remember that the purpose is to attract the\r
10123         viewers attention to the piece about to be moved and\r
10124         then to where it ends up. Too few frames would be less\r
10125         noticeable.                                             */\r
10126 \r
10127 static void\r
10128 Tween(start, mid, finish, factor, frames, nFrames)\r
10129      POINT * start; POINT * mid;\r
10130      POINT * finish; int factor;\r
10131      POINT frames[]; int * nFrames;\r
10132 {\r
10133   int n, fraction = 1, count = 0;\r
10134 \r
10135   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10136   for (n = 0; n < factor; n++)\r
10137     fraction *= 2;\r
10138   for (n = 0; n < factor; n++) {\r
10139     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10140     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10141     count ++;\r
10142     fraction = fraction / 2;\r
10143   }\r
10144   \r
10145   /* Midpoint */\r
10146   frames[count] = *mid;\r
10147   count ++;\r
10148   \r
10149   /* Slow out, stepping 1/2, then 1/4, ... */\r
10150   fraction = 2;\r
10151   for (n = 0; n < factor; n++) {\r
10152     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10153     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10154     count ++;\r
10155     fraction = fraction * 2;\r
10156   }\r
10157   *nFrames = count;\r
10158 }\r
10159 \r
10160 void\r
10161 SettingsPopUp(ChessProgramState *cps)\r
10162 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10163       EngineOptionsPopup(savedHwnd, cps);\r
10164 }\r
10165 \r
10166 int flock(int fid, int code)\r
10167 {\r
10168     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10169     OVERLAPPED ov;\r
10170     ov.hEvent = NULL;\r
10171     ov.Offset = 0;\r
10172     ov.OffsetHigh = 0;\r
10173     switch(code) {\r
10174       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10175 \r
10176       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10177       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10178       default: return -1;\r
10179     }\r
10180     return 0;\r
10181 }\r
10182 \r
10183 char *\r
10184 Col2Text (int n)\r
10185 {\r
10186     static int i=0;\r
10187     static char col[8][20];\r
10188     COLORREF color = *(COLORREF *) colorVariable[n];\r
10189     i = i+1 & 7;\r
10190     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10191     return col[i];\r
10192 }\r
10193 \r
10194 void\r
10195 ActivateTheme (int new)\r
10196 {   // Redo initialization of features depending on options that can occur in themes\r
10197    InitTextures();\r
10198    if(new) InitDrawingColors();\r
10199    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10200    InitDrawingSizes(boardSize, 0);\r
10201    InvalidateRect(hwndMain, NULL, TRUE);\r
10202 }\r