Fix multi-leg promotions
[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, 2016 Free\r
9  * Software Foundation, Inc.\r
10  *\r
11  * Enhancements Copyright 2005 Alessandro Scotti\r
12  *\r
13  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
14  * which was written and is copyrighted by Wayne Christopher.\r
15  *\r
16  * The following terms apply to Digital Equipment Corporation's copyright\r
17  * interest in XBoard:\r
18  * ------------------------------------------------------------------------\r
19  * All Rights Reserved\r
20  *\r
21  * Permission to use, copy, modify, and distribute this software and its\r
22  * documentation for any purpose and without fee is hereby granted,\r
23  * provided that the above copyright notice appear in all copies and that\r
24  * both that copyright notice and this permission notice appear in\r
25  * supporting documentation, and that the name of Digital not be\r
26  * used in advertising or publicity pertaining to distribution of the\r
27  * software without specific, written prior permission.\r
28  *\r
29  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
30  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
31  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
32  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
33  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
34  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
35  * SOFTWARE.\r
36  * ------------------------------------------------------------------------\r
37  *\r
38  * The following terms apply to the enhanced version of XBoard\r
39  * distributed by the Free Software Foundation:\r
40  * ------------------------------------------------------------------------\r
41  *\r
42  * GNU XBoard is free software: you can redistribute it and/or modify\r
43  * it under the terms of the GNU General Public License as published by\r
44  * the Free Software Foundation, either version 3 of the License, or (at\r
45  * your option) any later version.\r
46  *\r
47  * GNU XBoard is distributed in the hope that it will be useful, but\r
48  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
49  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
50  * General Public License for more details.\r
51  *\r
52  * You should have received a copy of the GNU General Public License\r
53  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
54  *\r
55  *------------------------------------------------------------------------\r
56  ** See the file ChangeLog for a revision history.  */\r
57 \r
58 #include "config.h"\r
59 \r
60 #include <windows.h>\r
61 #include <winuser.h>\r
62 #include <winsock.h>\r
63 #include <commctrl.h>\r
64 \r
65 #include <stdio.h>\r
66 #include <stdlib.h>\r
67 #include <time.h>\r
68 #include <malloc.h>\r
69 #include <sys/stat.h>\r
70 #include <fcntl.h>\r
71 #include <math.h>\r
72 #include <commdlg.h>\r
73 #include <dlgs.h>\r
74 #include <richedit.h>\r
75 #include <mmsystem.h>\r
76 #include <ctype.h>\r
77 #include <io.h>\r
78 \r
79 #if __GNUC__\r
80 #include <errno.h>\r
81 #include <string.h>\r
82 #endif\r
83 \r
84 #include "common.h"\r
85 #include "frontend.h"\r
86 #include "backend.h"\r
87 #include "winboard.h"\r
88 #include "moves.h"\r
89 #include "wclipbrd.h"\r
90 #include "woptions.h"\r
91 #include "wsockerr.h"\r
92 #include "defaults.h"\r
93 #include "help.h"\r
94 #include "wsnap.h"\r
95 \r
96 #define SLASH '/'\r
97 #define DATADIR "~~"\r
98 \r
99 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
100 \r
101   int myrandom(void);\r
102   void mysrandom(unsigned int seed);\r
103 \r
104 extern int whiteFlag, blackFlag;\r
105 Boolean flipClock = FALSE;\r
106 extern HANDLE chatHandle[];\r
107 extern enum ICS_TYPE ics_type;\r
108 \r
109 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
110 int  MyGetFullPathName P((char *name, char *fullname));\r
111 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
112 VOID NewVariantPopup(HWND hwnd);\r
113 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
114                    /*char*/int promoChar));\r
115 void DisplayMove P((int moveNumber));\r
116 void ChatPopUp P((char *s));\r
117 typedef struct {\r
118   ChessSquare piece;  \r
119   POINT pos;      /* window coordinates of current pos */\r
120   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
121   POINT from;     /* board coordinates of the piece's orig pos */\r
122   POINT to;       /* board coordinates of the piece's new pos */\r
123 } AnimInfo;\r
124 \r
125 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT start;    /* window coordinates of start pos */\r
129   POINT pos;      /* window coordinates of current pos */\r
130   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
131   POINT from;     /* board coordinates of the piece's orig pos */\r
132   ChessSquare piece;\r
133 } DragInfo;\r
134 \r
135 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
136 \r
137 typedef struct {\r
138   POINT sq[2];    /* board coordinates of from, to squares */\r
139 } HighlightInfo;\r
140 \r
141 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
144 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
145 \r
146 typedef struct { // [HGM] atomic\r
147   int fromX, fromY, toX, toY, radius;\r
148 } ExplodeInfo;\r
149 \r
150 static ExplodeInfo explodeInfo;\r
151 \r
152 /* Window class names */\r
153 char szAppName[] = "WinBoard";\r
154 char szConsoleName[] = "WBConsole";\r
155 \r
156 /* Title bar text */\r
157 char szTitle[] = "WinBoard";\r
158 char szConsoleTitle[] = "I C S Interaction";\r
159 \r
160 char *programName;\r
161 char *settingsFileName;\r
162 Boolean saveSettingsOnExit;\r
163 char installDir[MSG_SIZ];\r
164 int errorExitStatus;\r
165 \r
166 BoardSize boardSize;\r
167 Boolean chessProgram;\r
168 //static int boardX, boardY;\r
169 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
170 int squareSize, lineGap, minorSize;\r
171 static int winW, winH;\r
172 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
173 static int logoHeight = 0;\r
174 static char messageText[MESSAGE_TEXT_MAX];\r
175 static int clockTimerEvent = 0;\r
176 static int loadGameTimerEvent = 0;\r
177 static int analysisTimerEvent = 0;\r
178 static DelayedEventCallback delayedTimerCallback;\r
179 static int delayedTimerEvent = 0;\r
180 static int buttonCount = 2;\r
181 char *icsTextMenuString;\r
182 char *icsNames;\r
183 char *firstChessProgramNames;\r
184 char *secondChessProgramNames;\r
185 \r
186 #define PALETTESIZE 256\r
187 \r
188 HINSTANCE hInst;          /* current instance */\r
189 Boolean alwaysOnTop = FALSE;\r
190 RECT boardRect;\r
191 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
192   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
193 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
194 HPALETTE hPal;\r
195 ColorClass currentColorClass;\r
196 \r
197 static HWND savedHwnd;\r
198 HWND hCommPort = NULL;    /* currently open comm port */\r
199 static HWND hwndPause;    /* pause button */\r
200 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
201 static HBRUSH lightSquareBrush, darkSquareBrush,\r
202   blackSquareBrush, /* [HGM] for band between board and holdings */\r
203   explodeBrush,     /* [HGM] atomic */\r
204   markerBrush[8],   /* [HGM] markers */\r
205   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
206 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
207 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
208 static HPEN gridPen = NULL;\r
209 static HPEN highlightPen = NULL;\r
210 static HPEN premovePen = NULL;\r
211 static NPLOGPALETTE pLogPal;\r
212 static BOOL paletteChanged = FALSE;\r
213 static HICON iconWhite, iconBlack, iconCurrent;\r
214 static int doingSizing = FALSE;\r
215 static int lastSizing = 0;\r
216 static int prevStderrPort;\r
217 static HBITMAP userLogo;\r
218 \r
219 static HBITMAP liteBackTexture = NULL;\r
220 static HBITMAP darkBackTexture = NULL;\r
221 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
223 static int backTextureSquareSize = 0;\r
224 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
225 \r
226 #if __GNUC__ && !defined(_winmajor)\r
227 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
228 #else\r
229 \r
230 #if defined(_winmajor)\r
231 #define oldDialog (_winmajor < 4)\r
232 #else\r
233 #define oldDialog 0\r
234 #endif\r
235 #endif\r
236 \r
237 #define INTERNATIONAL\r
238 \r
239 #ifdef INTERNATIONAL\r
240 #  define _(s) T_(s)\r
241 #  define N_(s) s\r
242 #else\r
243 #  define _(s) s\r
244 #  define N_(s) s\r
245 #  define T_(s) s\r
246 #  define Translate(x, y)\r
247 #  define LoadLanguageFile(s)\r
248 #endif\r
249 \r
250 #ifdef INTERNATIONAL\r
251 \r
252 Boolean barbaric; // flag indicating if translation is needed\r
253 \r
254 // list of item numbers used in each dialog (used to alter language at run time)\r
255 \r
256 #define ABOUTBOX -1  /* not sure why these are needed */\r
257 #define ABOUTBOX2 -1\r
258 \r
259 int dialogItems[][42] = {\r
260 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
261 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
262   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
263 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
264   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
265   OPT_Ranget, IDOK, IDCANCEL }, \r
266 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
267   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
268 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
269 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
270   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
271 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
272 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
273   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
274 { ABOUTBOX2, IDC_ChessBoard }, \r
275 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
276   OPT_GameListClose, IDC_GameListDoFilter }, \r
277 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
278 { DLG_Error, IDOK }, \r
279 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
280   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
281 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
282 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
283   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
284   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
285 { DLG_IndexNumber, IDC_Index }, \r
286 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
287 { DLG_TypeInName, IDOK, IDCANCEL }, \r
288 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
289   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
290 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
291   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
292   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
293   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
294   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
295   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
296   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
297 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
298   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
299   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
300   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
301   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
302   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
303   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
304   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
305   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
306 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
307   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
308   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
309   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
310   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
311   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
312   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
313   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
314 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
315   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
316   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
317   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
318   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
319   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
320   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
321   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
322   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
323 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
324   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
325   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
326   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
327   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
328 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
329 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
330   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
331 { DLG_MoveHistory }, \r
332 { DLG_EvalGraph }, \r
333 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
334 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
335 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
336   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
337   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
338   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
339 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
340   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
341   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
342 { 0 }\r
343 };\r
344 \r
345 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
346 static int lastChecked;\r
347 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
348 extern int tinyLayout;\r
349 extern char * menuBarText[][10];\r
350 \r
351 void\r
352 LoadLanguageFile(char *name)\r
353 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
354     FILE *f;\r
355     int i=0, j=0, n=0, k;\r
356     char buf[MSG_SIZ];\r
357 \r
358     if(!name || name[0] == NULLCHAR) return;\r
359       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
360     appData.language = oldLanguage;\r
361     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
362     if((f = fopen(buf, "r")) == NULL) return;\r
363     while((k = fgetc(f)) != EOF) {\r
364         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
365         languageBuf[i] = k;\r
366         if(k == '\n') {\r
367             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
368                 char *p;\r
369                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
370                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
371                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
372                         english[j] = languageBuf + n + 1; *p = 0;\r
373                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
374 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
375                     }\r
376                 }\r
377             }\r
378             n = i + 1;\r
379         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
380             switch(k) {\r
381               case 'n': k = '\n'; break;\r
382               case 'r': k = '\r'; break;\r
383               case 't': k = '\t'; break;\r
384             }\r
385             languageBuf[--i] = k;\r
386         }\r
387         i++;\r
388     }\r
389     fclose(f);\r
390     barbaric = (j != 0);\r
391     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
392 }\r
393 \r
394 char *\r
395 T_(char *s)\r
396 {   // return the translation of the given string\r
397     // efficiency can be improved a lot...\r
398     int i=0;\r
399     static char buf[MSG_SIZ];\r
400 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
401     if(!barbaric) return s;\r
402     if(!s) return ""; // sanity\r
403     while(english[i]) {\r
404         if(!strcmp(s, english[i])) return foreign[i];\r
405         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
406             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
407             return buf;\r
408         }\r
409         i++;\r
410     }\r
411     return s;\r
412 }\r
413 \r
414 void\r
415 Translate(HWND hDlg, int dialogID)\r
416 {   // translate all text items in the given dialog\r
417     int i=0, j, k;\r
418     char buf[MSG_SIZ], *s;\r
419     if(!barbaric) return;\r
420     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
421     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
422     GetWindowText( hDlg, buf, MSG_SIZ );\r
423     s = T_(buf);\r
424     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
425     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
426         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
427         if(strlen(buf) == 0) continue;\r
428         s = T_(buf);\r
429         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
430     }\r
431 }\r
432 \r
433 HMENU\r
434 TranslateOneMenu(int i, HMENU subMenu)\r
435 {\r
436     int j;\r
437     static MENUITEMINFO info;\r
438 \r
439     info.cbSize = sizeof(MENUITEMINFO);\r
440     info.fMask = MIIM_STATE | MIIM_TYPE;\r
441           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
442             char buf[MSG_SIZ];\r
443             info.dwTypeData = buf;\r
444             info.cch = sizeof(buf);\r
445             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
446             if(i < 10) {\r
447                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
448                 else menuText[i][j] = strdup(buf); // remember original on first change\r
449             }\r
450             if(buf[0] == NULLCHAR) continue;\r
451             info.dwTypeData = T_(buf);\r
452             info.cch = strlen(buf)+1;\r
453             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
454           }\r
455     return subMenu;\r
456 }\r
457 \r
458 void\r
459 TranslateMenus(int addLanguage)\r
460 {\r
461     int i;\r
462     WIN32_FIND_DATA fileData;\r
463     HANDLE hFind;\r
464 #define IDM_English 1970\r
465     if(1) {\r
466         HMENU mainMenu = GetMenu(hwndMain);\r
467         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
468           HMENU subMenu = GetSubMenu(mainMenu, i);\r
469           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
470                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
471           TranslateOneMenu(i, subMenu);\r
472         }\r
473         DrawMenuBar(hwndMain);\r
474     }\r
475 \r
476     if(!addLanguage) return;\r
477     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
478         HMENU mainMenu = GetMenu(hwndMain);\r
479         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
480         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
481         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
482         i = 0; lastChecked = IDM_English;\r
483         do {\r
484             char *p, *q = fileData.cFileName;\r
485             int checkFlag = MF_UNCHECKED;\r
486             languageFile[i] = strdup(q);\r
487             if(barbaric && !strcmp(oldLanguage, q)) {\r
488                 checkFlag = MF_CHECKED;\r
489                 lastChecked = IDM_English + i + 1;\r
490                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
491             }\r
492             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
493             p = strstr(fileData.cFileName, ".lng");\r
494             if(p) *p = 0;\r
495             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
496         } while(FindNextFile(hFind, &fileData));\r
497         FindClose(hFind);\r
498     }\r
499 }\r
500 \r
501 #endif\r
502 \r
503 #define IDM_RecentEngines 3000\r
504 \r
505 void\r
506 RecentEngineMenu (char *s)\r
507 {\r
508     if(appData.icsActive) return;\r
509     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
510         HMENU mainMenu = GetMenu(hwndMain);\r
511         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
512         int i=IDM_RecentEngines;\r
513         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
514         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
515         while(*s) {\r
516           char *p = strchr(s, '\n');\r
517           if(p == NULL) return; // malformed!\r
518           *p = NULLCHAR;\r
519           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
520           *p = '\n';\r
521           s = p+1;\r
522         }\r
523     }\r
524 }\r
525 \r
526 \r
527 typedef struct {\r
528   char *name;\r
529   int squareSize;\r
530   int lineGap;\r
531   int smallLayout;\r
532   int tinyLayout;\r
533   int cliWidth, cliHeight;\r
534 } SizeInfo;\r
535 \r
536 SizeInfo sizeInfo[] = \r
537 {\r
538   { "tiny",     21, 0, 1, 2, 0, 0 },\r
539   { "teeny",    25, 1, 1, 2, 0, 0 },\r
540   { "dinky",    29, 1, 1, 2, 0, 0 },\r
541   { "petite",   33, 1, 1, 2, 0, 0 },\r
542   { "slim",     37, 2, 1, 1, 0, 0 },\r
543   { "small",    40, 2, 1, 1, 0, 0 },\r
544   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
545   { "middling", 49, 2, 0, 0, 0, 0 },\r
546   { "average",  54, 2, 0, 0, 0, 0 },\r
547   { "moderate", 58, 3, 0, 0, 0, 0 },\r
548   { "medium",   64, 3, 0, 0, 0, 0 },\r
549   { "bulky",    72, 3, 0, 0, 0, 0 },\r
550   { "large",    80, 3, 0, 0, 0, 0 },\r
551   { "big",      87, 3, 0, 0, 0, 0 },\r
552   { "huge",     95, 3, 0, 0, 0, 0 },\r
553   { "giant",    108, 3, 0, 0, 0, 0 },\r
554   { "colossal", 116, 4, 0, 0, 0, 0 },\r
555   { "titanic",  129, 4, 0, 0, 0, 0 },\r
556   { NULL, 0, 0, 0, 0, 0, 0 }\r
557 };\r
558 \r
559 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
560 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
561 {\r
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576   { 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
577   { 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
578   { 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
579   { 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
580 };\r
581 \r
582 MyFont *font[NUM_SIZES][NUM_FONTS];\r
583 \r
584 typedef struct {\r
585   char *label;\r
586   int id;\r
587   HWND hwnd;\r
588   WNDPROC wndproc;\r
589 } MyButtonDesc;\r
590 \r
591 #define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)\r
592 #define N_BUTTONS 5\r
593 \r
594 MyButtonDesc buttonDesc[N_BUTTONS] =\r
595 {\r
596   {"<<", IDM_ToStart, NULL, NULL},\r
597   {"<", IDM_Backward, NULL, NULL},\r
598   {"P", IDM_Pause, NULL, NULL},\r
599   {">", IDM_Forward, NULL, NULL},\r
600   {">>", IDM_ToEnd, NULL, NULL},\r
601 };\r
602 \r
603 int tinyLayout = 0, smallLayout = 0;\r
604 #define MENU_BAR_ITEMS 9\r
605 char *menuBarText[3][MENU_BAR_ITEMS+1] = {\r
606   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
607   { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },\r
608   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
609 };\r
610 \r
611 \r
612 MySound sounds[(int)NSoundClasses];\r
613 MyTextAttribs textAttribs[(int)NColorClasses];\r
614 \r
615 MyColorizeAttribs colorizeAttribs[] = {\r
616   { (COLORREF)0, 0, N_("Shout Text") },\r
617   { (COLORREF)0, 0, N_("SShout/CShout") },\r
618   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
619   { (COLORREF)0, 0, N_("Channel Text") },\r
620   { (COLORREF)0, 0, N_("Kibitz Text") },\r
621   { (COLORREF)0, 0, N_("Tell Text") },\r
622   { (COLORREF)0, 0, N_("Challenge Text") },\r
623   { (COLORREF)0, 0, N_("Request Text") },\r
624   { (COLORREF)0, 0, N_("Seek Text") },\r
625   { (COLORREF)0, 0, N_("Normal Text") },\r
626   { (COLORREF)0, 0, N_("None") }\r
627 };\r
628 \r
629 \r
630 \r
631 static char *commentTitle;\r
632 static char *commentText;\r
633 static int commentIndex;\r
634 static Boolean editComment = FALSE;\r
635 \r
636 \r
637 char errorTitle[MSG_SIZ];\r
638 char errorMessage[2*MSG_SIZ];\r
639 HWND errorDialog = NULL;\r
640 BOOLEAN moveErrorMessageUp = FALSE;\r
641 BOOLEAN consoleEcho = TRUE;\r
642 CHARFORMAT consoleCF;\r
643 COLORREF consoleBackgroundColor;\r
644 \r
645 char *programVersion;\r
646 \r
647 #define CPReal 1\r
648 #define CPComm 2\r
649 #define CPSock 3\r
650 #define CPRcmd 4\r
651 typedef int CPKind;\r
652 \r
653 typedef struct {\r
654   CPKind kind;\r
655   HANDLE hProcess;\r
656   DWORD pid;\r
657   HANDLE hTo;\r
658   HANDLE hFrom;\r
659   SOCKET sock;\r
660   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
661 } ChildProc;\r
662 \r
663 #define INPUT_SOURCE_BUF_SIZE 4096\r
664 \r
665 typedef struct _InputSource {\r
666   CPKind kind;\r
667   HANDLE hFile;\r
668   SOCKET sock;\r
669   int lineByLine;\r
670   HANDLE hThread;\r
671   DWORD id;\r
672   char buf[INPUT_SOURCE_BUF_SIZE];\r
673   char *next;\r
674   DWORD count;\r
675   int error;\r
676   InputCallback func;\r
677   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
678   VOIDSTAR closure;\r
679 } InputSource;\r
680 \r
681 InputSource *consoleInputSource;\r
682 \r
683 DCB dcb;\r
684 \r
685 /* forward */\r
686 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
687 VOID ConsoleCreate();\r
688 LRESULT CALLBACK\r
689   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
690 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
691 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
692 VOID ParseCommSettings(char *arg, DCB *dcb);\r
693 LRESULT CALLBACK\r
694   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
695 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
696 void ParseIcsTextMenu(char *icsTextMenuString);\r
697 VOID PopUpNameDialog(char firstchar);\r
698 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
699 \r
700 /* [AS] */\r
701 int NewGameFRC();\r
702 int GameListOptions();\r
703 \r
704 int dummy; // [HGM] for obsolete args\r
705 \r
706 HWND hwndMain = NULL;        /* root window*/\r
707 HWND hwndConsole = NULL;\r
708 HWND commentDialog = NULL;\r
709 HWND moveHistoryDialog = NULL;\r
710 HWND evalGraphDialog = NULL;\r
711 HWND engineOutputDialog = NULL;\r
712 HWND gameListDialog = NULL;\r
713 HWND editTagsDialog = NULL;\r
714 \r
715 int commentUp = FALSE;\r
716 \r
717 WindowPlacement wpMain;\r
718 WindowPlacement wpConsole;\r
719 WindowPlacement wpComment;\r
720 WindowPlacement wpMoveHistory;\r
721 WindowPlacement wpEvalGraph;\r
722 WindowPlacement wpEngineOutput;\r
723 WindowPlacement wpGameList;\r
724 WindowPlacement wpTags;\r
725 \r
726 VOID EngineOptionsPopup(); // [HGM] settings\r
727 \r
728 VOID GothicPopUp(char *title, VariantClass variant);\r
729 /*\r
730  * Setting "frozen" should disable all user input other than deleting\r
731  * the window.  We do this while engines are initializing themselves.\r
732  */\r
733 static int frozen = 0;\r
734 static int oldMenuItemState[MENU_BAR_ITEMS];\r
735 void FreezeUI()\r
736 {\r
737   HMENU hmenu;\r
738   int i;\r
739 \r
740   if (frozen) return;\r
741   frozen = 1;\r
742   hmenu = GetMenu(hwndMain);\r
743   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
744     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
745   }\r
746   DrawMenuBar(hwndMain);\r
747 }\r
748 \r
749 /* Undo a FreezeUI */\r
750 void ThawUI()\r
751 {\r
752   HMENU hmenu;\r
753   int i;\r
754 \r
755   if (!frozen) return;\r
756   frozen = 0;\r
757   hmenu = GetMenu(hwndMain);\r
758   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
759     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
760   }\r
761   DrawMenuBar(hwndMain);\r
762 }\r
763 \r
764 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
765 \r
766 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
767 #ifdef JAWS\r
768 #include "jaws.c"\r
769 #else\r
770 #define JAWS_INIT\r
771 #define JAWS_ARGS\r
772 #define JAWS_ALT_INTERCEPT\r
773 #define JAWS_KBUP_NAVIGATION\r
774 #define JAWS_KBDOWN_NAVIGATION\r
775 #define JAWS_MENU_ITEMS\r
776 #define JAWS_SILENCE\r
777 #define JAWS_REPLAY\r
778 #define JAWS_ACCEL\r
779 #define JAWS_COPYRIGHT\r
780 #define JAWS_DELETE(X) X\r
781 #define SAYMACHINEMOVE()\r
782 #define SAY(X)\r
783 #endif\r
784 \r
785 /*---------------------------------------------------------------------------*\\r
786  *\r
787  * WinMain\r
788  *\r
789 \*---------------------------------------------------------------------------*/\r
790 \r
791 static void HandleMessage P((MSG *message));\r
792 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
793 \r
794 int APIENTRY\r
795 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
796         LPSTR lpCmdLine, int nCmdShow)\r
797 {\r
798   MSG msg;\r
799 //  INITCOMMONCONTROLSEX ex;\r
800 \r
801   debugFP = stderr;\r
802 \r
803   LoadLibrary("RICHED32.DLL");\r
804   consoleCF.cbSize = sizeof(CHARFORMAT);\r
805 \r
806   if (!InitApplication(hInstance)) {\r
807     return (FALSE);\r
808   }\r
809   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
810     return (FALSE);\r
811   }\r
812 \r
813   JAWS_INIT\r
814   TranslateMenus(1);\r
815 \r
816 //  InitCommonControlsEx(&ex);\r
817   InitCommonControls();\r
818 \r
819   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
820   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
821   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
822 \r
823   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
824 \r
825   while (GetMessage(&msg, /* message structure */\r
826                     NULL, /* handle of window receiving the message */\r
827                     0,    /* lowest message to examine */\r
828                     0))   /* highest message to examine */\r
829     {\r
830         HandleMessage(&msg);\r
831     }\r
832 \r
833 \r
834   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
835 }\r
836 \r
837 static void\r
838 HandleMessage (MSG *message)\r
839 {\r
840     MSG msg = *message;\r
841 \r
842       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
843         // [HGM] navigate: switch between all windows with tab\r
844         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
845         int i, currentElement = 0;\r
846 \r
847         // first determine what element of the chain we come from (if any)\r
848         if(appData.icsActive) {\r
849             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
850             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
851         }\r
852         if(engineOutputDialog && EngineOutputIsUp()) {\r
853             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
854             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
855         }\r
856         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
857             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
858         }\r
859         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
860         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
861         if(msg.hwnd == e1)                 currentElement = 2; else\r
862         if(msg.hwnd == e2)                 currentElement = 3; else\r
863         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
864         if(msg.hwnd == mh)                currentElement = 4; else\r
865         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
866         if(msg.hwnd == hText)  currentElement = 5; else\r
867         if(msg.hwnd == hInput) currentElement = 6; else\r
868         for (i = 0; i < N_BUTTONS; i++) {\r
869             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
870         }\r
871 \r
872         // determine where to go to\r
873         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
874           do {\r
875             currentElement = (currentElement + direction) % 7;\r
876             switch(currentElement) {\r
877                 case 0:\r
878                   h = hwndMain; break; // passing this case always makes the loop exit\r
879                 case 1:\r
880                   h = buttonDesc[0].hwnd; break; // could be NULL\r
881                 case 2:\r
882                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
883                   h = e1; break;\r
884                 case 3:\r
885                   if(!EngineOutputIsUp()) continue;\r
886                   h = e2; break;\r
887                 case 4:\r
888                   if(!MoveHistoryIsUp()) continue;\r
889                   h = mh; break;\r
890 //              case 6: // input to eval graph does not seem to get here!\r
891 //                if(!EvalGraphIsUp()) continue;\r
892 //                h = evalGraphDialog; break;\r
893                 case 5:\r
894                   if(!appData.icsActive) continue;\r
895                   SAY("display");\r
896                   h = hText; break;\r
897                 case 6:\r
898                   if(!appData.icsActive) continue;\r
899                   SAY("input");\r
900                   h = hInput; break;\r
901             }\r
902           } while(h == 0);\r
903 \r
904           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
905           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
906           SetFocus(h);\r
907 \r
908           return; // this message now has been processed\r
909         }\r
910       }\r
911 \r
912       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
913           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
914           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
915           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
916           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
917           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
918           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
919           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
920           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
921           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
922         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
923         for(i=0; i<MAX_CHAT; i++) \r
924             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
925                 done = 1; break;\r
926         }\r
927         if(done) return; // [HGM] chat: end patch\r
928         TranslateMessage(&msg); /* Translates virtual key codes */\r
929         DispatchMessage(&msg);  /* Dispatches message to window */\r
930       }\r
931 }\r
932 \r
933 void\r
934 DoEvents ()\r
935 { /* Dispatch pending messages */\r
936   MSG msg;\r
937   while (PeekMessage(&msg, /* message structure */\r
938                      NULL, /* handle of window receiving the message */\r
939                      0,    /* lowest message to examine */\r
940                      0,    /* highest message to examine */\r
941                      PM_REMOVE))\r
942     {\r
943         HandleMessage(&msg);\r
944     }\r
945 }\r
946 \r
947 /*---------------------------------------------------------------------------*\\r
948  *\r
949  * Initialization functions\r
950  *\r
951 \*---------------------------------------------------------------------------*/\r
952 \r
953 void\r
954 SetUserLogo()\r
955 {   // update user logo if necessary\r
956     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
957 \r
958     if(appData.autoLogo) {\r
959           curName = UserName();\r
960           if(strcmp(curName, oldUserName)) {\r
961                 GetCurrentDirectory(MSG_SIZ, dir);\r
962                 SetCurrentDirectory(installDir);\r
963                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
964                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
965                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
966                 if(userLogo == NULL)\r
967                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
968                 SetCurrentDirectory(dir); /* return to prev directory */\r
969           }\r
970     }\r
971 }\r
972 \r
973 BOOL\r
974 InitApplication(HINSTANCE hInstance)\r
975 {\r
976   WNDCLASS wc;\r
977 \r
978   /* Fill in window class structure with parameters that describe the */\r
979   /* main window. */\r
980 \r
981   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
982   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
983   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
984   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
985   wc.hInstance     = hInstance;         /* Owner of this class */\r
986   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
987   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
988   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
989   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
990   wc.lpszClassName = szAppName;                 /* Name to register as */\r
991 \r
992   /* Register the window class and return success/failure code. */\r
993   if (!RegisterClass(&wc)) return FALSE;\r
994 \r
995   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
996   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
997   wc.cbClsExtra    = 0;\r
998   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
999   wc.hInstance     = hInstance;\r
1000   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
1001   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1002   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1003   wc.lpszMenuName  = NULL;\r
1004   wc.lpszClassName = szConsoleName;\r
1005 \r
1006   if (!RegisterClass(&wc)) return FALSE;\r
1007   return TRUE;\r
1008 }\r
1009 \r
1010 \r
1011 /* Set by InitInstance, used by EnsureOnScreen */\r
1012 int screenHeight, screenWidth;\r
1013 RECT screenGeometry;\r
1014 \r
1015 void\r
1016 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1017 {\r
1018 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1019   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1020   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1021   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1022   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1023   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1024 }\r
1025 \r
1026 VOID\r
1027 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1028 {\r
1029   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1030   GetCurrentDirectory(MSG_SIZ, dir);\r
1031   SetCurrentDirectory(installDir);\r
1032   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1033       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1034 \r
1035       if (cps->programLogo == NULL && appData.debugMode) {\r
1036           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1037       }\r
1038   } else if(appData.autoLogo) {\r
1039       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1040         char *opponent = "";\r
1041         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1042         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1043         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1044         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1045             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1046             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1047         }\r
1048       } else\r
1049       if(appData.directory[n] && appData.directory[n][0]) {\r
1050         SetCurrentDirectory(appData.directory[n]);\r
1051         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1052       }\r
1053   }\r
1054   SetCurrentDirectory(dir); /* return to prev directory */\r
1055 }\r
1056 \r
1057 VOID\r
1058 InitTextures()\r
1059 {\r
1060   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1061   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1062   \r
1063   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1064       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1065       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1066       liteBackTextureMode = appData.liteBackTextureMode;\r
1067 \r
1068       if (liteBackTexture == NULL && appData.debugMode) {\r
1069           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1070       }\r
1071   }\r
1072   \r
1073   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1074       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1075       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1076       darkBackTextureMode = appData.darkBackTextureMode;\r
1077 \r
1078       if (darkBackTexture == NULL && appData.debugMode) {\r
1079           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1080       }\r
1081   }\r
1082 }\r
1083 \r
1084 #ifndef SM_CXVIRTUALSCREEN\r
1085 #define SM_CXVIRTUALSCREEN 78\r
1086 #endif\r
1087 #ifndef SM_CYVIRTUALSCREEN\r
1088 #define SM_CYVIRTUALSCREEN 79\r
1089 #endif\r
1090 #ifndef SM_XVIRTUALSCREEN \r
1091 #define SM_XVIRTUALSCREEN 76\r
1092 #endif\r
1093 #ifndef SM_YVIRTUALSCREEN \r
1094 #define SM_YVIRTUALSCREEN 77\r
1095 #endif\r
1096 \r
1097 VOID\r
1098 InitGeometry()\r
1099 {\r
1100   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1101   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1102   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1103   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1104   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1105   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1106   screenGeometry.right = screenGeometry.left + screenWidth;\r
1107   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1108 }\r
1109 \r
1110 ChessProgramState broadcast;\r
1111 \r
1112 BOOL\r
1113 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1114 {\r
1115   HWND hwnd; /* Main window handle. */\r
1116   int ibs;\r
1117   WINDOWPLACEMENT wp;\r
1118   char *filepart;\r
1119 \r
1120   hInst = hInstance;    /* Store instance handle in our global variable */\r
1121   programName = szAppName;\r
1122 \r
1123   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1124     *filepart = NULLCHAR;\r
1125     SetCurrentDirectory(installDir);\r
1126   } else {\r
1127     GetCurrentDirectory(MSG_SIZ, installDir);\r
1128   }\r
1129   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1130   InitGeometry();\r
1131   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1132   /* xboard, and older WinBoards, controlled the move sound with the\r
1133      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1134      always turn the option on (so that the backend will call us),\r
1135      then let the user turn the sound off by setting it to silence if\r
1136      desired.  To accommodate old winboard.ini files saved by old\r
1137      versions of WinBoard, we also turn off the sound if the option\r
1138      was initially set to false. [HGM] taken out of InitAppData */\r
1139   if (!appData.ringBellAfterMoves) {\r
1140     sounds[(int)SoundMove].name = strdup("");\r
1141     appData.ringBellAfterMoves = TRUE;\r
1142   }\r
1143   if (appData.debugMode) {\r
1144     char *c = appData.nameOfDebugFile;\r
1145     if(strstr(c, "///") == c) {\r
1146       broadcast.which = "broadcaster";\r
1147       broadcast.pr   = NoProc;\r
1148       broadcast.isr  = NULL;\r
1149       broadcast.program = c + 3;\r
1150       broadcast.dir  = ".";\r
1151       broadcast.host = "localhost";\r
1152       StartChessProgram(&broadcast);\r
1153       debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");\r
1154     } else\r
1155     debugFP = fopen(c, "w");\r
1156     setbuf(debugFP, NULL);\r
1157   }\r
1158 \r
1159   LoadLanguageFile(appData.language);\r
1160 \r
1161   InitBackEnd1();\r
1162 \r
1163 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1164 //  InitEngineUCI( installDir, &second );\r
1165 \r
1166   /* Create a main window for this application instance. */\r
1167   hwnd = CreateWindow(szAppName, szTitle,\r
1168                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1169                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1170                       NULL, NULL, hInstance, NULL);\r
1171   hwndMain = hwnd;\r
1172 \r
1173   /* If window could not be created, return "failure" */\r
1174   if (!hwnd) {\r
1175     return (FALSE);\r
1176   }\r
1177 \r
1178   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1179   LoadLogo(&first, 0, FALSE);\r
1180   LoadLogo(&second, 1, appData.icsActive);\r
1181 \r
1182   SetUserLogo();\r
1183 \r
1184   iconWhite = LoadIcon(hInstance, "icon_white");\r
1185   iconBlack = LoadIcon(hInstance, "icon_black");\r
1186   iconCurrent = iconWhite;\r
1187   InitDrawingColors();\r
1188 \r
1189   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1190   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1191     /* Compute window size for each board size, and use the largest\r
1192        size that fits on this screen as the default. */\r
1193     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1194     if (boardSize == (BoardSize)-1 &&\r
1195         winH <= screenHeight\r
1196            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1197         && winW <= screenWidth) {\r
1198       boardSize = (BoardSize)ibs;\r
1199     }\r
1200   }\r
1201 \r
1202   InitDrawingSizes(boardSize, 0);\r
1203   RecentEngineMenu(appData.recentEngineList);\r
1204   InitMenuChecks();\r
1205   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1206 \r
1207   /* [AS] Load textures if specified */\r
1208   InitTextures();\r
1209 \r
1210   mysrandom( (unsigned) time(NULL) );\r
1211 \r
1212   /* [AS] Restore layout */\r
1213   if( wpMoveHistory.visible ) {\r
1214       MoveHistoryPopUp();\r
1215   }\r
1216 \r
1217   if( wpEvalGraph.visible ) {\r
1218       EvalGraphPopUp();\r
1219   }\r
1220 \r
1221   if( wpEngineOutput.visible ) {\r
1222       EngineOutputPopUp();\r
1223   }\r
1224 \r
1225   /* Make the window visible; update its client area; and return "success" */\r
1226   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1227   wp.length = sizeof(WINDOWPLACEMENT);\r
1228   wp.flags = 0;\r
1229   wp.showCmd = nCmdShow;\r
1230   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1231   wp.rcNormalPosition.left = wpMain.x;\r
1232   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1233   wp.rcNormalPosition.top = wpMain.y;\r
1234   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1235   SetWindowPlacement(hwndMain, &wp);\r
1236 \r
1237   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1238 \r
1239   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1240                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1241 \r
1242   if (hwndConsole) {\r
1243 #if AOT_CONSOLE\r
1244     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1245                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1246 #endif\r
1247     ShowWindow(hwndConsole, nCmdShow);\r
1248     SetActiveWindow(hwndConsole);\r
1249   }\r
1250   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1251   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1252 \r
1253   return TRUE;\r
1254 \r
1255 }\r
1256 \r
1257 VOID\r
1258 InitMenuChecks()\r
1259 {\r
1260   HMENU hmenu = GetMenu(hwndMain);\r
1261 \r
1262   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1263                         MF_BYCOMMAND|((appData.icsActive &&\r
1264                                        *appData.icsCommPort != NULLCHAR) ?\r
1265                                       MF_ENABLED : MF_GRAYED));\r
1266   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1267                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1268                                      MF_CHECKED : MF_UNCHECKED));\r
1269   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1270 }\r
1271 \r
1272 //---------------------------------------------------------------------------------------------------------\r
1273 \r
1274 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1275 #define XBOARD FALSE\r
1276 \r
1277 #define OPTCHAR "/"\r
1278 #define SEPCHAR "="\r
1279 #define TOPLEVEL 0\r
1280 \r
1281 #include "args.h"\r
1282 \r
1283 // front-end part of option handling\r
1284 \r
1285 VOID\r
1286 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1287 {\r
1288   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1289   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1290   DeleteDC(hdc);\r
1291   lf->lfWidth = 0;\r
1292   lf->lfEscapement = 0;\r
1293   lf->lfOrientation = 0;\r
1294   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1295   lf->lfItalic = mfp->italic;\r
1296   lf->lfUnderline = mfp->underline;\r
1297   lf->lfStrikeOut = mfp->strikeout;\r
1298   lf->lfCharSet = mfp->charset;\r
1299   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1300 \r
1301 \r
1302 \r
1303   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1304   lf->lfQuality = DEFAULT_QUALITY;\r
1305   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1306     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1307 }\r
1308 \r
1309 void\r
1310 CreateFontInMF(MyFont *mf)\r
1311\r
1312   LFfromMFP(&mf->lf, &mf->mfp);\r
1313   if (mf->hf) DeleteObject(mf->hf);\r
1314   mf->hf = CreateFontIndirect(&mf->lf);\r
1315 }\r
1316 \r
1317 // [HGM] This platform-dependent table provides the location for storing the color info\r
1318 void *\r
1319 colorVariable[] = {\r
1320   &whitePieceColor, \r
1321   &blackPieceColor, \r
1322   &lightSquareColor,\r
1323   &darkSquareColor, \r
1324   &highlightSquareColor,\r
1325   &premoveHighlightColor,\r
1326   NULL,\r
1327   &consoleBackgroundColor,\r
1328   &appData.fontForeColorWhite,\r
1329   &appData.fontBackColorWhite,\r
1330   &appData.fontForeColorBlack,\r
1331   &appData.fontBackColorBlack,\r
1332   &appData.evalHistColorWhite,\r
1333   &appData.evalHistColorBlack,\r
1334   &appData.highlightArrowColor,\r
1335 };\r
1336 \r
1337 /* Command line font name parser.  NULL name means do nothing.\r
1338    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1339    For backward compatibility, syntax without the colon is also\r
1340    accepted, but font names with digits in them won't work in that case.\r
1341 */\r
1342 VOID\r
1343 ParseFontName(char *name, MyFontParams *mfp)\r
1344 {\r
1345   char *p, *q;\r
1346   if (name == NULL) return;\r
1347   p = name;\r
1348   q = strchr(p, ':');\r
1349   if (q) {\r
1350     if (q - p >= sizeof(mfp->faceName))\r
1351       ExitArgError(_("Font name too long:"), name, TRUE);\r
1352     memcpy(mfp->faceName, p, q - p);\r
1353     mfp->faceName[q - p] = NULLCHAR;\r
1354     p = q + 1;\r
1355   } else {\r
1356     q = mfp->faceName;\r
1357 \r
1358     while (*p && !isdigit(*p)) {\r
1359       *q++ = *p++;\r
1360       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1361         ExitArgError(_("Font name too long:"), name, TRUE);\r
1362     }\r
1363     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1364     *q = NULLCHAR;\r
1365   }\r
1366   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1367   mfp->pointSize = (float) atof(p);\r
1368   mfp->bold = (strchr(p, 'b') != NULL);\r
1369   mfp->italic = (strchr(p, 'i') != NULL);\r
1370   mfp->underline = (strchr(p, 'u') != NULL);\r
1371   mfp->strikeout = (strchr(p, 's') != NULL);\r
1372   mfp->charset = DEFAULT_CHARSET;\r
1373   q = strchr(p, 'c');\r
1374   if (q)\r
1375     mfp->charset = (BYTE) atoi(q+1);\r
1376 }\r
1377 \r
1378 void\r
1379 ParseFont(char *name, int number)\r
1380 { // wrapper to shield back-end from 'font'\r
1381   ParseFontName(name, &font[boardSize][number]->mfp);\r
1382 }\r
1383 \r
1384 void\r
1385 SetFontDefaults()\r
1386 { // in WB  we have a 2D array of fonts; this initializes their description\r
1387   int i, j;\r
1388   /* Point font array elements to structures and\r
1389      parse default font names */\r
1390   for (i=0; i<NUM_FONTS; i++) {\r
1391     for (j=0; j<NUM_SIZES; j++) {\r
1392       font[j][i] = &fontRec[j][i];\r
1393       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1394     }\r
1395   }\r
1396 }\r
1397 \r
1398 void\r
1399 CreateFonts()\r
1400 { // here we create the actual fonts from the selected descriptions\r
1401   int i, j;\r
1402   for (i=0; i<NUM_FONTS; i++) {\r
1403     for (j=0; j<NUM_SIZES; j++) {\r
1404       CreateFontInMF(font[j][i]);\r
1405     }\r
1406   }\r
1407 }\r
1408 /* Color name parser.\r
1409    X version accepts X color names, but this one\r
1410    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1411 COLORREF\r
1412 ParseColorName(char *name)\r
1413 {\r
1414   int red, green, blue, count;\r
1415   char buf[MSG_SIZ];\r
1416 \r
1417   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1418   if (count != 3) {\r
1419     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1420       &red, &green, &blue);\r
1421   }\r
1422   if (count != 3) {\r
1423     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1424     DisplayError(buf, 0);\r
1425     return RGB(0, 0, 0);\r
1426   }\r
1427   return PALETTERGB(red, green, blue);\r
1428 }\r
1429 \r
1430 void\r
1431 ParseColor(int n, char *name)\r
1432 { // for WinBoard the color is an int, which needs to be derived from the string\r
1433   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1434 }\r
1435 \r
1436 void\r
1437 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1438 {\r
1439   char *e = argValue;\r
1440   int eff = 0;\r
1441 \r
1442   while (*e) {\r
1443     if (*e == 'b')      eff |= CFE_BOLD;\r
1444     else if (*e == 'i') eff |= CFE_ITALIC;\r
1445     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1446     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1447     else if (*e == '#' || isdigit(*e)) break;\r
1448     e++;\r
1449   }\r
1450   *effects = eff;\r
1451   *color   = ParseColorName(e);\r
1452 }\r
1453 \r
1454 void\r
1455 ParseTextAttribs(ColorClass cc, char *s)\r
1456 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1457     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1458     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1459 }\r
1460 \r
1461 void\r
1462 ParseBoardSize(void *addr, char *name)\r
1463 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1464   BoardSize bs = SizeTiny;\r
1465   while (sizeInfo[bs].name != NULL) {\r
1466     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1467         *(BoardSize *)addr = bs;\r
1468         return;\r
1469     }\r
1470     bs++;\r
1471   }\r
1472   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1473 }\r
1474 \r
1475 void\r
1476 LoadAllSounds()\r
1477 { // [HGM] import name from appData first\r
1478   ColorClass cc;\r
1479   SoundClass sc;\r
1480   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1481     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1482     textAttribs[cc].sound.data = NULL;\r
1483     MyLoadSound(&textAttribs[cc].sound);\r
1484   }\r
1485   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1486     textAttribs[cc].sound.name = strdup("");\r
1487     textAttribs[cc].sound.data = NULL;\r
1488   }\r
1489   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1490     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1491     sounds[sc].data = NULL;\r
1492     MyLoadSound(&sounds[sc]);\r
1493   }\r
1494 }\r
1495 \r
1496 void\r
1497 SetCommPortDefaults()\r
1498 {\r
1499    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1500   dcb.DCBlength = sizeof(DCB);\r
1501   dcb.BaudRate = 9600;\r
1502   dcb.fBinary = TRUE;\r
1503   dcb.fParity = FALSE;\r
1504   dcb.fOutxCtsFlow = FALSE;\r
1505   dcb.fOutxDsrFlow = FALSE;\r
1506   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1507   dcb.fDsrSensitivity = FALSE;\r
1508   dcb.fTXContinueOnXoff = TRUE;\r
1509   dcb.fOutX = FALSE;\r
1510   dcb.fInX = FALSE;\r
1511   dcb.fNull = FALSE;\r
1512   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1513   dcb.fAbortOnError = FALSE;\r
1514   dcb.ByteSize = 7;\r
1515   dcb.Parity = SPACEPARITY;\r
1516   dcb.StopBits = ONESTOPBIT;\r
1517 }\r
1518 \r
1519 // [HGM] args: these three cases taken out to stay in front-end\r
1520 void\r
1521 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1522 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1523         // while the curent board size determines the element. This system should be ported to XBoard.\r
1524         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1525         int bs;\r
1526         for (bs=0; bs<NUM_SIZES; bs++) {\r
1527           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1528           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1529           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1530             ad->argName, mfp->faceName, mfp->pointSize,\r
1531             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1532             mfp->bold ? "b" : "",\r
1533             mfp->italic ? "i" : "",\r
1534             mfp->underline ? "u" : "",\r
1535             mfp->strikeout ? "s" : "",\r
1536             (int)mfp->charset);\r
1537         }\r
1538       }\r
1539 \r
1540 void\r
1541 ExportSounds()\r
1542 { // [HGM] copy the names from the internal WB variables to appData\r
1543   ColorClass cc;\r
1544   SoundClass sc;\r
1545   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1546     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1547   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1548     (&appData.soundMove)[sc] = sounds[sc].name;\r
1549 }\r
1550 \r
1551 void\r
1552 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1553 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1554         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1555         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1556           (ta->effects & CFE_BOLD) ? "b" : "",\r
1557           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1558           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1559           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1560           (ta->effects) ? " " : "",\r
1561           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1562       }\r
1563 \r
1564 void\r
1565 SaveColor(FILE *f, ArgDescriptor *ad)\r
1566 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1567         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1568         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1569           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1570 }\r
1571 \r
1572 void\r
1573 SaveBoardSize(FILE *f, char *name, void *addr)\r
1574 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1575   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1576 }\r
1577 \r
1578 void\r
1579 ParseCommPortSettings(char *s)\r
1580 { // wrapper to keep dcb from back-end\r
1581   ParseCommSettings(s, &dcb);\r
1582 }\r
1583 \r
1584 void\r
1585 GetWindowCoords()\r
1586 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1587   GetActualPlacement(hwndMain, &wpMain);\r
1588   GetActualPlacement(hwndConsole, &wpConsole);\r
1589   GetActualPlacement(commentDialog, &wpComment);\r
1590   GetActualPlacement(editTagsDialog, &wpTags);\r
1591   GetActualPlacement(gameListDialog, &wpGameList);\r
1592   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1593   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1594   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1595 }\r
1596 \r
1597 void\r
1598 PrintCommPortSettings(FILE *f, char *name)\r
1599 { // wrapper to shield back-end from DCB\r
1600       PrintCommSettings(f, name, &dcb);\r
1601 }\r
1602 \r
1603 int\r
1604 MySearchPath(char *installDir, char *name, char *fullname)\r
1605 {\r
1606   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1607   if(name[0]== '%') {\r
1608     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1609     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1610       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1611       *strchr(buf, '%') = 0;\r
1612       strcat(fullname, getenv(buf));\r
1613       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1614     }\r
1615     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1616     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1617     return (int) strlen(fullname);\r
1618   }\r
1619   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1620 }\r
1621 \r
1622 int\r
1623 MyGetFullPathName(char *name, char *fullname)\r
1624 {\r
1625   char *dummy;\r
1626   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1627 }\r
1628 \r
1629 int\r
1630 MainWindowUp()\r
1631 { // [HGM] args: allows testing if main window is realized from back-end\r
1632   return hwndMain != NULL;\r
1633 }\r
1634 \r
1635 void\r
1636 PopUpStartupDialog()\r
1637 {\r
1638     FARPROC lpProc;\r
1639     \r
1640     LoadLanguageFile(appData.language);\r
1641     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1642     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1643     FreeProcInstance(lpProc);\r
1644 }\r
1645 \r
1646 /*---------------------------------------------------------------------------*\\r
1647  *\r
1648  * GDI board drawing routines\r
1649  *\r
1650 \*---------------------------------------------------------------------------*/\r
1651 \r
1652 /* [AS] Draw square using background texture */\r
1653 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1654 {\r
1655     XFORM   x;\r
1656 \r
1657     if( mode == 0 ) {\r
1658         return; /* Should never happen! */\r
1659     }\r
1660 \r
1661     SetGraphicsMode( dst, GM_ADVANCED );\r
1662 \r
1663     switch( mode ) {\r
1664     case 1:\r
1665         /* Identity */\r
1666         break;\r
1667     case 2:\r
1668         /* X reflection */\r
1669         x.eM11 = -1.0;\r
1670         x.eM12 = 0;\r
1671         x.eM21 = 0;\r
1672         x.eM22 = 1.0;\r
1673         x.eDx = (FLOAT) dw + dx - 1;\r
1674         x.eDy = 0;\r
1675         dx = 0;\r
1676         SetWorldTransform( dst, &x );\r
1677         break;\r
1678     case 3:\r
1679         /* Y reflection */\r
1680         x.eM11 = 1.0;\r
1681         x.eM12 = 0;\r
1682         x.eM21 = 0;\r
1683         x.eM22 = -1.0;\r
1684         x.eDx = 0;\r
1685         x.eDy = (FLOAT) dh + dy - 1;\r
1686         dy = 0;\r
1687         SetWorldTransform( dst, &x );\r
1688         break;\r
1689     case 4:\r
1690         /* X/Y flip */\r
1691         x.eM11 = 0;\r
1692         x.eM12 = 1.0;\r
1693         x.eM21 = 1.0;\r
1694         x.eM22 = 0;\r
1695         x.eDx = (FLOAT) dx;\r
1696         x.eDy = (FLOAT) dy;\r
1697         dx = 0;\r
1698         dy = 0;\r
1699         SetWorldTransform( dst, &x );\r
1700         break;\r
1701     }\r
1702 \r
1703     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1704 \r
1705     x.eM11 = 1.0;\r
1706     x.eM12 = 0;\r
1707     x.eM21 = 0;\r
1708     x.eM22 = 1.0;\r
1709     x.eDx = 0;\r
1710     x.eDy = 0;\r
1711     SetWorldTransform( dst, &x );\r
1712 \r
1713     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1714 }\r
1715 \r
1716 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1717 enum {\r
1718     PM_WP = (int) WhitePawn, \r
1719     PM_WN = (int) WhiteKnight, \r
1720     PM_WB = (int) WhiteBishop, \r
1721     PM_WR = (int) WhiteRook, \r
1722     PM_WQ = (int) WhiteQueen, \r
1723     PM_WF = (int) WhiteFerz, \r
1724     PM_WW = (int) WhiteWazir, \r
1725     PM_WE = (int) WhiteAlfil, \r
1726     PM_WM = (int) WhiteMan, \r
1727     PM_WO = (int) WhiteCannon, \r
1728     PM_WU = (int) WhiteUnicorn, \r
1729     PM_WH = (int) WhiteNightrider, \r
1730     PM_WA = (int) WhiteAngel, \r
1731     PM_WC = (int) WhiteMarshall, \r
1732     PM_WAB = (int) WhiteCardinal, \r
1733     PM_WD = (int) WhiteDragon, \r
1734     PM_WL = (int) WhiteLance, \r
1735     PM_WS = (int) WhiteCobra, \r
1736     PM_WV = (int) WhiteFalcon, \r
1737     PM_WSG = (int) WhiteSilver, \r
1738     PM_WG = (int) WhiteGrasshopper, \r
1739     PM_WK = (int) WhiteKing,\r
1740     PM_BP = (int) BlackPawn, \r
1741     PM_BN = (int) BlackKnight, \r
1742     PM_BB = (int) BlackBishop, \r
1743     PM_BR = (int) BlackRook, \r
1744     PM_BQ = (int) BlackQueen, \r
1745     PM_BF = (int) BlackFerz, \r
1746     PM_BW = (int) BlackWazir, \r
1747     PM_BE = (int) BlackAlfil, \r
1748     PM_BM = (int) BlackMan,\r
1749     PM_BO = (int) BlackCannon, \r
1750     PM_BU = (int) BlackUnicorn, \r
1751     PM_BH = (int) BlackNightrider, \r
1752     PM_BA = (int) BlackAngel, \r
1753     PM_BC = (int) BlackMarshall, \r
1754     PM_BG = (int) BlackGrasshopper, \r
1755     PM_BAB = (int) BlackCardinal,\r
1756     PM_BD = (int) BlackDragon,\r
1757     PM_BL = (int) BlackLance,\r
1758     PM_BS = (int) BlackCobra,\r
1759     PM_BV = (int) BlackFalcon,\r
1760     PM_BSG = (int) BlackSilver,\r
1761     PM_BK = (int) BlackKing\r
1762 };\r
1763 \r
1764 static HFONT hPieceFont = NULL;\r
1765 static HBITMAP hPieceMask[(int) EmptySquare];\r
1766 static HBITMAP hPieceFace[(int) EmptySquare];\r
1767 static int fontBitmapSquareSize = 0;\r
1768 static char pieceToFontChar[(int) EmptySquare] =\r
1769                               { 'p', 'n', 'b', 'r', 'q', \r
1770                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1771                       'k', 'o', 'm', 'v', 't', 'w', \r
1772                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1773                                                               'l' };\r
1774 \r
1775 extern BOOL SetCharTable( char *table, const char * map );\r
1776 /* [HGM] moved to backend.c */\r
1777 \r
1778 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1779 {\r
1780     HBRUSH hbrush;\r
1781     BYTE r1 = GetRValue( color );\r
1782     BYTE g1 = GetGValue( color );\r
1783     BYTE b1 = GetBValue( color );\r
1784     BYTE r2 = r1 / 2;\r
1785     BYTE g2 = g1 / 2;\r
1786     BYTE b2 = b1 / 2;\r
1787     RECT rc;\r
1788 \r
1789     /* Create a uniform background first */\r
1790     hbrush = CreateSolidBrush( color );\r
1791     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1792     FillRect( hdc, &rc, hbrush );\r
1793     DeleteObject( hbrush );\r
1794     \r
1795     if( mode == 1 ) {\r
1796         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1797         int steps = squareSize / 2;\r
1798         int i;\r
1799 \r
1800         for( i=0; i<steps; i++ ) {\r
1801             BYTE r = r1 - (r1-r2) * i / steps;\r
1802             BYTE g = g1 - (g1-g2) * i / steps;\r
1803             BYTE b = b1 - (b1-b2) * i / steps;\r
1804 \r
1805             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1806             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1807             FillRect( hdc, &rc, hbrush );\r
1808             DeleteObject(hbrush);\r
1809         }\r
1810     }\r
1811     else if( mode == 2 ) {\r
1812         /* Diagonal gradient, good more or less for every piece */\r
1813         POINT triangle[3];\r
1814         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1815         HBRUSH hbrush_old;\r
1816         int steps = squareSize;\r
1817         int i;\r
1818 \r
1819         triangle[0].x = squareSize - steps;\r
1820         triangle[0].y = squareSize;\r
1821         triangle[1].x = squareSize;\r
1822         triangle[1].y = squareSize;\r
1823         triangle[2].x = squareSize;\r
1824         triangle[2].y = squareSize - steps;\r
1825 \r
1826         for( i=0; i<steps; i++ ) {\r
1827             BYTE r = r1 - (r1-r2) * i / steps;\r
1828             BYTE g = g1 - (g1-g2) * i / steps;\r
1829             BYTE b = b1 - (b1-b2) * i / steps;\r
1830 \r
1831             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1832             hbrush_old = SelectObject( hdc, hbrush );\r
1833             Polygon( hdc, triangle, 3 );\r
1834             SelectObject( hdc, hbrush_old );\r
1835             DeleteObject(hbrush);\r
1836             triangle[0].x++;\r
1837             triangle[2].y++;\r
1838         }\r
1839 \r
1840         SelectObject( hdc, hpen );\r
1841     }\r
1842 }\r
1843 \r
1844 /*\r
1845     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1846     seems to work ok. The main problem here is to find the "inside" of a chess\r
1847     piece: follow the steps as explained below.\r
1848 */\r
1849 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1850 {\r
1851     HBITMAP hbm;\r
1852     HBITMAP hbm_old;\r
1853     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1854     RECT rc;\r
1855     SIZE sz;\r
1856 \r
1857 \r
1858     POINT pt;\r
1859     int backColor = whitePieceColor; \r
1860     int foreColor = blackPieceColor;\r
1861     \r
1862     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1863         backColor = appData.fontBackColorWhite;\r
1864         foreColor = appData.fontForeColorWhite;\r
1865     }\r
1866     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1867         backColor = appData.fontBackColorBlack;\r
1868         foreColor = appData.fontForeColorBlack;\r
1869     }\r
1870 \r
1871     /* Mask */\r
1872     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1873 \r
1874     hbm_old = SelectObject( hdc, hbm );\r
1875 \r
1876     rc.left = 0;\r
1877     rc.top = 0;\r
1878     rc.right = squareSize;\r
1879     rc.bottom = squareSize;\r
1880 \r
1881     /* Step 1: background is now black */\r
1882     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1883 \r
1884     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1885 \r
1886     pt.x = (squareSize - sz.cx) / 2;\r
1887     pt.y = (squareSize - sz.cy) / 2;\r
1888 \r
1889     SetBkMode( hdc, TRANSPARENT );\r
1890     SetTextColor( hdc, chroma );\r
1891     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1892     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1893 \r
1894     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1895     /* Step 3: the area outside the piece is filled with white */\r
1896 //    FloodFill( hdc, 0, 0, chroma );\r
1897     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1898     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1899     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1900     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1901     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1902     /* \r
1903         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1904         but if the start point is not inside the piece we're lost!\r
1905         There should be a better way to do this... if we could create a region or path\r
1906         from the fill operation we would be fine for example.\r
1907     */\r
1908 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1909     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1910 \r
1911     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1912         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1913         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1914 \r
1915         SelectObject( dc2, bm2 );\r
1916         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1917         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1918         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1919         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1920         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1921 \r
1922         DeleteDC( dc2 );\r
1923         DeleteObject( bm2 );\r
1924     }\r
1925 \r
1926     SetTextColor( hdc, 0 );\r
1927     /* \r
1928         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1929         draw the piece again in black for safety.\r
1930     */\r
1931     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1932 \r
1933     SelectObject( hdc, hbm_old );\r
1934 \r
1935     if( hPieceMask[index] != NULL ) {\r
1936         DeleteObject( hPieceMask[index] );\r
1937     }\r
1938 \r
1939     hPieceMask[index] = hbm;\r
1940 \r
1941     /* Face */\r
1942     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1943 \r
1944     SelectObject( hdc, hbm );\r
1945 \r
1946     {\r
1947         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1948         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1949         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1950 \r
1951         SelectObject( dc1, hPieceMask[index] );\r
1952         SelectObject( dc2, bm2 );\r
1953         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1954         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1955         \r
1956         /* \r
1957             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1958             the piece background and deletes (makes transparent) the rest.\r
1959             Thanks to that mask, we are free to paint the background with the greates\r
1960             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1961             We use this, to make gradients and give the pieces a "roundish" look.\r
1962         */\r
1963         SetPieceBackground( hdc, backColor, 2 );\r
1964         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1965 \r
1966         DeleteDC( dc2 );\r
1967         DeleteDC( dc1 );\r
1968         DeleteObject( bm2 );\r
1969     }\r
1970 \r
1971     SetTextColor( hdc, foreColor );\r
1972     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1973 \r
1974     SelectObject( hdc, hbm_old );\r
1975 \r
1976     if( hPieceFace[index] != NULL ) {\r
1977         DeleteObject( hPieceFace[index] );\r
1978     }\r
1979 \r
1980     hPieceFace[index] = hbm;\r
1981 }\r
1982 \r
1983 static int TranslatePieceToFontPiece( int piece )\r
1984 {\r
1985     switch( piece ) {\r
1986     case BlackPawn:\r
1987         return PM_BP;\r
1988     case BlackKnight:\r
1989         return PM_BN;\r
1990     case BlackBishop:\r
1991         return PM_BB;\r
1992     case BlackRook:\r
1993         return PM_BR;\r
1994     case BlackQueen:\r
1995         return PM_BQ;\r
1996     case BlackKing:\r
1997         return PM_BK;\r
1998     case WhitePawn:\r
1999         return PM_WP;\r
2000     case WhiteKnight:\r
2001         return PM_WN;\r
2002     case WhiteBishop:\r
2003         return PM_WB;\r
2004     case WhiteRook:\r
2005         return PM_WR;\r
2006     case WhiteQueen:\r
2007         return PM_WQ;\r
2008     case WhiteKing:\r
2009         return PM_WK;\r
2010 \r
2011     case BlackAngel:\r
2012         return PM_BA;\r
2013     case BlackMarshall:\r
2014         return PM_BC;\r
2015     case BlackFerz:\r
2016         return PM_BF;\r
2017     case BlackNightrider:\r
2018         return PM_BH;\r
2019     case BlackAlfil:\r
2020         return PM_BE;\r
2021     case BlackWazir:\r
2022         return PM_BW;\r
2023     case BlackUnicorn:\r
2024         return PM_BU;\r
2025     case BlackCannon:\r
2026         return PM_BO;\r
2027     case BlackGrasshopper:\r
2028         return PM_BG;\r
2029     case BlackMan:\r
2030         return PM_BM;\r
2031     case BlackSilver:\r
2032         return PM_BSG;\r
2033     case BlackLance:\r
2034         return PM_BL;\r
2035     case BlackFalcon:\r
2036         return PM_BV;\r
2037     case BlackCobra:\r
2038         return PM_BS;\r
2039     case BlackCardinal:\r
2040         return PM_BAB;\r
2041     case BlackDragon:\r
2042         return PM_BD;\r
2043 \r
2044     case WhiteAngel:\r
2045         return PM_WA;\r
2046     case WhiteMarshall:\r
2047         return PM_WC;\r
2048     case WhiteFerz:\r
2049         return PM_WF;\r
2050     case WhiteNightrider:\r
2051         return PM_WH;\r
2052     case WhiteAlfil:\r
2053         return PM_WE;\r
2054     case WhiteWazir:\r
2055         return PM_WW;\r
2056     case WhiteUnicorn:\r
2057         return PM_WU;\r
2058     case WhiteCannon:\r
2059         return PM_WO;\r
2060     case WhiteGrasshopper:\r
2061         return PM_WG;\r
2062     case WhiteMan:\r
2063         return PM_WM;\r
2064     case WhiteSilver:\r
2065         return PM_WSG;\r
2066     case WhiteLance:\r
2067         return PM_WL;\r
2068     case WhiteFalcon:\r
2069         return PM_WV;\r
2070     case WhiteCobra:\r
2071         return PM_WS;\r
2072     case WhiteCardinal:\r
2073         return PM_WAB;\r
2074     case WhiteDragon:\r
2075         return PM_WD;\r
2076     }\r
2077 \r
2078     return 0;\r
2079 }\r
2080 \r
2081 void CreatePiecesFromFont()\r
2082 {\r
2083     LOGFONT lf;\r
2084     HDC hdc_window = NULL;\r
2085     HDC hdc = NULL;\r
2086     HFONT hfont_old;\r
2087     int fontHeight;\r
2088     int i;\r
2089 \r
2090     if( fontBitmapSquareSize < 0 ) {\r
2091         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2092         return;\r
2093     }\r
2094 \r
2095     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2096             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2097         fontBitmapSquareSize = -1;\r
2098         return;\r
2099     }\r
2100 \r
2101     if( fontBitmapSquareSize != squareSize ) {\r
2102         hdc_window = GetDC( hwndMain );\r
2103         hdc = CreateCompatibleDC( hdc_window );\r
2104 \r
2105         if( hPieceFont != NULL ) {\r
2106             DeleteObject( hPieceFont );\r
2107         }\r
2108         else {\r
2109             for( i=0; i<=(int)BlackKing; i++ ) {\r
2110                 hPieceMask[i] = NULL;\r
2111                 hPieceFace[i] = NULL;\r
2112             }\r
2113         }\r
2114 \r
2115         fontHeight = 75;\r
2116 \r
2117         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2118             fontHeight = appData.fontPieceSize;\r
2119         }\r
2120 \r
2121         fontHeight = (fontHeight * squareSize) / 100;\r
2122 \r
2123         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2124         lf.lfWidth = 0;\r
2125         lf.lfEscapement = 0;\r
2126         lf.lfOrientation = 0;\r
2127         lf.lfWeight = FW_NORMAL;\r
2128         lf.lfItalic = 0;\r
2129         lf.lfUnderline = 0;\r
2130         lf.lfStrikeOut = 0;\r
2131         lf.lfCharSet = DEFAULT_CHARSET;\r
2132         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2133         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2134         lf.lfQuality = PROOF_QUALITY;\r
2135         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2136         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2137         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2138 \r
2139         hPieceFont = CreateFontIndirect( &lf );\r
2140 \r
2141         if( hPieceFont == NULL ) {\r
2142             fontBitmapSquareSize = -2;\r
2143         }\r
2144         else {\r
2145             /* Setup font-to-piece character table */\r
2146             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2147                 /* No (or wrong) global settings, try to detect the font */\r
2148                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2149                     /* Alpha */\r
2150                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2151                 }\r
2152                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2153                     /* DiagramTT* family */\r
2154                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2155                 }\r
2156                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2157                     /* Fairy symbols */\r
2158                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2159                 }\r
2160                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2161                     /* Good Companion (Some characters get warped as literal :-( */\r
2162                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2163                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2164                     SetCharTable(pieceToFontChar, s);\r
2165                 }\r
2166                 else {\r
2167                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2168                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2169                 }\r
2170             }\r
2171 \r
2172             /* Create bitmaps */\r
2173             hfont_old = SelectObject( hdc, hPieceFont );\r
2174             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2175                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2176                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2177 \r
2178             SelectObject( hdc, hfont_old );\r
2179 \r
2180             fontBitmapSquareSize = squareSize;\r
2181         }\r
2182     }\r
2183 \r
2184     if( hdc != NULL ) {\r
2185         DeleteDC( hdc );\r
2186     }\r
2187 \r
2188     if( hdc_window != NULL ) {\r
2189         ReleaseDC( hwndMain, hdc_window );\r
2190     }\r
2191 }\r
2192 \r
2193 HBITMAP\r
2194 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2195 {\r
2196   char name[128], buf[MSG_SIZ];\r
2197 \r
2198     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2199   if(appData.pieceDirectory[0]) {\r
2200     HBITMAP res;\r
2201     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2202     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2203     if(res) return res;\r
2204   }\r
2205   if (gameInfo.event &&\r
2206       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2207       strcmp(name, "k80s") == 0) {\r
2208     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2209   }\r
2210   return LoadBitmap(hinst, name);\r
2211 }\r
2212 \r
2213 \r
2214 /* Insert a color into the program's logical palette\r
2215    structure.  This code assumes the given color is\r
2216    the result of the RGB or PALETTERGB macro, and it\r
2217    knows how those macros work (which is documented).\r
2218 */\r
2219 VOID\r
2220 InsertInPalette(COLORREF color)\r
2221 {\r
2222   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2223 \r
2224   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2225     DisplayFatalError(_("Too many colors"), 0, 1);\r
2226     pLogPal->palNumEntries--;\r
2227     return;\r
2228   }\r
2229 \r
2230   pe->peFlags = (char) 0;\r
2231   pe->peRed = (char) (0xFF & color);\r
2232   pe->peGreen = (char) (0xFF & (color >> 8));\r
2233   pe->peBlue = (char) (0xFF & (color >> 16));\r
2234   return;\r
2235 }\r
2236 \r
2237 \r
2238 VOID\r
2239 InitDrawingColors()\r
2240 {\r
2241   int i;\r
2242   if (pLogPal == NULL) {\r
2243     /* Allocate enough memory for a logical palette with\r
2244      * PALETTESIZE entries and set the size and version fields\r
2245      * of the logical palette structure.\r
2246      */\r
2247     pLogPal = (NPLOGPALETTE)\r
2248       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2249                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2250     pLogPal->palVersion    = 0x300;\r
2251   }\r
2252   pLogPal->palNumEntries = 0;\r
2253 \r
2254   InsertInPalette(lightSquareColor);\r
2255   InsertInPalette(darkSquareColor);\r
2256   InsertInPalette(whitePieceColor);\r
2257   InsertInPalette(blackPieceColor);\r
2258   InsertInPalette(highlightSquareColor);\r
2259   InsertInPalette(premoveHighlightColor);\r
2260 \r
2261   /*  create a logical color palette according the information\r
2262    *  in the LOGPALETTE structure.\r
2263    */\r
2264   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2265 \r
2266   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2267   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2268   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2269   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2270   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2271   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2272   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2273     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2274 \r
2275    /* [AS] Force rendering of the font-based pieces */\r
2276   if( fontBitmapSquareSize > 0 ) {\r
2277     fontBitmapSquareSize = 0;\r
2278   }\r
2279 }\r
2280 \r
2281 \r
2282 int\r
2283 BoardWidth(int boardSize, int n)\r
2284 { /* [HGM] argument n added to allow different width and height */\r
2285   int lineGap = sizeInfo[boardSize].lineGap;\r
2286 \r
2287   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2288       lineGap = appData.overrideLineGap;\r
2289   }\r
2290 \r
2291   return (n + 1) * lineGap +\r
2292           n * sizeInfo[boardSize].squareSize;\r
2293 }\r
2294 \r
2295 /* Respond to board resize by dragging edge */\r
2296 VOID\r
2297 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2298 {\r
2299   BoardSize newSize = NUM_SIZES - 1;\r
2300   static int recurse = 0;\r
2301   if (IsIconic(hwndMain)) return;\r
2302   if (recurse > 0) return;\r
2303   recurse++;\r
2304   while (newSize > 0) {\r
2305         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2306         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2307            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2308     newSize--;\r
2309   } \r
2310   boardSize = newSize;\r
2311   InitDrawingSizes(boardSize, flags);\r
2312   recurse--;\r
2313 }\r
2314 \r
2315 \r
2316 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2317 \r
2318 VOID\r
2319 InitDrawingSizes(BoardSize boardSize, int flags)\r
2320 {\r
2321   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2322   ChessSquare piece;\r
2323   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2324   HDC hdc;\r
2325   SIZE clockSize, messageSize;\r
2326   HFONT oldFont;\r
2327   char buf[MSG_SIZ];\r
2328   char *str;\r
2329   HMENU hmenu = GetMenu(hwndMain);\r
2330   RECT crect, wrect, oldRect;\r
2331   int offby;\r
2332   LOGBRUSH logbrush;\r
2333   VariantClass v = gameInfo.variant;\r
2334 \r
2335   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2336   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2337 \r
2338   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2339   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2340   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2341   oldBoardSize = boardSize;\r
2342 \r
2343   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2344   { // correct board size to one where built-in pieces exist\r
2345     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2346        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2347 \r
2348       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2349       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2350       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2351       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2352       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2353                                    boardSize = SizeMiddling;\r
2354     }\r
2355   }\r
2356   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2357 \r
2358   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2359   oldRect.top = wpMain.y;\r
2360   oldRect.right = wpMain.x + wpMain.width;\r
2361   oldRect.bottom = wpMain.y + wpMain.height;\r
2362 \r
2363   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2364   smallLayout = sizeInfo[boardSize].smallLayout;\r
2365   squareSize = sizeInfo[boardSize].squareSize;\r
2366   lineGap = sizeInfo[boardSize].lineGap;\r
2367   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2368   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2369 \r
2370   // [HGM] decide on tininess based on total board width rather than square size\r
2371   tinyLayout = squareSize * (BOARD_WIDTH);\r
2372   tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;\r
2373 \r
2374   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2375       lineGap = appData.overrideLineGap;\r
2376   }\r
2377 \r
2378   if (tinyLayout != oldTinyLayout) {\r
2379     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2380     if (tinyLayout == 2) {\r
2381       style &= ~WS_SYSMENU;\r
2382       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2383                  "&Minimize\tCtrl+F4");\r
2384     } else {\r
2385       style |= WS_SYSMENU;\r
2386       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2387     }\r
2388     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2389 \r
2390     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2391       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2392         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2393     }\r
2394     DrawMenuBar(hwndMain);\r
2395   }\r
2396 \r
2397   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2398   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2399 \r
2400   /* Get text area sizes */\r
2401   hdc = GetDC(hwndMain);\r
2402   if (appData.clockMode) {\r
2403     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2404   } else {\r
2405     snprintf(buf, MSG_SIZ, _("White"));\r
2406   }\r
2407   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2408   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2409   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2410   str = _("We only care about the height here");\r
2411   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2412   SelectObject(hdc, oldFont);\r
2413   ReleaseDC(hwndMain, hdc);\r
2414 \r
2415   /* Compute where everything goes */\r
2416   if((first.programLogo || second.programLogo) && tinyLayout != 2) {\r
2417         /* [HGM] logo: if either logo is on, reserve space for it */\r
2418         logoHeight =  2*clockSize.cy;\r
2419         leftLogoRect.left   = OUTER_MARGIN;\r
2420         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2421         leftLogoRect.top    = OUTER_MARGIN;\r
2422         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2423 \r
2424         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2425         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2426         rightLogoRect.top    = OUTER_MARGIN;\r
2427         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2428 \r
2429 \r
2430     whiteRect.left = leftLogoRect.right;\r
2431     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2432     whiteRect.top = OUTER_MARGIN;\r
2433     whiteRect.bottom = whiteRect.top + logoHeight;\r
2434 \r
2435     blackRect.right = rightLogoRect.left;\r
2436     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2437     blackRect.top = whiteRect.top;\r
2438     blackRect.bottom = whiteRect.bottom;\r
2439   } else {\r
2440     whiteRect.left = OUTER_MARGIN;\r
2441     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2442     whiteRect.top = OUTER_MARGIN;\r
2443     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2444 \r
2445     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2446     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2447     blackRect.top = whiteRect.top;\r
2448     blackRect.bottom = whiteRect.bottom;\r
2449 \r
2450     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2451   }\r
2452 \r
2453   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2454   if (appData.showButtonBar) {\r
2455     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2456       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2457   } else {\r
2458     messageRect.right = OUTER_MARGIN + boardWidth;\r
2459   }\r
2460   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2461   messageRect.bottom = messageRect.top + messageSize.cy;\r
2462 \r
2463   boardRect.left = OUTER_MARGIN;\r
2464   boardRect.right = boardRect.left + boardWidth;\r
2465   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2466   boardRect.bottom = boardRect.top + boardHeight;\r
2467 \r
2468   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2469   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2470   oldTinyLayout = tinyLayout;\r
2471   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2472   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2473     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2474   winW *= 1 + twoBoards;\r
2475   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2476   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2477   wpMain.height = winH; //       without disturbing window attachments\r
2478   GetWindowRect(hwndMain, &wrect);\r
2479   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2480                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2481 \r
2482   // [HGM] placement: let attached windows follow size change.\r
2483   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2484   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2485   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2486   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2487   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2488 \r
2489   /* compensate if menu bar wrapped */\r
2490   GetClientRect(hwndMain, &crect);\r
2491   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2492   wpMain.height += offby;\r
2493   switch (flags) {\r
2494   case WMSZ_TOPLEFT:\r
2495     SetWindowPos(hwndMain, NULL, \r
2496                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2497                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2498     break;\r
2499 \r
2500   case WMSZ_TOPRIGHT:\r
2501   case WMSZ_TOP:\r
2502     SetWindowPos(hwndMain, NULL, \r
2503                  wrect.left, wrect.bottom - wpMain.height, \r
2504                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2505     break;\r
2506 \r
2507   case WMSZ_BOTTOMLEFT:\r
2508   case WMSZ_LEFT:\r
2509     SetWindowPos(hwndMain, NULL, \r
2510                  wrect.right - wpMain.width, wrect.top, \r
2511                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2512     break;\r
2513 \r
2514   case WMSZ_BOTTOMRIGHT:\r
2515   case WMSZ_BOTTOM:\r
2516   case WMSZ_RIGHT:\r
2517   default:\r
2518     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2519                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2520     break;\r
2521   }\r
2522 \r
2523   hwndPause = NULL;\r
2524   for (i = 0; i < N_BUTTONS; i++) {\r
2525     if (buttonDesc[i].hwnd != NULL) {\r
2526       DestroyWindow(buttonDesc[i].hwnd);\r
2527       buttonDesc[i].hwnd = NULL;\r
2528     }\r
2529     if (appData.showButtonBar) {\r
2530       buttonDesc[i].hwnd =\r
2531         CreateWindow("BUTTON", buttonDesc[i].label,\r
2532                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2533                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2534                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2535                      (HMENU) buttonDesc[i].id,\r
2536                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2537       if (tinyLayout == 2) {\r
2538         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2539                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2540                     MAKELPARAM(FALSE, 0));\r
2541       }\r
2542       if (buttonDesc[i].id == IDM_Pause)\r
2543         hwndPause = buttonDesc[i].hwnd;\r
2544       buttonDesc[i].wndproc = (WNDPROC)\r
2545         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2546     }\r
2547   }\r
2548   if (gridPen != NULL) DeleteObject(gridPen);\r
2549   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2550   if (premovePen != NULL) DeleteObject(premovePen);\r
2551   if (lineGap != 0) {\r
2552     logbrush.lbStyle = BS_SOLID;\r
2553     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2554     gridPen =\r
2555       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2556                    lineGap, &logbrush, 0, NULL);\r
2557     logbrush.lbColor = highlightSquareColor;\r
2558     highlightPen =\r
2559       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2560                    lineGap, &logbrush, 0, NULL);\r
2561 \r
2562     logbrush.lbColor = premoveHighlightColor; \r
2563     premovePen =\r
2564       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2565                    lineGap, &logbrush, 0, NULL);\r
2566 \r
2567     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2568     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2569       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2570       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2571         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2572       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2573         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2574       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2575     }\r
2576     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2577       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2578       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2579         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2580         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2581       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2582         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2583       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2584     }\r
2585   }\r
2586 \r
2587   /* [HGM] Licensing requirement */\r
2588 #ifdef GOTHIC\r
2589   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2590 #endif\r
2591 #ifdef FALCON\r
2592   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2593 #endif\r
2594   GothicPopUp( "", VariantNormal);\r
2595 \r
2596 \r
2597 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2598 \r
2599   /* Load piece bitmaps for this board size */\r
2600   for (i=0; i<=2; i++) {\r
2601     for (piece = WhitePawn;\r
2602          (int) piece < (int) BlackPawn;\r
2603          piece = (ChessSquare) ((int) piece + 1)) {\r
2604       if (pieceBitmap[i][piece] != NULL)\r
2605         DeleteObject(pieceBitmap[i][piece]);\r
2606       pieceBitmap[i][piece] = NULL;\r
2607     }\r
2608   }\r
2609 \r
2610   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2611 \r
2612   // Orthodox Chess pieces\r
2613   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2614   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2615   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2616   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2617   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2618   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2619   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2620   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2621   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2622   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2623   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2624   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2625   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2626   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2627   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2628   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2629     // in Shogi, Hijack the unused Queen for Lance\r
2630     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2631     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2632     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2633   } else {\r
2634     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2635     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2636     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2637   }\r
2638 \r
2639   if(squareSize <= 72 && squareSize >= 33) { \r
2640     /* A & C are available in most sizes now */\r
2641     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2642       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2643       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2644       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2645       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2646       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2647       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2648       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2649       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2650       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2651       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2652       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2653       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2654     } else { // Smirf-like\r
2655       if(gameInfo.variant == VariantSChess) {\r
2656         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2657         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2658         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2659       } else {\r
2660         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2661         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2662         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2663       }\r
2664     }\r
2665     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2666       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2667       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2668       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2669     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2670       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2671       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2672       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2673     } else { // WinBoard standard\r
2674       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2675       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2676       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2677     }\r
2678   }\r
2679 \r
2680 \r
2681   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2682     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2683     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2684     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2685     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2686     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2687     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2688     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2689     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2690     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2691     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2692     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2693     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2694     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2695     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2696     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2697     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2698     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2699     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2700     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2701     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2702     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2703     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2704     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2705     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2706     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2707     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2708     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2709     pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2710     pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2711     pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2712     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2713     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2714     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2715     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2716     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2717     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2718     pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2719     pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2720     pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2721     pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");\r
2722     pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");\r
2723     pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");\r
2724     pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");\r
2725     pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");\r
2726     pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");\r
2727     pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");\r
2728     pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");\r
2729     pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2730 \r
2731     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
2732       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2733       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2734       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2735       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2736       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2737       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2738       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2739       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2740       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2741       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2742       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2743       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2744     } else {\r
2745       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2746       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2747       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2748       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2749       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2750       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2751       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2752       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2753       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2754       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2755       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2756       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2757     }\r
2758 \r
2759   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2760     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2761     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2762     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2763     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2764     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2765     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2766     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2767     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2768     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2769     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2770     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2771     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2772     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2773     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2774   }\r
2775 \r
2776 \r
2777   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2778   /* special Shogi support in this size */\r
2779   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2780       for (piece = WhitePawn;\r
2781            (int) piece < (int) BlackPawn;\r
2782            piece = (ChessSquare) ((int) piece + 1)) {\r
2783         if (pieceBitmap[i][piece] != NULL)\r
2784           DeleteObject(pieceBitmap[i][piece]);\r
2785       }\r
2786     }\r
2787   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2788   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2789   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2790   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2791   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2792   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2793   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2794   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2795   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2796   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2797   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2798   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2799   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2800   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2801   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2802   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2803   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2804   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2805   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2806   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2807   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2808   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2809   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2810   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2811   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2812   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2813   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2814   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2815   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2816   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2817   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2818   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2819   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2820   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2821   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2822   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2823   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2824   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2825   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2826   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2827   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2828   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2829   minorSize = 0;\r
2830   }\r
2831 \r
2832   if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention\r
2833     char buf[MSG_SIZ];\r
2834     if(pieceBitmap[0][i]) continue;\r
2835     snprintf(buf, MSG_SIZ, "piece%d_", i);\r
2836     pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");\r
2837     pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");\r
2838     pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");\r
2839   }\r
2840 }\r
2841 \r
2842 HBITMAP\r
2843 PieceBitmap(ChessSquare p, int kind)\r
2844 {\r
2845   if ((int) p >= (int) BlackPawn)\r
2846     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2847 \r
2848   return pieceBitmap[kind][(int) p];\r
2849 }\r
2850 \r
2851 /***************************************************************/\r
2852 \r
2853 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2854 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2855 /*\r
2856 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2857 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2858 */\r
2859 \r
2860 VOID\r
2861 SquareToPos(int row, int column, int * x, int * y)\r
2862 {\r
2863   if (flipView) {\r
2864     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2865     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2866   } else {\r
2867     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2868     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2869   }\r
2870 }\r
2871 \r
2872 VOID\r
2873 DrawCoordsOnDC(HDC hdc)\r
2874 {\r
2875   static char files[] = "0123456789012345678901221098765432109876543210";\r
2876   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2877   char str[2] = { NULLCHAR, NULLCHAR };\r
2878   int oldMode, oldAlign, x, y, start, i;\r
2879   HFONT oldFont;\r
2880   HBRUSH oldBrush;\r
2881 \r
2882   if (!appData.showCoords)\r
2883     return;\r
2884 \r
2885   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2886 \r
2887   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2888   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2889   oldAlign = GetTextAlign(hdc);\r
2890   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2891 \r
2892   y = boardRect.top + lineGap;\r
2893   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2894 \r
2895   if(border) {\r
2896     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2897     x += border - lineGap - 4; y += squareSize - 6;\r
2898   } else\r
2899   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2900   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2901     str[0] = files[start + i];\r
2902     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2903     y += squareSize + lineGap;\r
2904   }\r
2905 \r
2906   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2907 \r
2908   if(border) {\r
2909     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2910     x += -border + 4; y += border - squareSize + 6;\r
2911   } else\r
2912   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2913   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2914     str[0] = ranks[start + i];\r
2915     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2916     x += squareSize + lineGap;\r
2917   }    \r
2918 \r
2919   SelectObject(hdc, oldBrush);\r
2920   SetBkMode(hdc, oldMode);\r
2921   SetTextAlign(hdc, oldAlign);\r
2922   SelectObject(hdc, oldFont);\r
2923 }\r
2924 \r
2925 VOID\r
2926 DrawGridOnDC(HDC hdc)\r
2927 {\r
2928   HPEN oldPen;\r
2929  \r
2930   if (lineGap != 0) {\r
2931     oldPen = SelectObject(hdc, gridPen);\r
2932     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2933     SelectObject(hdc, oldPen);\r
2934   }\r
2935 }\r
2936 \r
2937 #define HIGHLIGHT_PEN 0\r
2938 #define PREMOVE_PEN   1\r
2939 \r
2940 VOID\r
2941 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2942 {\r
2943   int x1, y1;\r
2944   HPEN oldPen, hPen;\r
2945   if (lineGap == 0) return;\r
2946   if (flipView) {\r
2947     x1 = boardRect.left +\r
2948       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2949     y1 = boardRect.top +\r
2950       lineGap/2 + y * (squareSize + lineGap) + border;\r
2951   } else {\r
2952     x1 = boardRect.left +\r
2953       lineGap/2 + x * (squareSize + lineGap) + border;\r
2954     y1 = boardRect.top +\r
2955       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2956   }\r
2957   hPen = pen ? premovePen : highlightPen;\r
2958   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2959   MoveToEx(hdc, x1, y1, NULL);\r
2960   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2961   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2962   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2963   LineTo(hdc, x1, y1);\r
2964   SelectObject(hdc, oldPen);\r
2965 }\r
2966 \r
2967 VOID\r
2968 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2969 {\r
2970   int i;\r
2971   for (i=0; i<2; i++) {\r
2972     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2973       DrawHighlightOnDC(hdc, TRUE,\r
2974                         h->sq[i].x, h->sq[i].y,\r
2975                         pen);\r
2976   }\r
2977 }\r
2978 \r
2979 /* Note: sqcolor is used only in monoMode */\r
2980 /* Note that this code is largely duplicated in woptions.c,\r
2981    function DrawSampleSquare, so that needs to be updated too */\r
2982 VOID\r
2983 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2984 {\r
2985   HBITMAP oldBitmap;\r
2986   HBRUSH oldBrush;\r
2987   int tmpSize;\r
2988 \r
2989   if (appData.blindfold) return;\r
2990 \r
2991   /* [AS] Use font-based pieces if needed */\r
2992   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2993     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2994     CreatePiecesFromFont();\r
2995 \r
2996     if( fontBitmapSquareSize == squareSize ) {\r
2997         int index = TranslatePieceToFontPiece(piece);\r
2998 \r
2999         SelectObject( tmphdc, hPieceMask[ index ] );\r
3000 \r
3001       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3002         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
3003       else\r
3004         BitBlt( hdc,\r
3005             x, y,\r
3006             squareSize, squareSize,\r
3007             tmphdc,\r
3008             0, 0,\r
3009             SRCAND );\r
3010 \r
3011         SelectObject( tmphdc, hPieceFace[ index ] );\r
3012 \r
3013       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
3014         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
3015       else\r
3016         BitBlt( hdc,\r
3017             x, y,\r
3018             squareSize, squareSize,\r
3019             tmphdc,\r
3020             0, 0,\r
3021             SRCPAINT );\r
3022 \r
3023         return;\r
3024     }\r
3025   }\r
3026 \r
3027   if (appData.monoMode) {\r
3028     SelectObject(tmphdc, PieceBitmap(piece, \r
3029       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3030     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3031            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3032   } else {\r
3033     HBRUSH xBrush = whitePieceBrush;\r
3034     tmpSize = squareSize;\r
3035     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
3036     if(minorSize &&\r
3037         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3038          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3039       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3040       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3041       x += (squareSize - minorSize)>>1;\r
3042       y += squareSize - minorSize - 2;\r
3043       tmpSize = minorSize;\r
3044     }\r
3045     if (color || appData.allWhite ) {\r
3046       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3047       if( color )\r
3048               oldBrush = SelectObject(hdc, xBrush);\r
3049       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3050       if(appData.upsideDown && color==flipView)\r
3051         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3052       else\r
3053         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3054       /* Use black for outline of white pieces */\r
3055       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3056       if(appData.upsideDown && color==flipView)\r
3057         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3058       else\r
3059         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3060     } else if(appData.pieceDirectory[0]) {\r
3061       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3062       oldBrush = SelectObject(hdc, xBrush);\r
3063       if(appData.upsideDown && color==flipView)\r
3064         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3065       else\r
3066         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3067       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3068       if(appData.upsideDown && color==flipView)\r
3069         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3070       else\r
3071         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3072     } else {\r
3073       /* Use square color for details of black pieces */\r
3074       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3075       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3076       if(appData.upsideDown && !flipView)\r
3077         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3078       else\r
3079         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3080     }\r
3081     SelectObject(hdc, oldBrush);\r
3082     SelectObject(tmphdc, oldBitmap);\r
3083   }\r
3084 }\r
3085 \r
3086 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3087 int GetBackTextureMode( int algo )\r
3088 {\r
3089     int result = BACK_TEXTURE_MODE_DISABLED;\r
3090 \r
3091     switch( algo ) \r
3092     {\r
3093         case BACK_TEXTURE_MODE_PLAIN:\r
3094             result = 1; /* Always use identity map */\r
3095             break;\r
3096         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3097             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3098             break;\r
3099     }\r
3100 \r
3101     return result;\r
3102 }\r
3103 \r
3104 /* \r
3105     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3106     to handle redraws cleanly (as random numbers would always be different).\r
3107 */\r
3108 VOID RebuildTextureSquareInfo()\r
3109 {\r
3110     BITMAP bi;\r
3111     int lite_w = 0;\r
3112     int lite_h = 0;\r
3113     int dark_w = 0;\r
3114     int dark_h = 0;\r
3115     int row;\r
3116     int col;\r
3117 \r
3118     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3119 \r
3120     if( liteBackTexture != NULL ) {\r
3121         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3122             lite_w = bi.bmWidth;\r
3123             lite_h = bi.bmHeight;\r
3124         }\r
3125     }\r
3126 \r
3127     if( darkBackTexture != NULL ) {\r
3128         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3129             dark_w = bi.bmWidth;\r
3130             dark_h = bi.bmHeight;\r
3131         }\r
3132     }\r
3133 \r
3134     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3135         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3136             if( (col + row) & 1 ) {\r
3137                 /* Lite square */\r
3138                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3139                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3140                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3141                   else\r
3142                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3143                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3144                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3145                   else\r
3146                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3147                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3148                 }\r
3149             }\r
3150             else {\r
3151                 /* Dark square */\r
3152                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3153                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3154                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3155                   else\r
3156                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3157                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3158                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3159                   else\r
3160                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3161                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3162                 }\r
3163             }\r
3164         }\r
3165     }\r
3166 }\r
3167 \r
3168 /* [AS] Arrow highlighting support */\r
3169 \r
3170 static double A_WIDTH = 5; /* Width of arrow body */\r
3171 \r
3172 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3173 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3174 \r
3175 static double Sqr( double x )\r
3176 {\r
3177     return x*x;\r
3178 }\r
3179 \r
3180 static int Round( double x )\r
3181 {\r
3182     return (int) (x + 0.5);\r
3183 }\r
3184 \r
3185 /* Draw an arrow between two points using current settings */\r
3186 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3187 {\r
3188     POINT arrow[7];\r
3189     double dx, dy, j, k, x, y;\r
3190 \r
3191     if( d_x == s_x ) {\r
3192         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3193 \r
3194         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3195         arrow[0].y = s_y;\r
3196 \r
3197         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3198         arrow[1].y = d_y - h;\r
3199 \r
3200         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3201         arrow[2].y = d_y - h;\r
3202 \r
3203         arrow[3].x = d_x;\r
3204         arrow[3].y = d_y;\r
3205 \r
3206         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3207         arrow[5].y = d_y - h;\r
3208 \r
3209         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3210         arrow[4].y = d_y - h;\r
3211 \r
3212         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3213         arrow[6].y = s_y;\r
3214     }\r
3215     else if( d_y == s_y ) {\r
3216         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3217 \r
3218         arrow[0].x = s_x;\r
3219         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3220 \r
3221         arrow[1].x = d_x - w;\r
3222         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3223 \r
3224         arrow[2].x = d_x - w;\r
3225         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3226 \r
3227         arrow[3].x = d_x;\r
3228         arrow[3].y = d_y;\r
3229 \r
3230         arrow[5].x = d_x - w;\r
3231         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3232 \r
3233         arrow[4].x = d_x - w;\r
3234         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3235 \r
3236         arrow[6].x = s_x;\r
3237         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3238     }\r
3239     else {\r
3240         /* [AS] Needed a lot of paper for this! :-) */\r
3241         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3242         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3243   \r
3244         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3245 \r
3246         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3247 \r
3248         x = s_x;\r
3249         y = s_y;\r
3250 \r
3251         arrow[0].x = Round(x - j);\r
3252         arrow[0].y = Round(y + j*dx);\r
3253 \r
3254         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3255         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3256 \r
3257         if( d_x > s_x ) {\r
3258             x = (double) d_x - k;\r
3259             y = (double) d_y - k*dy;\r
3260         }\r
3261         else {\r
3262             x = (double) d_x + k;\r
3263             y = (double) d_y + k*dy;\r
3264         }\r
3265 \r
3266         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3267 \r
3268         arrow[6].x = Round(x - j);\r
3269         arrow[6].y = Round(y + j*dx);\r
3270 \r
3271         arrow[2].x = Round(arrow[6].x + 2*j);\r
3272         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3273 \r
3274         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3275         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3276 \r
3277         arrow[4].x = d_x;\r
3278         arrow[4].y = d_y;\r
3279 \r
3280         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3281         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3282     }\r
3283 \r
3284     Polygon( hdc, arrow, 7 );\r
3285 }\r
3286 \r
3287 /* [AS] Draw an arrow between two squares */\r
3288 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3289 {\r
3290     int s_x, s_y, d_x, d_y;\r
3291     HPEN hpen;\r
3292     HPEN holdpen;\r
3293     HBRUSH hbrush;\r
3294     HBRUSH holdbrush;\r
3295     LOGBRUSH stLB;\r
3296 \r
3297     if( s_col == d_col && s_row == d_row ) {\r
3298         return;\r
3299     }\r
3300 \r
3301     /* Get source and destination points */\r
3302     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3303     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3304 \r
3305     if( d_y > s_y ) {\r
3306         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3307     }\r
3308     else if( d_y < s_y ) {\r
3309         d_y += squareSize / 2 + squareSize / 4;\r
3310     }\r
3311     else {\r
3312         d_y += squareSize / 2;\r
3313     }\r
3314 \r
3315     if( d_x > s_x ) {\r
3316         d_x += squareSize / 2 - squareSize / 4;\r
3317     }\r
3318     else if( d_x < s_x ) {\r
3319         d_x += squareSize / 2 + squareSize / 4;\r
3320     }\r
3321     else {\r
3322         d_x += squareSize / 2;\r
3323     }\r
3324 \r
3325     s_x += squareSize / 2;\r
3326     s_y += squareSize / 2;\r
3327 \r
3328     /* Adjust width */\r
3329     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3330 \r
3331     /* Draw */\r
3332     stLB.lbStyle = BS_SOLID;\r
3333     stLB.lbColor = appData.highlightArrowColor;\r
3334     stLB.lbHatch = 0;\r
3335 \r
3336     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3337     holdpen = SelectObject( hdc, hpen );\r
3338     hbrush = CreateBrushIndirect( &stLB );\r
3339     holdbrush = SelectObject( hdc, hbrush );\r
3340 \r
3341     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3342 \r
3343     SelectObject( hdc, holdpen );\r
3344     SelectObject( hdc, holdbrush );\r
3345     DeleteObject( hpen );\r
3346     DeleteObject( hbrush );\r
3347 }\r
3348 \r
3349 BOOL HasHighlightInfo()\r
3350 {\r
3351     BOOL result = FALSE;\r
3352 \r
3353     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3354         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3355     {\r
3356         result = TRUE;\r
3357     }\r
3358 \r
3359     return result;\r
3360 \r
3361 \r
3362 \r
3363 }\r
3364 \r
3365 BOOL IsDrawArrowEnabled()\r
3366 {\r
3367     BOOL result = FALSE;\r
3368 \r
3369     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3370         result = TRUE;\r
3371     }\r
3372 \r
3373     return result;\r
3374 }\r
3375 \r
3376 VOID DrawArrowHighlight( HDC hdc )\r
3377 {\r
3378     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3379         DrawArrowBetweenSquares( hdc,\r
3380             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3381             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3382     }\r
3383 }\r
3384 \r
3385 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3386 {\r
3387     HRGN result = NULL;\r
3388 \r
3389     if( HasHighlightInfo() ) {\r
3390         int x1, y1, x2, y2;\r
3391         int sx, sy, dx, dy;\r
3392 \r
3393         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3394         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3395 \r
3396         sx = MIN( x1, x2 );\r
3397         sy = MIN( y1, y2 );\r
3398         dx = MAX( x1, x2 ) + squareSize;\r
3399         dy = MAX( y1, y2 ) + squareSize;\r
3400 \r
3401         result = CreateRectRgn( sx, sy, dx, dy );\r
3402     }\r
3403 \r
3404     return result;\r
3405 }\r
3406 \r
3407 /*\r
3408     Warning: this function modifies the behavior of several other functions. \r
3409     \r
3410     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3411     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3412     repaint is scattered all over the place, which is not good for features such as\r
3413     "arrow highlighting" that require a full repaint of the board.\r
3414 \r
3415     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3416     user interaction, when speed is not so important) but especially to avoid errors\r
3417     in the displayed graphics.\r
3418 \r
3419     In such patched places, I always try refer to this function so there is a single\r
3420     place to maintain knowledge.\r
3421     \r
3422     To restore the original behavior, just return FALSE unconditionally.\r
3423 */\r
3424 BOOL IsFullRepaintPreferrable()\r
3425 {\r
3426     BOOL result = FALSE;\r
3427 \r
3428     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3429         /* Arrow may appear on the board */\r
3430         result = TRUE;\r
3431     }\r
3432 \r
3433     return result;\r
3434 }\r
3435 \r
3436 /* \r
3437     This function is called by DrawPosition to know whether a full repaint must\r
3438     be forced or not.\r
3439 \r
3440     Only DrawPosition may directly call this function, which makes use of \r
3441     some state information. Other function should call DrawPosition specifying \r
3442     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3443 */\r
3444 BOOL DrawPositionNeedsFullRepaint()\r
3445 {\r
3446     BOOL result = FALSE;\r
3447 \r
3448     /* \r
3449         Probably a slightly better policy would be to trigger a full repaint\r
3450         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3451         but animation is fast enough that it's difficult to notice.\r
3452     */\r
3453     if( animInfo.piece == EmptySquare ) {\r
3454         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3455             result = TRUE;\r
3456         }\r
3457     }\r
3458 \r
3459     return result;\r
3460 }\r
3461 \r
3462 static HBITMAP borderBitmap;\r
3463 \r
3464 VOID\r
3465 DrawBackgroundOnDC(HDC hdc)\r
3466 {\r
3467   \r
3468   BITMAP bi;\r
3469   HDC tmphdc;\r
3470   HBITMAP hbm;\r
3471   static char oldBorder[MSG_SIZ];\r
3472   int w = 600, h = 600, mode;\r
3473 \r
3474   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3475     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3476     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3477   }\r
3478   if(borderBitmap == NULL) { // loading failed, use white\r
3479     FillRect( hdc, &boardRect, whitePieceBrush );\r
3480     return;\r
3481   }\r
3482   tmphdc = CreateCompatibleDC(hdc);\r
3483   hbm = SelectObject(tmphdc, borderBitmap);\r
3484   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3485             w = bi.bmWidth;\r
3486             h = bi.bmHeight;\r
3487   }\r
3488   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3489   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3490                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3491   SetStretchBltMode(hdc, mode);\r
3492   SelectObject(tmphdc, hbm);\r
3493   DeleteDC(tmphdc);\r
3494 }\r
3495 \r
3496 VOID\r
3497 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3498 {\r
3499   int row, column, x, y, square_color, piece_color;\r
3500   ChessSquare piece;\r
3501   HBRUSH oldBrush;\r
3502   HDC texture_hdc = NULL;\r
3503 \r
3504   /* [AS] Initialize background textures if needed */\r
3505   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3506       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3507       if( backTextureSquareSize != squareSize \r
3508        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3509           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3510           backTextureSquareSize = squareSize;\r
3511           RebuildTextureSquareInfo();\r
3512       }\r
3513 \r
3514       texture_hdc = CreateCompatibleDC( hdc );\r
3515   }\r
3516 \r
3517   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3518     for (column = 0; column < BOARD_WIDTH; column++) {\r
3519   \r
3520       SquareToPos(row, column, &x, &y);\r
3521 \r
3522       piece = board[row][column];\r
3523 \r
3524       square_color = ((column + row) % 2) == 1;\r
3525       if( gameInfo.variant == VariantXiangqi ) {\r
3526           square_color = !InPalace(row, column);\r
3527           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3528           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3529       }\r
3530       piece_color = (int) piece < (int) BlackPawn;\r
3531 \r
3532 \r
3533       /* [HGM] holdings file: light square or black */\r
3534       if(column == BOARD_LEFT-2) {\r
3535             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3536                 square_color = 1;\r
3537             else {\r
3538                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3539                 continue;\r
3540             }\r
3541       } else\r
3542       if(column == BOARD_RGHT + 1 ) {\r
3543             if( row < gameInfo.holdingsSize )\r
3544                 square_color = 1;\r
3545             else {\r
3546                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3547                 continue;\r
3548             }\r
3549       }\r
3550       if(column == BOARD_LEFT-1 ) /* left align */\r
3551             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3552       else if( column == BOARD_RGHT) /* right align */\r
3553             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3554       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3555       else\r
3556       if (appData.monoMode) {\r
3557         if (piece == EmptySquare) {\r
3558           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3559                  square_color ? WHITENESS : BLACKNESS);\r
3560         } else {\r
3561           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3562         }\r
3563       } \r
3564       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3565           /* [AS] Draw the square using a texture bitmap */\r
3566           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3567           int r = row, c = column; // [HGM] do not flip board in flipView\r
3568           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3569 \r
3570           DrawTile( x, y, \r
3571               squareSize, squareSize, \r
3572               hdc, \r
3573               texture_hdc,\r
3574               backTextureSquareInfo[r][c].mode,\r
3575               backTextureSquareInfo[r][c].x,\r
3576               backTextureSquareInfo[r][c].y );\r
3577 \r
3578           SelectObject( texture_hdc, hbm );\r
3579 \r
3580           if (piece != EmptySquare) {\r
3581               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3582           }\r
3583       }\r
3584       else {\r
3585         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3586 \r
3587         oldBrush = SelectObject(hdc, brush );\r
3588         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3589         SelectObject(hdc, oldBrush);\r
3590         if (piece != EmptySquare)\r
3591           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3592       }\r
3593     }\r
3594   }\r
3595 \r
3596   if( texture_hdc != NULL ) {\r
3597     DeleteDC( texture_hdc );\r
3598   }\r
3599 }\r
3600 \r
3601 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3602 void fputDW(FILE *f, int x)\r
3603 {\r
3604         fputc(x     & 255, f);\r
3605         fputc(x>>8  & 255, f);\r
3606         fputc(x>>16 & 255, f);\r
3607         fputc(x>>24 & 255, f);\r
3608 }\r
3609 \r
3610 #define MAX_CLIPS 200   /* more than enough */\r
3611 \r
3612 VOID\r
3613 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3614 {\r
3615 //  HBITMAP bufferBitmap;\r
3616   BITMAP bi;\r
3617 //  RECT Rect;\r
3618   HDC tmphdc;\r
3619   HBITMAP hbm;\r
3620   int w = 100, h = 50;\r
3621 \r
3622   if(logo == NULL) {\r
3623     if(!logoHeight) return;\r
3624     FillRect( hdc, &logoRect, whitePieceBrush );\r
3625   }\r
3626 //  GetClientRect(hwndMain, &Rect);\r
3627 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3628 //                                      Rect.bottom-Rect.top+1);\r
3629   tmphdc = CreateCompatibleDC(hdc);\r
3630   hbm = SelectObject(tmphdc, logo);\r
3631   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3632             w = bi.bmWidth;\r
3633             h = bi.bmHeight;\r
3634   }\r
3635   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3636                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3637   SelectObject(tmphdc, hbm);\r
3638   DeleteDC(tmphdc);\r
3639 }\r
3640 \r
3641 VOID\r
3642 DisplayLogos()\r
3643 {\r
3644   if(logoHeight) {\r
3645         HDC hdc = GetDC(hwndMain);\r
3646         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3647         if(appData.autoLogo) {\r
3648           \r
3649           switch(gameMode) { // pick logos based on game mode\r
3650             case IcsObserving:\r
3651                 whiteLogo = second.programLogo; // ICS logo\r
3652                 blackLogo = second.programLogo;\r
3653             default:\r
3654                 break;\r
3655             case IcsPlayingWhite:\r
3656                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3657                 blackLogo = second.programLogo; // ICS logo\r
3658                 break;\r
3659             case IcsPlayingBlack:\r
3660                 whiteLogo = second.programLogo; // ICS logo\r
3661                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3662                 break;\r
3663             case TwoMachinesPlay:\r
3664                 if(first.twoMachinesColor[0] == 'b') {\r
3665                     whiteLogo = second.programLogo;\r
3666                     blackLogo = first.programLogo;\r
3667                 }\r
3668                 break;\r
3669             case MachinePlaysWhite:\r
3670                 blackLogo = userLogo;\r
3671                 break;\r
3672             case MachinePlaysBlack:\r
3673                 whiteLogo = userLogo;\r
3674                 blackLogo = first.programLogo;\r
3675           }\r
3676         }\r
3677         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3678         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3679         ReleaseDC(hwndMain, hdc);\r
3680   }\r
3681 }\r
3682 \r
3683 void\r
3684 UpdateLogos(int display)\r
3685 { // called after loading new engine(s), in tourney or from menu\r
3686   LoadLogo(&first, 0, FALSE);\r
3687   LoadLogo(&second, 1, appData.icsActive);\r
3688   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3689   if(display) DisplayLogos();\r
3690 }\r
3691 \r
3692 static HDC hdcSeek;\r
3693 \r
3694 // [HGM] seekgraph\r
3695 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3696 {\r
3697     POINT stPt;\r
3698     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3699     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3700     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3701     SelectObject( hdcSeek, hp );\r
3702 }\r
3703 \r
3704 // front-end wrapper for drawing functions to do rectangles\r
3705 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3706 {\r
3707     HPEN hp;\r
3708     RECT rc;\r
3709 \r
3710     if (hdcSeek == NULL) {\r
3711     hdcSeek = GetDC(hwndMain);\r
3712       if (!appData.monoMode) {\r
3713         SelectPalette(hdcSeek, hPal, FALSE);\r
3714         RealizePalette(hdcSeek);\r
3715       }\r
3716     }\r
3717     hp = SelectObject( hdcSeek, gridPen );\r
3718     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3719     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3720     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3721     SelectObject( hdcSeek, hp );\r
3722 }\r
3723 \r
3724 // front-end wrapper for putting text in graph\r
3725 void DrawSeekText(char *buf, int x, int y)\r
3726 {\r
3727         SIZE stSize;\r
3728         SetBkMode( hdcSeek, TRANSPARENT );\r
3729         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3730         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3731 }\r
3732 \r
3733 void DrawSeekDot(int x, int y, int color)\r
3734 {\r
3735         int square = color & 0x80;\r
3736         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3737                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3738         color &= 0x7F;\r
3739         if(square)\r
3740             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3741                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3742         else\r
3743             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3744                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3745             SelectObject(hdcSeek, oldBrush);\r
3746 }\r
3747 \r
3748 void DrawSeekOpen()\r
3749 {\r
3750 }\r
3751 \r
3752 void DrawSeekClose()\r
3753 {\r
3754 }\r
3755 \r
3756 \r
3757 \r
3758 VOID\r
3759 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3760 {\r
3761   static Board lastReq[2], lastDrawn[2];\r
3762   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3763   static int lastDrawnFlipView = 0;\r
3764   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3765   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3766   HDC tmphdc;\r
3767   HDC hdcmem;\r
3768   HBITMAP bufferBitmap;\r
3769   HBITMAP oldBitmap;\r
3770   RECT Rect;\r
3771   HRGN clips[MAX_CLIPS];\r
3772   ChessSquare dragged_piece = EmptySquare;\r
3773   int nr = twoBoards*partnerUp;\r
3774 \r
3775   /* I'm undecided on this - this function figures out whether a full\r
3776    * repaint is necessary on its own, so there's no real reason to have the\r
3777    * caller tell it that.  I think this can safely be set to FALSE - but\r
3778    * if we trust the callers not to request full repaints unnessesarily, then\r
3779    * we could skip some clipping work.  In other words, only request a full\r
3780    * redraw when the majority of pieces have changed positions (ie. flip, \r
3781    * gamestart and similar)  --Hawk\r
3782    */\r
3783   Boolean fullrepaint = repaint;\r
3784 \r
3785   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3786 \r
3787   if( DrawPositionNeedsFullRepaint() ) {\r
3788       fullrepaint = TRUE;\r
3789   }\r
3790 \r
3791   if (board == NULL) {\r
3792     if (!lastReqValid[nr]) {\r
3793       return;\r
3794     }\r
3795     board = lastReq[nr];\r
3796   } else {\r
3797     CopyBoard(lastReq[nr], board);\r
3798     lastReqValid[nr] = 1;\r
3799   }\r
3800 \r
3801   if (doingSizing) {\r
3802     return;\r
3803   }\r
3804 \r
3805   if (IsIconic(hwndMain)) {\r
3806     return;\r
3807   }\r
3808 \r
3809   if (hdc == NULL) {\r
3810     hdc = GetDC(hwndMain);\r
3811     if (!appData.monoMode) {\r
3812       SelectPalette(hdc, hPal, FALSE);\r
3813       RealizePalette(hdc);\r
3814     }\r
3815     releaseDC = TRUE;\r
3816   } else {\r
3817     releaseDC = FALSE;\r
3818   }\r
3819 \r
3820   /* Create some work-DCs */\r
3821   hdcmem = CreateCompatibleDC(hdc);\r
3822   tmphdc = CreateCompatibleDC(hdc);\r
3823 \r
3824   /* If dragging is in progress, we temporarely remove the piece */\r
3825   /* [HGM] or temporarily decrease count if stacked              */\r
3826   /*       !! Moved to before board compare !!                   */\r
3827   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3828     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3829     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3830             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3831         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3832     } else \r
3833     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3834             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3835         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3836     } else \r
3837         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3838   }\r
3839 \r
3840   /* Figure out which squares need updating by comparing the \r
3841    * newest board with the last drawn board and checking if\r
3842    * flipping has changed.\r
3843    */\r
3844   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3845     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3846       for (column = 0; column < BOARD_WIDTH; column++) {\r
3847         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3848           SquareToPos(row, column, &x, &y);\r
3849           clips[num_clips++] =\r
3850             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3851         }\r
3852       }\r
3853     }\r
3854    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3855     for (i=0; i<2; i++) {\r
3856       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3857           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3858         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3859             lastDrawnHighlight.sq[i].y >= 0) {\r
3860           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3861                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3862           clips[num_clips++] =\r
3863             CreateRectRgn(x - lineGap, y - lineGap, \r
3864                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3865         }\r
3866         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3867           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3868           clips[num_clips++] =\r
3869             CreateRectRgn(x - lineGap, y - lineGap, \r
3870                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3871         }\r
3872       }\r
3873     }\r
3874     for (i=0; i<2; i++) {\r
3875       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3876           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3877         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3878             lastDrawnPremove.sq[i].y >= 0) {\r
3879           SquareToPos(lastDrawnPremove.sq[i].y,\r
3880                       lastDrawnPremove.sq[i].x, &x, &y);\r
3881           clips[num_clips++] =\r
3882             CreateRectRgn(x - lineGap, y - lineGap, \r
3883                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3884         }\r
3885         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3886             premoveHighlightInfo.sq[i].y >= 0) {\r
3887           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3888                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3889           clips[num_clips++] =\r
3890             CreateRectRgn(x - lineGap, y - lineGap, \r
3891                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3892         }\r
3893       }\r
3894     }\r
3895    } else { // nr == 1\r
3896         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3897         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3898         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3899         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3900       for (i=0; i<2; i++) {\r
3901         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3902             partnerHighlightInfo.sq[i].y >= 0) {\r
3903           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3904                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3905           clips[num_clips++] =\r
3906             CreateRectRgn(x - lineGap, y - lineGap, \r
3907                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3908         }\r
3909         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3910             oldPartnerHighlight.sq[i].y >= 0) {\r
3911           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3912                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3913           clips[num_clips++] =\r
3914             CreateRectRgn(x - lineGap, y - lineGap, \r
3915                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3916         }\r
3917       }\r
3918    }\r
3919   } else {\r
3920     fullrepaint = TRUE;\r
3921   }\r
3922 \r
3923   /* Create a buffer bitmap - this is the actual bitmap\r
3924    * being written to.  When all the work is done, we can\r
3925    * copy it to the real DC (the screen).  This avoids\r
3926    * the problems with flickering.\r
3927    */\r
3928   GetClientRect(hwndMain, &Rect);\r
3929   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3930                                         Rect.bottom-Rect.top+1);\r
3931   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3932   if (!appData.monoMode) {\r
3933     SelectPalette(hdcmem, hPal, FALSE);\r
3934   }\r
3935 \r
3936   /* Create clips for dragging */\r
3937   if (!fullrepaint) {\r
3938     if (dragInfo.from.x >= 0) {\r
3939       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3940       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3941     }\r
3942     if (dragInfo.start.x >= 0) {\r
3943       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3944       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3945     }\r
3946     if (dragInfo.pos.x >= 0) {\r
3947       x = dragInfo.pos.x - squareSize / 2;\r
3948       y = dragInfo.pos.y - squareSize / 2;\r
3949       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3950     }\r
3951     if (dragInfo.lastpos.x >= 0) {\r
3952       x = dragInfo.lastpos.x - squareSize / 2;\r
3953       y = dragInfo.lastpos.y - squareSize / 2;\r
3954       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3955     }\r
3956   }\r
3957 \r
3958   /* Are we animating a move?  \r
3959    * If so, \r
3960    *   - remove the piece from the board (temporarely)\r
3961    *   - calculate the clipping region\r
3962    */\r
3963   if (!fullrepaint) {\r
3964     if (animInfo.piece != EmptySquare) {\r
3965       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3966       x = boardRect.left + animInfo.lastpos.x;\r
3967       y = boardRect.top + animInfo.lastpos.y;\r
3968       x2 = boardRect.left + animInfo.pos.x;\r
3969       y2 = boardRect.top + animInfo.pos.y;\r
3970       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3971       /* Slight kludge.  The real problem is that after AnimateMove is\r
3972          done, the position on the screen does not match lastDrawn.\r
3973          This currently causes trouble only on e.p. captures in\r
3974          atomic, where the piece moves to an empty square and then\r
3975          explodes.  The old and new positions both had an empty square\r
3976          at the destination, but animation has drawn a piece there and\r
3977          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3978 \r
3979       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3980     }\r
3981   }\r
3982 \r
3983   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3984   if (num_clips == 0)\r
3985     fullrepaint = TRUE;\r
3986 \r
3987   /* Set clipping on the memory DC */\r
3988   if (!fullrepaint) {\r
3989     SelectClipRgn(hdcmem, clips[0]);\r
3990     for (x = 1; x < num_clips; x++) {\r
3991       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3992         abort();  // this should never ever happen!\r
3993     }\r
3994   }\r
3995 \r
3996   /* Do all the drawing to the memory DC */\r
3997   if(explodeInfo.radius) { // [HGM] atomic\r
3998         HBRUSH oldBrush;\r
3999         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4000         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
4001         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4002         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4003         x += squareSize/2;\r
4004         y += squareSize/2;\r
4005         if(!fullrepaint) {\r
4006           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4007           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4008         }\r
4009         DrawGridOnDC(hdcmem);\r
4010         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4011         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4012         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4013         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
4014         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4015         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4016         SelectObject(hdcmem, oldBrush);\r
4017   } else {\r
4018     if(border) DrawBackgroundOnDC(hdcmem);\r
4019     DrawGridOnDC(hdcmem);\r
4020     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
4021         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4022         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4023     } else {\r
4024         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
4025         oldPartnerHighlight = partnerHighlightInfo;\r
4026     }\r
4027     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4028   }\r
4029   if(nr == 0) // [HGM] dual: markers only on left board\r
4030   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4031     for (column = 0; column < BOARD_WIDTH; column++) {\r
4032         if (marker[row][column]) { // marker changes only occur with full repaint!\r
4033             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
4034             SquareToPos(row, column, &x, &y);\r
4035             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
4036                           x + 3*squareSize/4, y + 3*squareSize/4);\r
4037             SelectObject(hdcmem, oldBrush);\r
4038         }\r
4039     }\r
4040   }\r
4041 \r
4042   if( appData.highlightMoveWithArrow ) {\r
4043 \r
4044     DrawArrowHighlight(hdcmem);\r
4045   }\r
4046 \r
4047   DrawCoordsOnDC(hdcmem);\r
4048 \r
4049   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4050                  /* to make sure lastDrawn contains what is actually drawn */\r
4051 \r
4052   /* Put the dragged piece back into place and draw it (out of place!) */\r
4053     if (dragged_piece != EmptySquare) {\r
4054     /* [HGM] or restack */\r
4055     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4056                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4057     else\r
4058     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4059                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4060 \r
4061     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4062     x = dragInfo.pos.x - squareSize / 2;\r
4063     y = dragInfo.pos.y - squareSize / 2;\r
4064     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4065                   ((int) dragInfo.piece < (int) BlackPawn), \r
4066                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4067   }   \r
4068   \r
4069   /* Put the animated piece back into place and draw it */\r
4070   if (animInfo.piece != EmptySquare) {\r
4071     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4072     x = boardRect.left + animInfo.pos.x;\r
4073     y = boardRect.top + animInfo.pos.y;\r
4074     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4075                   ((int) animInfo.piece < (int) BlackPawn),\r
4076                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4077   }\r
4078 \r
4079   /* Release the bufferBitmap by selecting in the old bitmap \r
4080    * and delete the memory DC\r
4081    */\r
4082   SelectObject(hdcmem, oldBitmap);\r
4083   DeleteDC(hdcmem);\r
4084 \r
4085   /* Set clipping on the target DC */\r
4086   if (!fullrepaint) {\r
4087     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4088         RECT rect;\r
4089         GetRgnBox(clips[x], &rect);\r
4090         DeleteObject(clips[x]);\r
4091         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4092                           rect.right + wpMain.width/2, rect.bottom);\r
4093     }\r
4094     SelectClipRgn(hdc, clips[0]);\r
4095     for (x = 1; x < num_clips; x++) {\r
4096       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4097         abort();   // this should never ever happen!\r
4098     } \r
4099   }\r
4100 \r
4101   /* Copy the new bitmap onto the screen in one go.\r
4102    * This way we avoid any flickering\r
4103    */\r
4104   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4105   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4106          boardRect.right - boardRect.left,\r
4107          boardRect.bottom - boardRect.top,\r
4108          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4109   if(saveDiagFlag) { \r
4110     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4111     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4112     HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);\r
4113 \r
4114     bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);\r
4115     obmp = SelectObject(tmp, bufferBitmap);\r
4116     BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,\r
4117            tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);\r
4118     GetObject(bufferBitmap, sizeof(b), &b);\r
4119     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4120         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4121         bih.biWidth = b.bmWidth;\r
4122         bih.biHeight = b.bmHeight;\r
4123         bih.biPlanes = 1;\r
4124         bih.biBitCount = b.bmBitsPixel;\r
4125         bih.biCompression = 0;\r
4126         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4127         bih.biXPelsPerMeter = 0;\r
4128         bih.biYPelsPerMeter = 0;\r
4129         bih.biClrUsed = 0;\r
4130         bih.biClrImportant = 0;\r
4131 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4132 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4133         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4134 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4135 \r
4136         wb = b.bmWidthBytes;\r
4137         // count colors\r
4138         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4139                 int k = ((int*) pData)[i];\r
4140                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4141                 if(j >= 16) break;\r
4142                 color[j] = k;\r
4143                 if(j >= nrColors) nrColors = j+1;\r
4144         }\r
4145         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4146                 INT p = 0;\r
4147                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4148                     for(w=0; w<(wb>>2); w+=2) {\r
4149                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4150                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4151                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4152                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4153                         pData[p++] = m | j<<4;\r
4154                     }\r
4155                     while(p&3) pData[p++] = 0;\r
4156                 }\r
4157                 fac = 3;\r
4158                 wb = ((wb+31)>>5)<<2;\r
4159         }\r
4160         // write BITMAPFILEHEADER\r
4161         fprintf(diagFile, "BM");\r
4162         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4163         fputDW(diagFile, 0);\r
4164         fputDW(diagFile, 0x36 + (fac?64:0));\r
4165         // write BITMAPINFOHEADER\r
4166         fputDW(diagFile, 40);\r
4167         fputDW(diagFile, b.bmWidth);\r
4168         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4169         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4170         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4171         fputDW(diagFile, 0);\r
4172         fputDW(diagFile, 0);\r
4173         fputDW(diagFile, 0);\r
4174         fputDW(diagFile, 0);\r
4175         fputDW(diagFile, 0);\r
4176         fputDW(diagFile, 0);\r
4177         // write color table\r
4178         if(fac)\r
4179         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4180         // write bitmap data\r
4181         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4182                 fputc(pData[i], diagFile);\r
4183         free(pData);\r
4184      }\r
4185      DeleteObject(bufferBitmap); bufferBitmap = src;\r
4186      SelectObject(tmp, obmp);\r
4187      DeleteDC(tmp);\r
4188   }\r
4189 \r
4190   SelectObject(tmphdc, oldBitmap);\r
4191 \r
4192   /* Massive cleanup */\r
4193   for (x = 0; x < num_clips; x++)\r
4194     DeleteObject(clips[x]);\r
4195 \r
4196   DeleteDC(tmphdc);\r
4197   DeleteObject(bufferBitmap);\r
4198 \r
4199   if (releaseDC) \r
4200     ReleaseDC(hwndMain, hdc);\r
4201   \r
4202   if (lastDrawnFlipView != flipView && nr == 0) {\r
4203     if (flipView)\r
4204       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4205     else\r
4206       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4207   }\r
4208 \r
4209 /*  CopyBoard(lastDrawn, board);*/\r
4210   lastDrawnHighlight = highlightInfo;\r
4211   lastDrawnPremove   = premoveHighlightInfo;\r
4212   lastDrawnFlipView = flipView;\r
4213   lastDrawnValid[nr] = 1;\r
4214 }\r
4215 \r
4216 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4217 int\r
4218 SaveDiagram(f)\r
4219      FILE *f;\r
4220 {\r
4221     saveDiagFlag = 1; diagFile = f;\r
4222     HDCDrawPosition(NULL, TRUE, NULL);\r
4223     saveDiagFlag = 0;\r
4224 \r
4225     fclose(f);\r
4226     return TRUE;\r
4227 }\r
4228 \r
4229 \r
4230 /*---------------------------------------------------------------------------*\\r
4231 | CLIENT PAINT PROCEDURE\r
4232 |   This is the main event-handler for the WM_PAINT message.\r
4233 |\r
4234 \*---------------------------------------------------------------------------*/\r
4235 VOID\r
4236 PaintProc(HWND hwnd)\r
4237 {\r
4238   HDC         hdc;\r
4239   PAINTSTRUCT ps;\r
4240   HFONT       oldFont;\r
4241 \r
4242   if((hdc = BeginPaint(hwnd, &ps))) {\r
4243     if (IsIconic(hwnd)) {\r
4244       DrawIcon(hdc, 2, 2, iconCurrent);\r
4245     } else {\r
4246       if (!appData.monoMode) {\r
4247         SelectPalette(hdc, hPal, FALSE);\r
4248         RealizePalette(hdc);\r
4249       }\r
4250       HDCDrawPosition(hdc, 1, NULL);\r
4251       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4252         flipView = !flipView; partnerUp = !partnerUp;\r
4253         HDCDrawPosition(hdc, 1, NULL);\r
4254         flipView = !flipView; partnerUp = !partnerUp;\r
4255       }\r
4256       oldFont =\r
4257         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4258       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4259                  ETO_CLIPPED|ETO_OPAQUE,\r
4260                  &messageRect, messageText, strlen(messageText), NULL);\r
4261       SelectObject(hdc, oldFont);\r
4262       DisplayBothClocks();\r
4263       DisplayLogos();\r
4264     }\r
4265     EndPaint(hwnd,&ps);\r
4266   }\r
4267 \r
4268   return;\r
4269 }\r
4270 \r
4271 \r
4272 /*\r
4273  * If the user selects on a border boundary, return -1; if off the board,\r
4274  *   return -2.  Otherwise map the event coordinate to the square.\r
4275  * The offset boardRect.left or boardRect.top must already have been\r
4276  *   subtracted from x.\r
4277  */\r
4278 int EventToSquare(x, limit)\r
4279      int x, limit;\r
4280 {\r
4281   if (x <= border)\r
4282     return -2;\r
4283   if (x < lineGap + border)\r
4284     return -1;\r
4285   x -= lineGap + border;\r
4286   if ((x % (squareSize + lineGap)) >= squareSize)\r
4287     return -1;\r
4288   x /= (squareSize + lineGap);\r
4289     if (x >= limit)\r
4290     return -2;\r
4291   return x;\r
4292 }\r
4293 \r
4294 typedef struct {\r
4295   char piece;\r
4296   int command;\r
4297   char* name;\r
4298 } DropEnable;\r
4299 \r
4300 DropEnable dropEnables[] = {\r
4301   { 'P', DP_Pawn, N_("Pawn") },\r
4302   { 'N', DP_Knight, N_("Knight") },\r
4303   { 'B', DP_Bishop, N_("Bishop") },\r
4304   { 'R', DP_Rook, N_("Rook") },\r
4305   { 'Q', DP_Queen, N_("Queen") },\r
4306 };\r
4307 \r
4308 VOID\r
4309 SetupDropMenu(HMENU hmenu)\r
4310 {\r
4311   int i, count, enable;\r
4312   char *p;\r
4313   extern char white_holding[], black_holding[];\r
4314   char item[MSG_SIZ];\r
4315 \r
4316   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4317     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4318                dropEnables[i].piece);\r
4319     count = 0;\r
4320     while (p && *p++ == dropEnables[i].piece) count++;\r
4321       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4322     enable = count > 0 || !appData.testLegality\r
4323       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4324                       && !appData.icsActive);\r
4325     ModifyMenu(hmenu, dropEnables[i].command,\r
4326                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4327                dropEnables[i].command, item);\r
4328   }\r
4329 }\r
4330 \r
4331 void DragPieceBegin(int x, int y, Boolean instantly)\r
4332 {\r
4333       dragInfo.lastpos.x = boardRect.left + x;\r
4334       dragInfo.lastpos.y = boardRect.top + y;\r
4335       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4336       dragInfo.from.x = fromX;\r
4337       dragInfo.from.y = fromY;\r
4338       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4339       dragInfo.start = dragInfo.from;\r
4340       SetCapture(hwndMain);\r
4341 }\r
4342 \r
4343 void DragPieceEnd(int x, int y)\r
4344 {\r
4345     ReleaseCapture();\r
4346     dragInfo.start.x = dragInfo.start.y = -1;\r
4347     dragInfo.from = dragInfo.start;\r
4348     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4349 }\r
4350 \r
4351 void ChangeDragPiece(ChessSquare piece)\r
4352 {\r
4353     dragInfo.piece = piece;\r
4354 }\r
4355 \r
4356 /* Event handler for mouse messages */\r
4357 VOID\r
4358 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4359 {\r
4360   int x, y, menuNr;\r
4361   POINT pt;\r
4362   static int recursive = 0;\r
4363   HMENU hmenu;\r
4364   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4365 \r
4366   if (recursive) {\r
4367     if (message == WM_MBUTTONUP) {\r
4368       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4369          to the middle button: we simulate pressing the left button too!\r
4370          */\r
4371       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4372       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4373     }\r
4374     return;\r
4375   }\r
4376   recursive++;\r
4377   \r
4378   pt.x = LOWORD(lParam);\r
4379   pt.y = HIWORD(lParam);\r
4380   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4381   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4382   if (!flipView && y >= 0) {\r
4383     y = BOARD_HEIGHT - 1 - y;\r
4384   }\r
4385   if (flipView && x >= 0) {\r
4386     x = BOARD_WIDTH - 1 - x;\r
4387   }\r
4388 \r
4389   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4390   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4391 \r
4392   switch (message) {\r
4393   case WM_LBUTTONDOWN:\r
4394       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4395         ClockClick(flipClock); break;\r
4396       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4397         ClockClick(!flipClock); break;\r
4398       }\r
4399     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4400       dragInfo.start.x = dragInfo.start.y = -1;\r
4401       dragInfo.from = dragInfo.start;\r
4402     }\r
4403     if(fromX == -1 && frozen) { // not sure where this is for\r
4404                 fromX = fromY = -1; \r
4405       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4406       break;\r
4407     }\r
4408       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4409       DrawPosition(TRUE, NULL);\r
4410     break;\r
4411 \r
4412   case WM_LBUTTONUP:\r
4413       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4414       DrawPosition(TRUE, NULL);\r
4415     break;\r
4416 \r
4417   case WM_MOUSEMOVE:\r
4418     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4419     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4420     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4421     if ((appData.animateDragging || appData.highlightDragging)\r
4422         && (wParam & MK_LBUTTON || dragging == 2)\r
4423         && dragInfo.from.x >= 0) \r
4424     {\r
4425       BOOL full_repaint = FALSE;\r
4426 \r
4427       if (appData.animateDragging) {\r
4428         dragInfo.pos = pt;\r
4429       }\r
4430       if (appData.highlightDragging) {\r
4431         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4432         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4433             full_repaint = TRUE;\r
4434         }\r
4435       }\r
4436       \r
4437       DrawPosition( full_repaint, NULL);\r
4438       \r
4439       dragInfo.lastpos = dragInfo.pos;\r
4440     }\r
4441     break;\r
4442 \r
4443   case WM_MOUSEWHEEL: // [DM]\r
4444     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4445        /* Mouse Wheel is being rolled forward\r
4446         * Play moves forward\r
4447         */\r
4448        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4449                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4450        /* Mouse Wheel is being rolled backward\r
4451         * Play moves backward\r
4452         */\r
4453        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4454                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4455     }\r
4456     break;\r
4457 \r
4458   case WM_MBUTTONUP:\r
4459   case WM_RBUTTONUP:\r
4460     ReleaseCapture();\r
4461     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4462     break;\r
4463  \r
4464   case WM_MBUTTONDOWN:\r
4465   case WM_RBUTTONDOWN:\r
4466     ErrorPopDown();\r
4467     ReleaseCapture();\r
4468     fromX = fromY = -1;\r
4469     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4470     dragInfo.start.x = dragInfo.start.y = -1;\r
4471     dragInfo.from = dragInfo.start;\r
4472     dragInfo.lastpos = dragInfo.pos;\r
4473     if (appData.highlightDragging) {\r
4474       ClearHighlights();\r
4475     }\r
4476     if(y == -2) {\r
4477       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4478       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4479           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4480       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4481           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4482       }\r
4483       break;\r
4484     }\r
4485     DrawPosition(TRUE, NULL);\r
4486 \r
4487     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4488     switch (menuNr) {\r
4489     case 0:\r
4490       if (message == WM_MBUTTONDOWN) {\r
4491         buttonCount = 3;  /* even if system didn't think so */\r
4492         if (wParam & MK_SHIFT) \r
4493           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4494         else\r
4495           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4496       } else { /* message == WM_RBUTTONDOWN */\r
4497         /* Just have one menu, on the right button.  Windows users don't\r
4498            think to try the middle one, and sometimes other software steals\r
4499            it, or it doesn't really exist. */\r
4500         if(gameInfo.variant != VariantShogi)\r
4501             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4502         else\r
4503             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4504       }\r
4505       break;\r
4506     case 2:\r
4507       SetCapture(hwndMain);\r
4508       break;\r
4509     case 1:\r
4510       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4511       SetupDropMenu(hmenu);\r
4512       MenuPopup(hwnd, pt, hmenu, -1);\r
4513     default:\r
4514       break;\r
4515     }\r
4516     break;\r
4517   }\r
4518 \r
4519   recursive--;\r
4520 }\r
4521 \r
4522 /* Preprocess messages for buttons in main window */\r
4523 LRESULT CALLBACK\r
4524 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4525 {\r
4526   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4527   int i, dir;\r
4528 \r
4529   for (i=0; i<N_BUTTONS; i++) {\r
4530     if (buttonDesc[i].id == id) break;\r
4531   }\r
4532   if (i == N_BUTTONS) return 0;\r
4533   switch (message) {\r
4534   case WM_KEYDOWN:\r
4535     switch (wParam) {\r
4536     case VK_LEFT:\r
4537     case VK_RIGHT:\r
4538       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4539       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4540       return TRUE;\r
4541     }\r
4542     break;\r
4543   case WM_CHAR:\r
4544     switch (wParam) {\r
4545     case '\r':\r
4546       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4547       return TRUE;\r
4548     default:\r
4549       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4550         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4551         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4552         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4553         SetFocus(h);\r
4554         SendMessage(h, WM_CHAR, wParam, lParam);\r
4555         return TRUE;\r
4556       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4557         TypeInEvent((char)wParam);\r
4558       }\r
4559       break;\r
4560     }\r
4561     break;\r
4562   }\r
4563   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4564 }\r
4565 \r
4566 static int promoStyle;\r
4567 \r
4568 /* Process messages for Promotion dialog box */\r
4569 LRESULT CALLBACK\r
4570 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4571 {\r
4572 \r
4573   char promoChar;\r
4574 \r
4575   switch (message) {\r
4576 \r
4577   case WM_INITDIALOG: /* message: initialize dialog box */\r
4578     /* Center the dialog over the application window */\r
4579     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4580     Translate(hDlg, DLG_PromotionKing);\r
4581     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4582       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4583        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4584        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4585                SW_SHOW : SW_HIDE);\r
4586     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4587     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4588        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4589          PieceToChar(WhiteAngel) != '~') ||\r
4590         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4591          PieceToChar(BlackAngel) != '~')   ) ?\r
4592                SW_SHOW : SW_HIDE);\r
4593     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4594        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4595          PieceToChar(WhiteMarshall) != '~') ||\r
4596         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4597          PieceToChar(BlackMarshall) != '~')   ) ?\r
4598                SW_SHOW : SW_HIDE);\r
4599     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4600     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4601     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4602     if(promoStyle) {\r
4603         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4604         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4605         SetWindowText(hDlg, "Promote?");\r
4606     }\r
4607     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4608        gameInfo.variant == VariantSuper ?\r
4609                SW_SHOW : SW_HIDE);\r
4610     return TRUE;\r
4611 \r
4612   case WM_COMMAND: /* message: received a command */\r
4613     switch (LOWORD(wParam)) {\r
4614     case IDCANCEL:\r
4615       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4616       ClearHighlights();\r
4617       DrawPosition(FALSE, NULL);\r
4618       return TRUE;\r
4619     case PB_King:\r
4620       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4621       break;\r
4622     case PB_Queen:\r
4623       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4624       break;\r
4625     case PB_Rook:\r
4626       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4627       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4628       break;\r
4629     case PB_Bishop:\r
4630       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4631       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4632       break;\r
4633     case PB_Chancellor:\r
4634       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4635       break;\r
4636     case PB_Archbishop:\r
4637       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4638       break;\r
4639     case PB_Knight:\r
4640       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4641                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4642       break;\r
4643     default:\r
4644       return FALSE;\r
4645     }\r
4646     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4647     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4648     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4649     fromX = fromY = -1;\r
4650     if (!appData.highlightLastMove) {\r
4651       ClearHighlights();\r
4652       DrawPosition(FALSE, NULL);\r
4653     }\r
4654     return TRUE;\r
4655   }\r
4656   return FALSE;\r
4657 }\r
4658 \r
4659 /* Pop up promotion dialog */\r
4660 VOID\r
4661 PromotionPopup(HWND hwnd)\r
4662 {\r
4663   FARPROC lpProc;\r
4664 \r
4665   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4666   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4667     hwnd, (DLGPROC)lpProc);\r
4668   FreeProcInstance(lpProc);\r
4669 }\r
4670 \r
4671 void\r
4672 PromotionPopUp(char choice)\r
4673 {\r
4674   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4675   DrawPosition(TRUE, NULL);\r
4676   PromotionPopup(hwndMain);\r
4677 }\r
4678 \r
4679 VOID\r
4680 LoadGameDialog(HWND hwnd, char* title)\r
4681 {\r
4682   UINT number = 0;\r
4683   FILE *f;\r
4684   char fileTitle[MSG_SIZ];\r
4685   f = OpenFileDialog(hwnd, "rb", "",\r
4686                      appData.oldSaveStyle ? "gam" : "pgn",\r
4687                      GAME_FILT,\r
4688                      title, &number, fileTitle, NULL);\r
4689   if (f != NULL) {\r
4690     cmailMsgLoaded = FALSE;\r
4691     if (number == 0) {\r
4692       int error = GameListBuild(f);\r
4693       if (error) {\r
4694         DisplayError(_("Cannot build game list"), error);\r
4695       } else if (!ListEmpty(&gameList) &&\r
4696                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4697         GameListPopUp(f, fileTitle);\r
4698         return;\r
4699       }\r
4700       GameListDestroy();\r
4701       number = 1;\r
4702     }\r
4703     LoadGame(f, number, fileTitle, FALSE);\r
4704   }\r
4705 }\r
4706 \r
4707 int get_term_width()\r
4708 {\r
4709     HDC hdc;\r
4710     TEXTMETRIC tm;\r
4711     RECT rc;\r
4712     HFONT hfont, hold_font;\r
4713     LOGFONT lf;\r
4714     HWND hText;\r
4715 \r
4716     if (hwndConsole)\r
4717         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4718     else\r
4719         return 79;\r
4720 \r
4721     // get the text metrics\r
4722     hdc = GetDC(hText);\r
4723     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4724     if (consoleCF.dwEffects & CFE_BOLD)\r
4725         lf.lfWeight = FW_BOLD;\r
4726     if (consoleCF.dwEffects & CFE_ITALIC)\r
4727         lf.lfItalic = TRUE;\r
4728     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4729         lf.lfStrikeOut = TRUE;\r
4730     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4731         lf.lfUnderline = TRUE;\r
4732     hfont = CreateFontIndirect(&lf);\r
4733     hold_font = SelectObject(hdc, hfont);\r
4734     GetTextMetrics(hdc, &tm);\r
4735     SelectObject(hdc, hold_font);\r
4736     DeleteObject(hfont);\r
4737     ReleaseDC(hText, hdc);\r
4738 \r
4739     // get the rectangle\r
4740     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4741 \r
4742     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4743 }\r
4744 \r
4745 void UpdateICSWidth(HWND hText)\r
4746 {\r
4747     LONG old_width, new_width;\r
4748 \r
4749     new_width = get_term_width(hText, FALSE);\r
4750     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4751     if (new_width != old_width)\r
4752     {\r
4753         ics_update_width(new_width);\r
4754         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4755     }\r
4756 }\r
4757 \r
4758 VOID\r
4759 ChangedConsoleFont()\r
4760 {\r
4761   CHARFORMAT cfmt;\r
4762   CHARRANGE tmpsel, sel;\r
4763   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4764   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4765   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4766   PARAFORMAT paraf;\r
4767 \r
4768   cfmt.cbSize = sizeof(CHARFORMAT);\r
4769   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4770     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4771                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4772   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4773    * size.  This was undocumented in the version of MSVC++ that I had\r
4774    * when I wrote the code, but is apparently documented now.\r
4775    */\r
4776   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4777   cfmt.bCharSet = f->lf.lfCharSet;\r
4778   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4779   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4780   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4781   /* Why are the following seemingly needed too? */\r
4782   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4783   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4784   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4785   tmpsel.cpMin = 0;\r
4786   tmpsel.cpMax = -1; /*999999?*/\r
4787   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4788   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4789   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4790    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4791    */\r
4792   paraf.cbSize = sizeof(paraf);\r
4793   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4794   paraf.dxStartIndent = 0;\r
4795   paraf.dxOffset = WRAP_INDENT;\r
4796   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4797   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4798   UpdateICSWidth(hText);\r
4799 }\r
4800 \r
4801 /*---------------------------------------------------------------------------*\\r
4802  *\r
4803  * Window Proc for main window\r
4804  *\r
4805 \*---------------------------------------------------------------------------*/\r
4806 \r
4807 /* Process messages for main window, etc. */\r
4808 LRESULT CALLBACK\r
4809 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4810 {\r
4811   FARPROC lpProc;\r
4812   int wmId;\r
4813   char *defName;\r
4814   FILE *f;\r
4815   UINT number;\r
4816   char fileTitle[MSG_SIZ];\r
4817   static SnapData sd;\r
4818   static int peek=0;\r
4819 \r
4820   switch (message) {\r
4821 \r
4822   case WM_PAINT: /* message: repaint portion of window */\r
4823     PaintProc(hwnd);\r
4824     break;\r
4825 \r
4826   case WM_ERASEBKGND:\r
4827     if (IsIconic(hwnd)) {\r
4828       /* Cheat; change the message */\r
4829       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4830     } else {\r
4831       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4832     }\r
4833     break;\r
4834 \r
4835   case WM_LBUTTONDOWN:\r
4836   case WM_MBUTTONDOWN:\r
4837   case WM_RBUTTONDOWN:\r
4838   case WM_LBUTTONUP:\r
4839   case WM_MBUTTONUP:\r
4840   case WM_RBUTTONUP:\r
4841   case WM_MOUSEMOVE:\r
4842   case WM_MOUSEWHEEL:\r
4843     MouseEvent(hwnd, message, wParam, lParam);\r
4844     break;\r
4845 \r
4846   case WM_KEYUP:\r
4847     if((char)wParam == '\b') {\r
4848       ForwardEvent(); peek = 0;\r
4849     }\r
4850 \r
4851     JAWS_KBUP_NAVIGATION\r
4852 \r
4853     break;\r
4854 \r
4855   case WM_KEYDOWN:\r
4856     if((char)wParam == '\b') {\r
4857       if(!peek) BackwardEvent(), peek = 1;\r
4858     }\r
4859 \r
4860     JAWS_KBDOWN_NAVIGATION\r
4861 \r
4862     break;\r
4863 \r
4864   case WM_CHAR:\r
4865     \r
4866     JAWS_ALT_INTERCEPT\r
4867 \r
4868     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4869         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4870         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4871         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4872         SetFocus(h);\r
4873         SendMessage(h, message, wParam, lParam);\r
4874     } else if(lParam != KF_REPEAT) {\r
4875         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4876                 TypeInEvent((char)wParam);\r
4877         } else if((char)wParam == 003) CopyGameToClipboard();\r
4878          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4879     }\r
4880 \r
4881     break;\r
4882 \r
4883   case WM_PALETTECHANGED:\r
4884     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4885       int nnew;\r
4886       HDC hdc = GetDC(hwndMain);\r
4887       SelectPalette(hdc, hPal, TRUE);\r
4888       nnew = RealizePalette(hdc);\r
4889       if (nnew > 0) {\r
4890         paletteChanged = TRUE;\r
4891 \r
4892         InvalidateRect(hwnd, &boardRect, FALSE);\r
4893       }\r
4894       ReleaseDC(hwnd, hdc);\r
4895     }\r
4896     break;\r
4897 \r
4898   case WM_QUERYNEWPALETTE:\r
4899     if (!appData.monoMode /*&& paletteChanged*/) {\r
4900       int nnew;\r
4901       HDC hdc = GetDC(hwndMain);\r
4902       paletteChanged = FALSE;\r
4903       SelectPalette(hdc, hPal, FALSE);\r
4904       nnew = RealizePalette(hdc);\r
4905       if (nnew > 0) {\r
4906         InvalidateRect(hwnd, &boardRect, FALSE);\r
4907       }\r
4908       ReleaseDC(hwnd, hdc);\r
4909       return TRUE;\r
4910     }\r
4911     return FALSE;\r
4912 \r
4913   case WM_COMMAND: /* message: command from application menu */\r
4914     wmId    = LOWORD(wParam);\r
4915 \r
4916     switch (wmId) {\r
4917     case IDM_NewGame:\r
4918       ResetGameEvent();\r
4919       SAY("new game enter a move to play against the computer with white");\r
4920       break;\r
4921 \r
4922     case IDM_NewGameFRC:\r
4923       if( NewGameFRC() == 0 ) {\r
4924         ResetGameEvent();\r
4925       }\r
4926       break;\r
4927 \r
4928     case IDM_NewVariant:\r
4929       NewVariantPopup(hwnd);\r
4930       break;\r
4931 \r
4932     case IDM_LoadGame:\r
4933       LoadGameDialog(hwnd, _("Load Game from File"));\r
4934       break;\r
4935 \r
4936     case IDM_LoadNextGame:\r
4937       ReloadGame(1);\r
4938       break;\r
4939 \r
4940     case IDM_LoadPrevGame:\r
4941       ReloadGame(-1);\r
4942       break;\r
4943 \r
4944     case IDM_ReloadGame:\r
4945       ReloadGame(0);\r
4946       break;\r
4947 \r
4948     case IDM_LoadPosition:\r
4949       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4950         Reset(FALSE, TRUE);\r
4951       }\r
4952       number = 1;\r
4953       f = OpenFileDialog(hwnd, "rb", "",\r
4954                          appData.oldSaveStyle ? "pos" : "fen",\r
4955                          POSITION_FILT,\r
4956                          _("Load Position from File"), &number, fileTitle, NULL);\r
4957       if (f != NULL) {\r
4958         LoadPosition(f, number, fileTitle);\r
4959       }\r
4960       break;\r
4961 \r
4962     case IDM_LoadNextPosition:\r
4963       ReloadPosition(1);\r
4964       break;\r
4965 \r
4966     case IDM_LoadPrevPosition:\r
4967       ReloadPosition(-1);\r
4968       break;\r
4969 \r
4970     case IDM_ReloadPosition:\r
4971       ReloadPosition(0);\r
4972       break;\r
4973 \r
4974     case IDM_SaveGame:\r
4975       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4976       f = OpenFileDialog(hwnd, "a", defName,\r
4977                          appData.oldSaveStyle ? "gam" : "pgn",\r
4978                          GAME_FILT,\r
4979                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4980       if (f != NULL) {\r
4981         SaveGame(f, 0, "");\r
4982       }\r
4983       break;\r
4984 \r
4985     case IDM_SavePosition:\r
4986       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4987       f = OpenFileDialog(hwnd, "a", defName,\r
4988                          appData.oldSaveStyle ? "pos" : "fen",\r
4989                          POSITION_FILT,\r
4990                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4991       if (f != NULL) {\r
4992         SavePosition(f, 0, "");\r
4993       }\r
4994       break;\r
4995 \r
4996     case IDM_SaveDiagram:\r
4997       defName = "diagram";\r
4998       f = OpenFileDialog(hwnd, "wb", defName,\r
4999                          "bmp",\r
5000                          DIAGRAM_FILT,\r
5001                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
5002       if (f != NULL) {\r
5003         SaveDiagram(f);\r
5004       }\r
5005       break;\r
5006 \r
5007     case IDM_SaveSelected:\r
5008       f = OpenFileDialog(hwnd, "a", "",\r
5009                          "pgn",\r
5010                          GAME_FILT,\r
5011                          _("Save Game to File"), NULL, fileTitle, NULL);\r
5012       if (f != NULL) {\r
5013         SaveSelected(f, 0, "");\r
5014       }\r
5015       break;\r
5016 \r
5017     case IDM_CreateBook:\r
5018       CreateBookEvent();\r
5019       break;\r
5020 \r
5021     case IDM_CopyGame:\r
5022       CopyGameToClipboard();\r
5023       break;\r
5024 \r
5025     case IDM_PasteGame:\r
5026       PasteGameFromClipboard();\r
5027       break;\r
5028 \r
5029     case IDM_CopyGameListToClipboard:\r
5030       CopyGameListToClipboard();\r
5031       break;\r
5032 \r
5033     /* [AS] Autodetect FEN or PGN data */\r
5034     case IDM_PasteAny:\r
5035       PasteGameOrFENFromClipboard();\r
5036       break;\r
5037 \r
5038     /* [AS] Move history */\r
5039     case IDM_ShowMoveHistory:\r
5040         if( MoveHistoryIsUp() ) {\r
5041             MoveHistoryPopDown();\r
5042         }\r
5043         else {\r
5044             MoveHistoryPopUp();\r
5045         }\r
5046         break;\r
5047 \r
5048     /* [AS] Eval graph */\r
5049     case IDM_ShowEvalGraph:\r
5050         if( EvalGraphIsUp() ) {\r
5051             EvalGraphPopDown();\r
5052         }\r
5053         else {\r
5054             EvalGraphPopUp();\r
5055             SetFocus(hwndMain);\r
5056         }\r
5057         break;\r
5058 \r
5059     /* [AS] Engine output */\r
5060     case IDM_ShowEngineOutput:\r
5061         if( EngineOutputIsUp() ) {\r
5062             EngineOutputPopDown();\r
5063         }\r
5064         else {\r
5065             EngineOutputPopUp();\r
5066         }\r
5067         break;\r
5068 \r
5069     /* [AS] User adjudication */\r
5070     case IDM_UserAdjudication_White:\r
5071         UserAdjudicationEvent( +1 );\r
5072         break;\r
5073 \r
5074     case IDM_UserAdjudication_Black:\r
5075         UserAdjudicationEvent( -1 );\r
5076         break;\r
5077 \r
5078     case IDM_UserAdjudication_Draw:\r
5079         UserAdjudicationEvent( 0 );\r
5080         break;\r
5081 \r
5082     /* [AS] Game list options dialog */\r
5083     case IDM_GameListOptions:\r
5084       GameListOptions();\r
5085       break;\r
5086 \r
5087     case IDM_NewChat:\r
5088       ChatPopUp(NULL);\r
5089       break;\r
5090 \r
5091     case IDM_CopyPosition:\r
5092       CopyFENToClipboard();\r
5093       break;\r
5094 \r
5095     case IDM_PastePosition:\r
5096       PasteFENFromClipboard();\r
5097       break;\r
5098 \r
5099     case IDM_MailMove:\r
5100       MailMoveEvent();\r
5101       break;\r
5102 \r
5103     case IDM_ReloadCMailMsg:\r
5104       Reset(TRUE, TRUE);\r
5105       ReloadCmailMsgEvent(FALSE);\r
5106       break;\r
5107 \r
5108     case IDM_Minimize:\r
5109       ShowWindow(hwnd, SW_MINIMIZE);\r
5110       break;\r
5111 \r
5112     case IDM_Exit:\r
5113       ExitEvent(0);\r
5114       break;\r
5115 \r
5116     case IDM_MachineWhite:\r
5117       MachineWhiteEvent();\r
5118       /*\r
5119        * refresh the tags dialog only if it's visible\r
5120        */\r
5121       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5122           char *tags;\r
5123           tags = PGNTags(&gameInfo);\r
5124           TagsPopUp(tags, CmailMsg());\r
5125           free(tags);\r
5126       }\r
5127       SAY("computer starts playing white");\r
5128       break;\r
5129 \r
5130     case IDM_MachineBlack:\r
5131       MachineBlackEvent();\r
5132       /*\r
5133        * refresh the tags dialog only if it's visible\r
5134        */\r
5135       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5136           char *tags;\r
5137           tags = PGNTags(&gameInfo);\r
5138           TagsPopUp(tags, CmailMsg());\r
5139           free(tags);\r
5140       }\r
5141       SAY("computer starts playing black");\r
5142       break;\r
5143 \r
5144     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5145       if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
5146       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5147       break;\r
5148 \r
5149     case IDM_TwoMachines:\r
5150       TwoMachinesEvent();\r
5151       /*\r
5152 \r
5153        * refresh the tags dialog only if it's visible\r
5154        */\r
5155       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5156           char *tags;\r
5157           tags = PGNTags(&gameInfo);\r
5158           TagsPopUp(tags, CmailMsg());\r
5159           free(tags);\r
5160       }\r
5161       SAY("computer starts playing both sides");\r
5162       break;\r
5163 \r
5164     case IDM_AnalysisMode:\r
5165       if(AnalyzeModeEvent()) {\r
5166         SAY("analyzing current position");\r
5167       }\r
5168       break;\r
5169 \r
5170     case IDM_AnalyzeFile:\r
5171       AnalyzeFileEvent();\r
5172       break;\r
5173 \r
5174     case IDM_IcsClient:\r
5175       IcsClientEvent();\r
5176       break;\r
5177 \r
5178     case IDM_EditGame:\r
5179     case IDM_EditGame2:\r
5180       EditGameEvent();\r
5181       SAY("edit game");\r
5182       break;\r
5183 \r
5184     case IDM_EditPosition:\r
5185     case IDM_EditPosition2:\r
5186       EditPositionEvent();\r
5187       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5188       break;\r
5189 \r
5190     case IDM_Training:\r
5191       TrainingEvent();\r
5192       break;\r
5193 \r
5194     case IDM_ShowGameList:\r
5195       ShowGameListProc();\r
5196       break;\r
5197 \r
5198     case IDM_EditProgs1:\r
5199       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5200       break;\r
5201 \r
5202     case IDM_LoadProg1:\r
5203      LoadEnginePopUp(hwndMain, 0);\r
5204       break;\r
5205 \r
5206     case IDM_LoadProg2:\r
5207      LoadEnginePopUp(hwndMain, 1);\r
5208       break;\r
5209 \r
5210     case IDM_EditServers:\r
5211       EditTagsPopUp(icsNames, &icsNames);\r
5212       break;\r
5213 \r
5214     case IDM_EditTags:\r
5215     case IDM_Tags:\r
5216       EditTagsProc();\r
5217       break;\r
5218 \r
5219     case IDM_EditBook:\r
5220       EditBookEvent();\r
5221       break;\r
5222 \r
5223     case IDM_EditComment:\r
5224     case IDM_Comment:\r
5225       if (commentUp && editComment) {\r
5226         CommentPopDown();\r
5227       } else {\r
5228         EditCommentEvent();\r
5229       }\r
5230       break;\r
5231 \r
5232     case IDM_Pause:\r
5233       PauseEvent();\r
5234       break;\r
5235 \r
5236     case IDM_Accept:\r
5237       AcceptEvent();\r
5238       break;\r
5239 \r
5240     case IDM_Decline:\r
5241       DeclineEvent();\r
5242       break;\r
5243 \r
5244     case IDM_Rematch:\r
5245 \r
5246       RematchEvent();\r
5247       break;\r
5248 \r
5249     case IDM_CallFlag:\r
5250       CallFlagEvent();\r
5251       break;\r
5252 \r
5253     case IDM_Draw:\r
5254       DrawEvent();\r
5255       break;\r
5256 \r
5257     case IDM_Adjourn:\r
5258       AdjournEvent();\r
5259       break;\r
5260 \r
5261     case IDM_Abort:\r
5262       AbortEvent();\r
5263       break;\r
5264 \r
5265     case IDM_Resign:\r
5266       ResignEvent();\r
5267       break;\r
5268 \r
5269     case IDM_StopObserving:\r
5270       StopObservingEvent();\r
5271       break;\r
5272 \r
5273     case IDM_StopExamining:\r
5274       StopExaminingEvent();\r
5275       break;\r
5276 \r
5277     case IDM_Upload:\r
5278       UploadGameEvent();\r
5279       break;\r
5280 \r
5281     case IDM_TypeInMove:\r
5282       TypeInEvent('\000');\r
5283       break;\r
5284 \r
5285     case IDM_TypeInName:\r
5286       PopUpNameDialog('\000');\r
5287       break;\r
5288 \r
5289     case IDM_Backward:\r
5290       BackwardEvent();\r
5291       SetFocus(hwndMain);\r
5292       break;\r
5293 \r
5294     JAWS_MENU_ITEMS\r
5295 \r
5296     case IDM_Forward:\r
5297       ForwardEvent();\r
5298       SetFocus(hwndMain);\r
5299       break;\r
5300 \r
5301     case IDM_ToStart:\r
5302       ToStartEvent();\r
5303       SetFocus(hwndMain);\r
5304       break;\r
5305 \r
5306     case IDM_ToEnd:\r
5307       ToEndEvent();\r
5308       SetFocus(hwndMain);\r
5309       break;\r
5310 \r
5311     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5312     case OPT_GameListPrev:\r
5313       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5314       break;\r
5315 \r
5316     case IDM_Revert:\r
5317       RevertEvent(FALSE);\r
5318       break;\r
5319 \r
5320     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5321       RevertEvent(TRUE);\r
5322       break;\r
5323 \r
5324     case IDM_TruncateGame:\r
5325       TruncateGameEvent();\r
5326       break;\r
5327 \r
5328     case IDM_MoveNow:\r
5329       MoveNowEvent();\r
5330       break;\r
5331 \r
5332     case IDM_RetractMove:\r
5333       RetractMoveEvent();\r
5334       break;\r
5335 \r
5336     case IDM_FlipView:\r
5337       flipView = !flipView;\r
5338       DrawPosition(FALSE, NULL);\r
5339       break;\r
5340 \r
5341     case IDM_FlipClock:\r
5342       flipClock = !flipClock;\r
5343       DisplayBothClocks();\r
5344       DisplayLogos();\r
5345       break;\r
5346 \r
5347     case IDM_MuteSounds:\r
5348       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5349       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5350                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5351       break;\r
5352 \r
5353     case IDM_GeneralOptions:\r
5354       GeneralOptionsPopup(hwnd);\r
5355       DrawPosition(TRUE, NULL);\r
5356       break;\r
5357 \r
5358     case IDM_BoardOptions:\r
5359       BoardOptionsPopup(hwnd);\r
5360       break;\r
5361 \r
5362     case IDM_ThemeOptions:\r
5363       ThemeOptionsPopup(hwnd);\r
5364       break;\r
5365 \r
5366     case IDM_EnginePlayOptions:\r
5367       EnginePlayOptionsPopup(hwnd);\r
5368       break;\r
5369 \r
5370     case IDM_Engine1Options:\r
5371       EngineOptionsPopup(hwnd, &first);\r
5372       break;\r
5373 \r
5374     case IDM_Engine2Options:\r
5375       savedHwnd = hwnd;\r
5376       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5377       EngineOptionsPopup(hwnd, &second);\r
5378       break;\r
5379 \r
5380     case IDM_OptionsUCI:\r
5381       UciOptionsPopup(hwnd);\r
5382       break;\r
5383 \r
5384     case IDM_Tourney:\r
5385       TourneyPopup(hwnd);\r
5386       break;\r
5387 \r
5388     case IDM_IcsOptions:\r
5389       IcsOptionsPopup(hwnd);\r
5390       break;\r
5391 \r
5392     case IDM_Fonts:\r
5393       FontsOptionsPopup(hwnd);\r
5394       break;\r
5395 \r
5396     case IDM_Sounds:\r
5397       SoundOptionsPopup(hwnd);\r
5398       break;\r
5399 \r
5400     case IDM_CommPort:\r
5401       CommPortOptionsPopup(hwnd);\r
5402       break;\r
5403 \r
5404     case IDM_LoadOptions:\r
5405       LoadOptionsPopup(hwnd);\r
5406       break;\r
5407 \r
5408     case IDM_SaveOptions:\r
5409       SaveOptionsPopup(hwnd);\r
5410       break;\r
5411 \r
5412     case IDM_TimeControl:\r
5413       TimeControlOptionsPopup(hwnd);\r
5414       break;\r
5415 \r
5416     case IDM_SaveSettings:\r
5417       SaveSettings(settingsFileName);\r
5418       break;\r
5419 \r
5420     case IDM_SaveSettingsOnExit:\r
5421       saveSettingsOnExit = !saveSettingsOnExit;\r
5422       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5423                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5424                                          MF_CHECKED : MF_UNCHECKED));\r
5425       break;\r
5426 \r
5427     case IDM_Hint:\r
5428       HintEvent();\r
5429       break;\r
5430 \r
5431     case IDM_Book:\r
5432       BookEvent();\r
5433       break;\r
5434 \r
5435     case IDM_AboutGame:\r
5436       AboutGameEvent();\r
5437       break;\r
5438 \r
5439     case IDM_Debug:\r
5440       appData.debugMode = !appData.debugMode;\r
5441       if (appData.debugMode) {\r
5442         char dir[MSG_SIZ];\r
5443         GetCurrentDirectory(MSG_SIZ, dir);\r
5444         SetCurrentDirectory(installDir);\r
5445         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5446         SetCurrentDirectory(dir);\r
5447         setbuf(debugFP, NULL);\r
5448       } else {\r
5449         fclose(debugFP);\r
5450         debugFP = NULL;\r
5451       }\r
5452       break;\r
5453 \r
5454     case IDM_HELPCONTENTS:\r
5455       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5456           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5457           MessageBox (GetFocus(),\r
5458                     _("Unable to activate help"),\r
5459                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5460       }\r
5461       break;\r
5462 \r
5463     case IDM_HELPSEARCH:\r
5464         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5465             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5466         MessageBox (GetFocus(),\r
5467                     _("Unable to activate help"),\r
5468                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5469       }\r
5470       break;\r
5471 \r
5472     case IDM_HELPHELP:\r
5473       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5474         MessageBox (GetFocus(),\r
5475                     _("Unable to activate help"),\r
5476                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5477       }\r
5478       break;\r
5479 \r
5480     case IDM_ABOUT:\r
5481       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5482       DialogBox(hInst, \r
5483         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5484         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5485       FreeProcInstance(lpProc);\r
5486       break;\r
5487 \r
5488     case IDM_DirectCommand1:\r
5489       AskQuestionEvent(_("Direct Command"),\r
5490                        _("Send to chess program:"), "", "1");\r
5491       break;\r
5492     case IDM_DirectCommand2:\r
5493       AskQuestionEvent(_("Direct Command"),\r
5494                        _("Send to second chess program:"), "", "2");\r
5495       break;\r
5496 \r
5497     case EP_WhitePawn:\r
5498       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5499       fromX = fromY = -1;\r
5500       break;\r
5501 \r
5502     case EP_WhiteKnight:\r
5503       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5504       fromX = fromY = -1;\r
5505       break;\r
5506 \r
5507     case EP_WhiteBishop:\r
5508       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5509       fromX = fromY = -1;\r
5510       break;\r
5511 \r
5512     case EP_WhiteRook:\r
5513       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5514       fromX = fromY = -1;\r
5515       break;\r
5516 \r
5517     case EP_WhiteQueen:\r
5518       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5519       fromX = fromY = -1;\r
5520       break;\r
5521 \r
5522     case EP_WhiteFerz:\r
5523       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5524       fromX = fromY = -1;\r
5525       break;\r
5526 \r
5527     case EP_WhiteWazir:\r
5528       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5529       fromX = fromY = -1;\r
5530       break;\r
5531 \r
5532     case EP_WhiteAlfil:\r
5533       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5534       fromX = fromY = -1;\r
5535       break;\r
5536 \r
5537     case EP_WhiteCannon:\r
5538       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5539       fromX = fromY = -1;\r
5540       break;\r
5541 \r
5542     case EP_WhiteCardinal:\r
5543       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5544       fromX = fromY = -1;\r
5545       break;\r
5546 \r
5547     case EP_WhiteMarshall:\r
5548       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5549       fromX = fromY = -1;\r
5550       break;\r
5551 \r
5552     case EP_WhiteKing:\r
5553       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5554       fromX = fromY = -1;\r
5555       break;\r
5556 \r
5557     case EP_BlackPawn:\r
5558       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5559       fromX = fromY = -1;\r
5560       break;\r
5561 \r
5562     case EP_BlackKnight:\r
5563       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5564       fromX = fromY = -1;\r
5565       break;\r
5566 \r
5567     case EP_BlackBishop:\r
5568       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5569       fromX = fromY = -1;\r
5570       break;\r
5571 \r
5572     case EP_BlackRook:\r
5573       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5574       fromX = fromY = -1;\r
5575       break;\r
5576 \r
5577     case EP_BlackQueen:\r
5578       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5579       fromX = fromY = -1;\r
5580       break;\r
5581 \r
5582     case EP_BlackFerz:\r
5583       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5584       fromX = fromY = -1;\r
5585       break;\r
5586 \r
5587     case EP_BlackWazir:\r
5588       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5589       fromX = fromY = -1;\r
5590       break;\r
5591 \r
5592     case EP_BlackAlfil:\r
5593       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5594       fromX = fromY = -1;\r
5595       break;\r
5596 \r
5597     case EP_BlackCannon:\r
5598       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5599       fromX = fromY = -1;\r
5600       break;\r
5601 \r
5602     case EP_BlackCardinal:\r
5603       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5604       fromX = fromY = -1;\r
5605       break;\r
5606 \r
5607     case EP_BlackMarshall:\r
5608       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5609       fromX = fromY = -1;\r
5610       break;\r
5611 \r
5612     case EP_BlackKing:\r
5613       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5614       fromX = fromY = -1;\r
5615       break;\r
5616 \r
5617     case EP_EmptySquare:\r
5618       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5619       fromX = fromY = -1;\r
5620       break;\r
5621 \r
5622     case EP_ClearBoard:\r
5623       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5624       fromX = fromY = -1;\r
5625       break;\r
5626 \r
5627     case EP_White:\r
5628       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5629       fromX = fromY = -1;\r
5630       break;\r
5631 \r
5632     case EP_Black:\r
5633       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5634       fromX = fromY = -1;\r
5635       break;\r
5636 \r
5637     case EP_Promote:\r
5638       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5639       fromX = fromY = -1;\r
5640       break;\r
5641 \r
5642     case EP_Demote:\r
5643       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5644       fromX = fromY = -1;\r
5645       break;\r
5646 \r
5647     case DP_Pawn:\r
5648       DropMenuEvent(WhitePawn, fromX, fromY);\r
5649       fromX = fromY = -1;\r
5650       break;\r
5651 \r
5652     case DP_Knight:\r
5653       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5654       fromX = fromY = -1;\r
5655       break;\r
5656 \r
5657     case DP_Bishop:\r
5658       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5659       fromX = fromY = -1;\r
5660       break;\r
5661 \r
5662     case DP_Rook:\r
5663       DropMenuEvent(WhiteRook, fromX, fromY);\r
5664       fromX = fromY = -1;\r
5665       break;\r
5666 \r
5667     case DP_Queen:\r
5668       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5669       fromX = fromY = -1;\r
5670       break;\r
5671 \r
5672     case IDM_English:\r
5673       barbaric = 0; appData.language = "";\r
5674       TranslateMenus(0);\r
5675       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5676       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5677       lastChecked = wmId;\r
5678       break;\r
5679 \r
5680     default:\r
5681       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5682           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5683       else\r
5684       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5685           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5686           TranslateMenus(0);\r
5687           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5688           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5689           lastChecked = wmId;\r
5690           break;\r
5691       }\r
5692       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5693     }\r
5694     break;\r
5695 \r
5696   case WM_TIMER:\r
5697     switch (wParam) {\r
5698     case CLOCK_TIMER_ID:\r
5699       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5700       clockTimerEvent = 0;\r
5701       DecrementClocks(); /* call into back end */\r
5702       break;\r
5703     case LOAD_GAME_TIMER_ID:\r
5704       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5705       loadGameTimerEvent = 0;\r
5706       AutoPlayGameLoop(); /* call into back end */\r
5707       break;\r
5708     case ANALYSIS_TIMER_ID:\r
5709       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5710                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5711         AnalysisPeriodicEvent(0);\r
5712       } else {\r
5713         KillTimer(hwnd, analysisTimerEvent);\r
5714         analysisTimerEvent = 0;\r
5715       }\r
5716       break;\r
5717     case DELAYED_TIMER_ID:\r
5718       KillTimer(hwnd, delayedTimerEvent);\r
5719       delayedTimerEvent = 0;\r
5720       delayedTimerCallback();\r
5721       break;\r
5722     }\r
5723     break;\r
5724 \r
5725   case WM_USER_Input:\r
5726     InputEvent(hwnd, message, wParam, lParam);\r
5727     break;\r
5728 \r
5729   /* [AS] Also move "attached" child windows */\r
5730   case WM_WINDOWPOSCHANGING:\r
5731 \r
5732     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5733         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5734 \r
5735         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5736             /* Window is moving */\r
5737             RECT rcMain;\r
5738 \r
5739 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5740             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5741             rcMain.right  = wpMain.x + wpMain.width;\r
5742             rcMain.top    = wpMain.y;\r
5743             rcMain.bottom = wpMain.y + wpMain.height;\r
5744             \r
5745             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5746             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5747             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5748             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5749             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5750             wpMain.x = lpwp->x;\r
5751             wpMain.y = lpwp->y;\r
5752 \r
5753         }\r
5754     }\r
5755     break;\r
5756 \r
5757   /* [AS] Snapping */\r
5758   case WM_ENTERSIZEMOVE:\r
5759     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5760     if (hwnd == hwndMain) {\r
5761       doingSizing = TRUE;\r
5762       lastSizing = 0;\r
5763     }\r
5764     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5765     break;\r
5766 \r
5767   case WM_SIZING:\r
5768     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5769     if (hwnd == hwndMain) {\r
5770       lastSizing = wParam;\r
5771     }\r
5772     break;\r
5773 \r
5774   case WM_MOVING:\r
5775     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5776       return OnMoving( &sd, hwnd, wParam, lParam );\r
5777 \r
5778   case WM_EXITSIZEMOVE:\r
5779     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5780     if (hwnd == hwndMain) {\r
5781       RECT client;\r
5782       doingSizing = FALSE;\r
5783       InvalidateRect(hwnd, &boardRect, FALSE);\r
5784       GetClientRect(hwnd, &client);\r
5785       ResizeBoard(client.right, client.bottom, lastSizing);\r
5786       lastSizing = 0;\r
5787       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5788     }\r
5789     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5790     break;\r
5791 \r
5792   case WM_DESTROY: /* message: window being destroyed */\r
5793     PostQuitMessage(0);\r
5794     break;\r
5795 \r
5796   case WM_CLOSE:\r
5797     if (hwnd == hwndMain) {\r
5798       ExitEvent(0);\r
5799     }\r
5800     break;\r
5801 \r
5802   default:      /* Passes it on if unprocessed */\r
5803     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5804   }\r
5805 \r
5806 \r
5807   return 0;\r
5808 }\r
5809 \r
5810 /*---------------------------------------------------------------------------*\\r
5811  *\r
5812  * Misc utility routines\r
5813  *\r
5814 \*---------------------------------------------------------------------------*/\r
5815 \r
5816 /*\r
5817  * Decent random number generator, at least not as bad as Windows\r
5818  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5819  */\r
5820 unsigned int randstate;\r
5821 \r
5822 int\r
5823 myrandom(void)\r
5824 {\r
5825   randstate = randstate * 1664525 + 1013904223;\r
5826   return (int) randstate & 0x7fffffff;\r
5827 }\r
5828 \r
5829 void\r
5830 mysrandom(unsigned int seed)\r
5831 {\r
5832   randstate = seed;\r
5833 }\r
5834 \r
5835 \r
5836 /* \r
5837  * returns TRUE if user selects a different color, FALSE otherwise \r
5838  */\r
5839 \r
5840 BOOL\r
5841 ChangeColor(HWND hwnd, COLORREF *which)\r
5842 {\r
5843   static BOOL firstTime = TRUE;\r
5844   static DWORD customColors[16];\r
5845   CHOOSECOLOR cc;\r
5846   COLORREF newcolor;\r
5847   int i;\r
5848   ColorClass ccl;\r
5849 \r
5850   if (firstTime) {\r
5851     /* Make initial colors in use available as custom colors */\r
5852     /* Should we put the compiled-in defaults here instead? */\r
5853     i = 0;\r
5854     customColors[i++] = lightSquareColor & 0xffffff;\r
5855     customColors[i++] = darkSquareColor & 0xffffff;\r
5856     customColors[i++] = whitePieceColor & 0xffffff;\r
5857     customColors[i++] = blackPieceColor & 0xffffff;\r
5858     customColors[i++] = highlightSquareColor & 0xffffff;\r
5859     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5860 \r
5861     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5862       customColors[i++] = textAttribs[ccl].color;\r
5863     }\r
5864     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5865     firstTime = FALSE;\r
5866   }\r
5867 \r
5868   cc.lStructSize = sizeof(cc);\r
5869   cc.hwndOwner = hwnd;\r
5870   cc.hInstance = NULL;\r
5871   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5872   cc.lpCustColors = (LPDWORD) customColors;\r
5873   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5874 \r
5875   if (!ChooseColor(&cc)) return FALSE;\r
5876 \r
5877   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5878   if (newcolor == *which) return FALSE;\r
5879   *which = newcolor;\r
5880   return TRUE;\r
5881 \r
5882   /*\r
5883   InitDrawingColors();\r
5884   InvalidateRect(hwnd, &boardRect, FALSE);\r
5885   */\r
5886 }\r
5887 \r
5888 BOOLEAN\r
5889 MyLoadSound(MySound *ms)\r
5890 {\r
5891   BOOL ok = FALSE;\r
5892   struct stat st;\r
5893   FILE *f;\r
5894 \r
5895   if (ms->data && ms->flag) free(ms->data);\r
5896   ms->data = NULL;\r
5897 \r
5898   switch (ms->name[0]) {\r
5899   case NULLCHAR:\r
5900     /* Silence */\r
5901     ok = TRUE;\r
5902     break;\r
5903   case '$':\r
5904     /* System sound from Control Panel.  Don't preload here. */\r
5905     ok = TRUE;\r
5906     break;\r
5907   case '!':\r
5908     if (ms->name[1] == NULLCHAR) {\r
5909       /* "!" alone = silence */\r
5910       ok = TRUE;\r
5911     } else {\r
5912       /* Builtin wave resource.  Error if not found. */\r
5913       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5914       if (h == NULL) break;\r
5915       ms->data = (void *)LoadResource(hInst, h);\r
5916       ms->flag = 0; // not maloced, so cannot be freed!\r
5917       if (h == NULL) break;\r
5918       ok = TRUE;\r
5919     }\r
5920     break;\r
5921   default:\r
5922     /* .wav file.  Error if not found. */\r
5923     f = fopen(ms->name, "rb");\r
5924     if (f == NULL) break;\r
5925     if (fstat(fileno(f), &st) < 0) break;\r
5926     ms->data = malloc(st.st_size);\r
5927     ms->flag = 1;\r
5928     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5929     fclose(f);\r
5930     ok = TRUE;\r
5931     break;\r
5932   }\r
5933   if (!ok) {\r
5934     char buf[MSG_SIZ];\r
5935       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5936     DisplayError(buf, GetLastError());\r
5937   }\r
5938   return ok;\r
5939 }\r
5940 \r
5941 BOOLEAN\r
5942 MyPlaySound(MySound *ms)\r
5943 {\r
5944   BOOLEAN ok = FALSE;\r
5945 \r
5946   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5947   switch (ms->name[0]) {\r
5948   case NULLCHAR:\r
5949         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5950     /* Silence */\r
5951     ok = TRUE;\r
5952     break;\r
5953   case '$':\r
5954     /* System sound from Control Panel (deprecated feature).\r
5955        "$" alone or an unset sound name gets default beep (still in use). */\r
5956     if (ms->name[1]) {\r
5957       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5958     }\r
5959     if (!ok) ok = MessageBeep(MB_OK);\r
5960     break; \r
5961   case '!':\r
5962     /* Builtin wave resource, or "!" alone for silence */\r
5963     if (ms->name[1]) {\r
5964       if (ms->data == NULL) return FALSE;\r
5965       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5966     } else {\r
5967       ok = TRUE;\r
5968     }\r
5969     break;\r
5970   default:\r
5971     /* .wav file.  Error if not found. */\r
5972     if (ms->data == NULL) return FALSE;\r
5973     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5974     break;\r
5975   }\r
5976   /* Don't print an error: this can happen innocently if the sound driver\r
5977      is busy; for instance, if another instance of WinBoard is playing\r
5978      a sound at about the same time. */\r
5979   return ok;\r
5980 }\r
5981 \r
5982 \r
5983 LRESULT CALLBACK\r
5984 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5985 {\r
5986   BOOL ok;\r
5987   OPENFILENAME *ofn;\r
5988   static UINT *number; /* gross that this is static */\r
5989 \r
5990   switch (message) {\r
5991   case WM_INITDIALOG: /* message: initialize dialog box */\r
5992     /* Center the dialog over the application window */\r
5993     ofn = (OPENFILENAME *) lParam;\r
5994     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5995       number = (UINT *) ofn->lCustData;\r
5996       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5997     } else {\r
5998       number = NULL;\r
5999     }\r
6000     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6001     Translate(hDlg, 1536);\r
6002     return FALSE;  /* Allow for further processing */\r
6003 \r
6004   case WM_COMMAND:\r
6005     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6006       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6007     }\r
6008     return FALSE;  /* Allow for further processing */\r
6009   }\r
6010   return FALSE;\r
6011 }\r
6012 \r
6013 UINT APIENTRY\r
6014 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6015 {\r
6016   static UINT *number;\r
6017   OPENFILENAME *ofname;\r
6018   OFNOTIFY *ofnot;\r
6019   switch (uiMsg) {\r
6020   case WM_INITDIALOG:\r
6021     Translate(hdlg, DLG_IndexNumber);\r
6022     ofname = (OPENFILENAME *)lParam;\r
6023     number = (UINT *)(ofname->lCustData);\r
6024     break;\r
6025   case WM_NOTIFY:\r
6026     ofnot = (OFNOTIFY *)lParam;\r
6027     if (ofnot->hdr.code == CDN_FILEOK) {\r
6028       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6029     }\r
6030     break;\r
6031   }\r
6032   return 0;\r
6033 }\r
6034 \r
6035 \r
6036 FILE *\r
6037 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6038                char *nameFilt, char *dlgTitle, UINT *number,\r
6039                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6040 {\r
6041   OPENFILENAME openFileName;\r
6042   char buf1[MSG_SIZ];\r
6043   FILE *f;\r
6044 \r
6045   if (fileName == NULL) fileName = buf1;\r
6046   if (defName == NULL) {\r
6047     safeStrCpy(fileName, "*.", 3 );\r
6048     strcat(fileName, defExt);\r
6049   } else {\r
6050     safeStrCpy(fileName, defName, MSG_SIZ );\r
6051   }\r
6052     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
6053   if (number) *number = 0;\r
6054 \r
6055   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6056   openFileName.hwndOwner         = hwnd;\r
6057   openFileName.hInstance         = (HANDLE) hInst;\r
6058   openFileName.lpstrFilter       = nameFilt;\r
6059   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6060   openFileName.nMaxCustFilter    = 0L;\r
6061   openFileName.nFilterIndex      = 1L;\r
6062   openFileName.lpstrFile         = fileName;\r
6063   openFileName.nMaxFile          = MSG_SIZ;\r
6064   openFileName.lpstrFileTitle    = fileTitle;\r
6065   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6066   openFileName.lpstrInitialDir   = NULL;\r
6067   openFileName.lpstrTitle        = dlgTitle;\r
6068   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6069     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6070     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6071     | (oldDialog ? 0 : OFN_EXPLORER);\r
6072   openFileName.nFileOffset       = 0;\r
6073   openFileName.nFileExtension    = 0;\r
6074   openFileName.lpstrDefExt       = defExt;\r
6075   openFileName.lCustData         = (LONG) number;\r
6076   openFileName.lpfnHook          = oldDialog ?\r
6077     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6078   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6079 \r
6080   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6081                         GetOpenFileName(&openFileName)) {\r
6082     /* open the file */\r
6083     f = fopen(openFileName.lpstrFile, write);\r
6084     if (f == NULL) {\r
6085       MessageBox(hwnd, _("File open failed"), NULL,\r
6086                  MB_OK|MB_ICONEXCLAMATION);\r
6087       return NULL;\r
6088     }\r
6089   } else {\r
6090     int err = CommDlgExtendedError();\r
6091     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6092     return FALSE;\r
6093   }\r
6094   return f;\r
6095 }\r
6096 \r
6097 \r
6098 \r
6099 VOID APIENTRY\r
6100 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6101 {\r
6102   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6103 \r
6104   /*\r
6105    * Get the first pop-up menu in the menu template. This is the\r
6106    * menu that TrackPopupMenu displays.\r
6107    */\r
6108   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6109   TranslateOneMenu(10, hmenuTrackPopup);\r
6110 \r
6111   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6112 \r
6113   /*\r
6114    * TrackPopup uses screen coordinates, so convert the\r
6115    * coordinates of the mouse click to screen coordinates.\r
6116    */\r
6117   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6118 \r
6119   /* Draw and track the floating pop-up menu. */\r
6120   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6121                  pt.x, pt.y, 0, hwnd, NULL);\r
6122 \r
6123   /* Destroy the menu.*/\r
6124   DestroyMenu(hmenu);\r
6125 }\r
6126    \r
6127 typedef struct {\r
6128   HWND hDlg, hText;\r
6129   int sizeX, sizeY, newSizeX, newSizeY;\r
6130   HDWP hdwp;\r
6131 } ResizeEditPlusButtonsClosure;\r
6132 \r
6133 BOOL CALLBACK\r
6134 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6135 {\r
6136   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6137   RECT rect;\r
6138   POINT pt;\r
6139 \r
6140   if (hChild == cl->hText) return TRUE;\r
6141   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6142   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6143   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6144   ScreenToClient(cl->hDlg, &pt);\r
6145   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6146     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6147   return TRUE;\r
6148 }\r
6149 \r
6150 /* Resize a dialog that has a (rich) edit field filling most of\r
6151    the top, with a row of buttons below */\r
6152 VOID\r
6153 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6154 {\r
6155   RECT rectText;\r
6156   int newTextHeight, newTextWidth;\r
6157   ResizeEditPlusButtonsClosure cl;\r
6158   \r
6159   /*if (IsIconic(hDlg)) return;*/\r
6160   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6161   \r
6162   cl.hdwp = BeginDeferWindowPos(8);\r
6163 \r
6164   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6165   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6166   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6167   if (newTextHeight < 0) {\r
6168     newSizeY += -newTextHeight;\r
6169     newTextHeight = 0;\r
6170   }\r
6171   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6172     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6173 \r
6174   cl.hDlg = hDlg;\r
6175   cl.hText = hText;\r
6176   cl.sizeX = sizeX;\r
6177   cl.sizeY = sizeY;\r
6178   cl.newSizeX = newSizeX;\r
6179   cl.newSizeY = newSizeY;\r
6180   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6181 \r
6182   EndDeferWindowPos(cl.hdwp);\r
6183 }\r
6184 \r
6185 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6186 {\r
6187     RECT    rChild, rParent;\r
6188     int     wChild, hChild, wParent, hParent;\r
6189     int     wScreen, hScreen, xNew, yNew;\r
6190     HDC     hdc;\r
6191 \r
6192     /* Get the Height and Width of the child window */\r
6193     GetWindowRect (hwndChild, &rChild);\r
6194     wChild = rChild.right - rChild.left;\r
6195     hChild = rChild.bottom - rChild.top;\r
6196 \r
6197     /* Get the Height and Width of the parent window */\r
6198     GetWindowRect (hwndParent, &rParent);\r
6199     wParent = rParent.right - rParent.left;\r
6200     hParent = rParent.bottom - rParent.top;\r
6201 \r
6202     /* Get the display limits */\r
6203     hdc = GetDC (hwndChild);\r
6204     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6205     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6206     ReleaseDC(hwndChild, hdc);\r
6207 \r
6208     /* Calculate new X position, then adjust for screen */\r
6209     xNew = rParent.left + ((wParent - wChild) /2);\r
6210     if (xNew < 0) {\r
6211         xNew = 0;\r
6212     } else if ((xNew+wChild) > wScreen) {\r
6213         xNew = wScreen - wChild;\r
6214     }\r
6215 \r
6216     /* Calculate new Y position, then adjust for screen */\r
6217     if( mode == 0 ) {\r
6218         yNew = rParent.top  + ((hParent - hChild) /2);\r
6219     }\r
6220     else {\r
6221         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6222     }\r
6223 \r
6224     if (yNew < 0) {\r
6225         yNew = 0;\r
6226     } else if ((yNew+hChild) > hScreen) {\r
6227         yNew = hScreen - hChild;\r
6228     }\r
6229 \r
6230     /* Set it, and return */\r
6231     return SetWindowPos (hwndChild, NULL,\r
6232                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6233 }\r
6234 \r
6235 /* Center one window over another */\r
6236 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6237 {\r
6238     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6239 }\r
6240 \r
6241 /*---------------------------------------------------------------------------*\\r
6242  *\r
6243  * Startup Dialog functions\r
6244  *\r
6245 \*---------------------------------------------------------------------------*/\r
6246 void\r
6247 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6248 {\r
6249   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6250 \r
6251   while (*cd != NULL) {\r
6252     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6253     cd++;\r
6254   }\r
6255 }\r
6256 \r
6257 void\r
6258 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6259 {\r
6260   char buf1[MAX_ARG_LEN];\r
6261   int len;\r
6262 \r
6263   if (str[0] == '@') {\r
6264     FILE* f = fopen(str + 1, "r");\r
6265     if (f == NULL) {\r
6266       DisplayFatalError(str + 1, errno, 2);\r
6267       return;\r
6268     }\r
6269     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6270     fclose(f);\r
6271     buf1[len] = NULLCHAR;\r
6272     str = buf1;\r
6273   }\r
6274 \r
6275   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6276 \r
6277   for (;;) {\r
6278     char buf[MSG_SIZ];\r
6279     char *end = strchr(str, '\n');\r
6280     if (end == NULL) return;\r
6281     memcpy(buf, str, end - str);\r
6282     buf[end - str] = NULLCHAR;\r
6283     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6284     str = end + 1;\r
6285   }\r
6286 }\r
6287 \r
6288 void\r
6289 SetStartupDialogEnables(HWND hDlg)\r
6290 {\r
6291   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6292     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6293     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6294   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6295     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6296   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6297     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6298   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6299     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6300   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6301     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6302     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6303     IsDlgButtonChecked(hDlg, OPT_View));\r
6304 }\r
6305 \r
6306 char *\r
6307 QuoteForFilename(char *filename)\r
6308 {\r
6309   int dquote, space;\r
6310   dquote = strchr(filename, '"') != NULL;\r
6311   space = strchr(filename, ' ') != NULL;\r
6312   if (dquote || space) {\r
6313     if (dquote) {\r
6314       return "'";\r
6315     } else {\r
6316       return "\"";\r
6317     }\r
6318   } else {\r
6319     return "";\r
6320   }\r
6321 }\r
6322 \r
6323 VOID\r
6324 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6325 {\r
6326   char buf[MSG_SIZ];\r
6327   char *q;\r
6328 \r
6329   InitComboStringsFromOption(hwndCombo, nthnames);\r
6330   q = QuoteForFilename(nthcp);\r
6331     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6332   if (*nthdir != NULLCHAR) {\r
6333     q = QuoteForFilename(nthdir);\r
6334       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6335   }\r
6336   if (*nthcp == NULLCHAR) {\r
6337     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6338   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6339     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6340     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6341   }\r
6342 }\r
6343 \r
6344 LRESULT CALLBACK\r
6345 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6346 {\r
6347   char buf[MSG_SIZ];\r
6348   HANDLE hwndCombo;\r
6349   char *p;\r
6350 \r
6351   switch (message) {\r
6352   case WM_INITDIALOG:\r
6353     /* Center the dialog */\r
6354     CenterWindow (hDlg, GetDesktopWindow());\r
6355     Translate(hDlg, DLG_Startup);\r
6356     /* Initialize the dialog items */\r
6357     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6358                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6359                   firstChessProgramNames);\r
6360     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6361                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6362                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6363     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6364     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6365       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6366     if (*appData.icsHelper != NULLCHAR) {\r
6367       char *q = QuoteForFilename(appData.icsHelper);\r
6368       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6369     }\r
6370     if (*appData.icsHost == NULLCHAR) {\r
6371       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6372       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6373     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6374       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6375       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6376     }\r
6377 \r
6378     if (appData.icsActive) {\r
6379       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6380     }\r
6381     else if (appData.noChessProgram) {\r
6382       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6383     }\r
6384     else {\r
6385       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6386     }\r
6387 \r
6388     SetStartupDialogEnables(hDlg);\r
6389     return TRUE;\r
6390 \r
6391   case WM_COMMAND:\r
6392     switch (LOWORD(wParam)) {\r
6393     case IDOK:\r
6394       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6395         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6396         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6397         p = buf;\r
6398         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6399         ParseArgs(StringGet, &p);\r
6400         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6401         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6402         p = buf;\r
6403         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6404         ParseArgs(StringGet, &p);\r
6405         SwapEngines(singleList); // ... and then make it 'second'\r
6406 \r
6407         appData.noChessProgram = FALSE;\r
6408         appData.icsActive = FALSE;\r
6409       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6410         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6411         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6412         p = buf;\r
6413         ParseArgs(StringGet, &p);\r
6414         if (appData.zippyPlay) {\r
6415           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6416           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6417           p = buf;\r
6418           ParseArgs(StringGet, &p);\r
6419         }\r
6420       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6421         appData.noChessProgram = TRUE;\r
6422         appData.icsActive = FALSE;\r
6423       } else {\r
6424         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6425                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6426         return TRUE;\r
6427       }\r
6428       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6429         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6430         p = buf;\r
6431         ParseArgs(StringGet, &p);\r
6432       }\r
6433       EndDialog(hDlg, TRUE);\r
6434       return TRUE;\r
6435 \r
6436     case IDCANCEL:\r
6437       ExitEvent(0);\r
6438       return TRUE;\r
6439 \r
6440     case IDM_HELPCONTENTS:\r
6441       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6442         MessageBox (GetFocus(),\r
6443                     _("Unable to activate help"),\r
6444                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6445       }\r
6446       break;\r
6447 \r
6448     default:\r
6449       SetStartupDialogEnables(hDlg);\r
6450       break;\r
6451     }\r
6452     break;\r
6453   }\r
6454   return FALSE;\r
6455 }\r
6456 \r
6457 /*---------------------------------------------------------------------------*\\r
6458  *\r
6459  * About box dialog functions\r
6460  *\r
6461 \*---------------------------------------------------------------------------*/\r
6462 \r
6463 /* Process messages for "About" dialog box */\r
6464 LRESULT CALLBACK\r
6465 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6466 {\r
6467   switch (message) {\r
6468   case WM_INITDIALOG: /* message: initialize dialog box */\r
6469     /* Center the dialog over the application window */\r
6470     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6471     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6472     Translate(hDlg, ABOUTBOX);\r
6473     JAWS_COPYRIGHT\r
6474     return (TRUE);\r
6475 \r
6476   case WM_COMMAND: /* message: received a command */\r
6477     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6478         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6479       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6480       return (TRUE);\r
6481     }\r
6482     break;\r
6483   }\r
6484   return (FALSE);\r
6485 }\r
6486 \r
6487 /*---------------------------------------------------------------------------*\\r
6488  *\r
6489  * Comment Dialog functions\r
6490  *\r
6491 \*---------------------------------------------------------------------------*/\r
6492 \r
6493 LRESULT CALLBACK\r
6494 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6495 {\r
6496   static HANDLE hwndText = NULL;\r
6497   int len, newSizeX, newSizeY;\r
6498   static int sizeX, sizeY;\r
6499   char *str;\r
6500   RECT rect;\r
6501   MINMAXINFO *mmi;\r
6502 \r
6503   switch (message) {\r
6504   case WM_INITDIALOG: /* message: initialize dialog box */\r
6505     /* Initialize the dialog items */\r
6506     Translate(hDlg, DLG_EditComment);\r
6507     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6508     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6509     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6510     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6511     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6512     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6513     SetWindowText(hDlg, commentTitle);\r
6514     if (editComment) {\r
6515       SetFocus(hwndText);\r
6516     } else {\r
6517       SetFocus(GetDlgItem(hDlg, IDOK));\r
6518     }\r
6519     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6520                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6521                 MAKELPARAM(FALSE, 0));\r
6522     /* Size and position the dialog */\r
6523     if (!commentDialog) {\r
6524       commentDialog = hDlg;\r
6525       GetClientRect(hDlg, &rect);\r
6526       sizeX = rect.right;\r
6527       sizeY = rect.bottom;\r
6528       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6529           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6530         WINDOWPLACEMENT wp;\r
6531         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6532         wp.length = sizeof(WINDOWPLACEMENT);\r
6533         wp.flags = 0;\r
6534         wp.showCmd = SW_SHOW;\r
6535         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6536         wp.rcNormalPosition.left = wpComment.x;\r
6537         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6538         wp.rcNormalPosition.top = wpComment.y;\r
6539         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6540         SetWindowPlacement(hDlg, &wp);\r
6541 \r
6542         GetClientRect(hDlg, &rect);\r
6543         newSizeX = rect.right;\r
6544         newSizeY = rect.bottom;\r
6545         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6546                               newSizeX, newSizeY);\r
6547         sizeX = newSizeX;\r
6548         sizeY = newSizeY;\r
6549       }\r
6550     }\r
6551     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6552     return FALSE;\r
6553 \r
6554   case WM_COMMAND: /* message: received a command */\r
6555     switch (LOWORD(wParam)) {\r
6556     case IDOK:\r
6557       if (editComment) {\r
6558         char *p, *q;\r
6559         /* Read changed options from the dialog box */\r
6560         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6561         len = GetWindowTextLength(hwndText);\r
6562         str = (char *) malloc(len + 1);\r
6563         GetWindowText(hwndText, str, len + 1);\r
6564         p = q = str;\r
6565         while (*q) {\r
6566           if (*q == '\r')\r
6567             q++;\r
6568           else\r
6569             *p++ = *q++;\r
6570         }\r
6571         *p = NULLCHAR;\r
6572         ReplaceComment(commentIndex, str);\r
6573         free(str);\r
6574       }\r
6575       CommentPopDown();\r
6576       return TRUE;\r
6577 \r
6578     case IDCANCEL:\r
6579     case OPT_CancelComment:\r
6580       CommentPopDown();\r
6581       return TRUE;\r
6582 \r
6583     case OPT_ClearComment:\r
6584       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6585       break;\r
6586 \r
6587     case OPT_EditComment:\r
6588       EditCommentEvent();\r
6589       return TRUE;\r
6590 \r
6591     default:\r
6592       break;\r
6593     }\r
6594     break;\r
6595 \r
6596   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6597         if( wParam == OPT_CommentText ) {\r
6598             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6599 \r
6600             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6601                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6602                 POINTL pt;\r
6603                 LRESULT index;\r
6604 \r
6605                 pt.x = LOWORD( lpMF->lParam );\r
6606                 pt.y = HIWORD( lpMF->lParam );\r
6607 \r
6608                 if(lpMF->msg == WM_CHAR) {\r
6609                         CHARRANGE sel;\r
6610                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6611                         index = sel.cpMin;\r
6612                 } else\r
6613                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6614 \r
6615                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6616                 len = GetWindowTextLength(hwndText);\r
6617                 str = (char *) malloc(len + 1);\r
6618                 GetWindowText(hwndText, str, len + 1);\r
6619                 ReplaceComment(commentIndex, str);\r
6620                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6621                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6622                 free(str);\r
6623 \r
6624                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6625                 lpMF->msg = WM_USER;\r
6626 \r
6627                 return TRUE;\r
6628             }\r
6629         }\r
6630         break;\r
6631 \r
6632   case WM_SIZE:\r
6633     newSizeX = LOWORD(lParam);\r
6634     newSizeY = HIWORD(lParam);\r
6635     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6636     sizeX = newSizeX;\r
6637     sizeY = newSizeY;\r
6638     break;\r
6639 \r
6640   case WM_GETMINMAXINFO:\r
6641     /* Prevent resizing window too small */\r
6642     mmi = (MINMAXINFO *) lParam;\r
6643     mmi->ptMinTrackSize.x = 100;\r
6644     mmi->ptMinTrackSize.y = 100;\r
6645     break;\r
6646   }\r
6647   return FALSE;\r
6648 }\r
6649 \r
6650 VOID\r
6651 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6652 {\r
6653   FARPROC lpProc;\r
6654   char *p, *q;\r
6655 \r
6656   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6657 \r
6658   if (str == NULL) str = "";\r
6659   p = (char *) malloc(2 * strlen(str) + 2);\r
6660   q = p;\r
6661   while (*str) {\r
6662     if (*str == '\n') *q++ = '\r';\r
6663     *q++ = *str++;\r
6664   }\r
6665   *q = NULLCHAR;\r
6666   if (commentText != NULL) free(commentText);\r
6667 \r
6668   commentIndex = index;\r
6669   commentTitle = title;\r
6670   commentText = p;\r
6671   editComment = edit;\r
6672 \r
6673   if (commentDialog) {\r
6674     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6675     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6676   } else {\r
6677     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6678     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6679                  hwndMain, (DLGPROC)lpProc);\r
6680     FreeProcInstance(lpProc);\r
6681   }\r
6682   commentUp = TRUE;\r
6683 }\r
6684 \r
6685 \r
6686 /*---------------------------------------------------------------------------*\\r
6687  *\r
6688  * Type-in move dialog functions\r
6689  * \r
6690 \*---------------------------------------------------------------------------*/\r
6691 \r
6692 LRESULT CALLBACK\r
6693 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6694 {\r
6695   char move[MSG_SIZ];\r
6696   HWND hInput;\r
6697 \r
6698   switch (message) {\r
6699   case WM_INITDIALOG:\r
6700     move[0] = (char) lParam;\r
6701     move[1] = NULLCHAR;\r
6702     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6703     Translate(hDlg, DLG_TypeInMove);\r
6704     hInput = GetDlgItem(hDlg, OPT_Move);\r
6705     SetWindowText(hInput, move);\r
6706     SetFocus(hInput);\r
6707     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6708     return FALSE;\r
6709 \r
6710   case WM_COMMAND:\r
6711     switch (LOWORD(wParam)) {\r
6712     case IDOK:\r
6713 \r
6714       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6715       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6716       TypeInDoneEvent(move);\r
6717       EndDialog(hDlg, TRUE);\r
6718       return TRUE;\r
6719     case IDCANCEL:\r
6720       EndDialog(hDlg, FALSE);\r
6721       return TRUE;\r
6722     default:\r
6723       break;\r
6724     }\r
6725     break;\r
6726   }\r
6727   return FALSE;\r
6728 }\r
6729 \r
6730 VOID\r
6731 PopUpMoveDialog(char firstchar)\r
6732 {\r
6733     FARPROC lpProc;\r
6734 \r
6735       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6736       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6737         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6738       FreeProcInstance(lpProc);\r
6739 }\r
6740 \r
6741 /*---------------------------------------------------------------------------*\\r
6742  *\r
6743  * Type-in name dialog functions\r
6744  * \r
6745 \*---------------------------------------------------------------------------*/\r
6746 \r
6747 LRESULT CALLBACK\r
6748 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6749 {\r
6750   char move[MSG_SIZ];\r
6751   HWND hInput;\r
6752 \r
6753   switch (message) {\r
6754   case WM_INITDIALOG:\r
6755     move[0] = (char) lParam;\r
6756     move[1] = NULLCHAR;\r
6757     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6758     Translate(hDlg, DLG_TypeInName);\r
6759     hInput = GetDlgItem(hDlg, OPT_Name);\r
6760     SetWindowText(hInput, move);\r
6761     SetFocus(hInput);\r
6762     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6763     return FALSE;\r
6764 \r
6765   case WM_COMMAND:\r
6766     switch (LOWORD(wParam)) {\r
6767     case IDOK:\r
6768       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6769       appData.userName = strdup(move);\r
6770       SetUserLogo(); DisplayLogos();\r
6771       SetGameInfo();\r
6772       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6773         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6774         DisplayTitle(move);\r
6775       }\r
6776 \r
6777 \r
6778       EndDialog(hDlg, TRUE);\r
6779       return TRUE;\r
6780     case IDCANCEL:\r
6781       EndDialog(hDlg, FALSE);\r
6782       return TRUE;\r
6783     default:\r
6784       break;\r
6785     }\r
6786     break;\r
6787   }\r
6788   return FALSE;\r
6789 }\r
6790 \r
6791 VOID\r
6792 PopUpNameDialog(char firstchar)\r
6793 {\r
6794     FARPROC lpProc;\r
6795     \r
6796       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6797       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6798         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6799       FreeProcInstance(lpProc);\r
6800 }\r
6801 \r
6802 /*---------------------------------------------------------------------------*\\r
6803  *\r
6804  *  Error dialogs\r
6805  * \r
6806 \*---------------------------------------------------------------------------*/\r
6807 \r
6808 /* Nonmodal error box */\r
6809 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6810                              WPARAM wParam, LPARAM lParam);\r
6811 \r
6812 VOID\r
6813 ErrorPopUp(char *title, char *content)\r
6814 {\r
6815   FARPROC lpProc;\r
6816   char *p, *q;\r
6817   BOOLEAN modal = hwndMain == NULL;\r
6818 \r
6819   p = content;\r
6820   q = errorMessage;\r
6821   while (*p) {\r
6822     if (*p == '\n') {\r
6823       if (modal) {\r
6824         *q++ = ' ';\r
6825         p++;\r
6826       } else {\r
6827         *q++ = '\r';\r
6828         *q++ = *p++;\r
6829       }\r
6830     } else {\r
6831       *q++ = *p++;\r
6832     }\r
6833   }\r
6834   *q = NULLCHAR;\r
6835   strncpy(errorTitle, title, sizeof(errorTitle));\r
6836   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6837   \r
6838   if (modal) {\r
6839     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6840   } else {\r
6841     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6842     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6843                  hwndMain, (DLGPROC)lpProc);\r
6844     FreeProcInstance(lpProc);\r
6845   }\r
6846 }\r
6847 \r
6848 VOID\r
6849 ErrorPopDown()\r
6850 {\r
6851   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6852   if (errorDialog == NULL) return;\r
6853   DestroyWindow(errorDialog);\r
6854   errorDialog = NULL;\r
6855   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6856 }\r
6857 \r
6858 LRESULT CALLBACK\r
6859 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6860 {\r
6861   RECT rChild;\r
6862 \r
6863   switch (message) {\r
6864   case WM_INITDIALOG:\r
6865     GetWindowRect(hDlg, &rChild);\r
6866 \r
6867     /*\r
6868     SetWindowPos(hDlg, NULL, rChild.left,\r
6869       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6870       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6871     */\r
6872 \r
6873     /* \r
6874         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6875         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6876         and it doesn't work when you resize the dialog.\r
6877         For now, just give it a default position.\r
6878     */\r
6879     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6880     Translate(hDlg, DLG_Error);\r
6881 \r
6882     errorDialog = hDlg;\r
6883     SetWindowText(hDlg, errorTitle);\r
6884     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6885     return FALSE;\r
6886 \r
6887   case WM_COMMAND:\r
6888     switch (LOWORD(wParam)) {\r
6889     case IDOK:\r
6890     case IDCANCEL:\r
6891       if (errorDialog == hDlg) errorDialog = NULL;\r
6892       DestroyWindow(hDlg);\r
6893       return TRUE;\r
6894 \r
6895     default:\r
6896       break;\r
6897     }\r
6898     break;\r
6899   }\r
6900   return FALSE;\r
6901 }\r
6902 \r
6903 #ifdef GOTHIC\r
6904 HWND gothicDialog = NULL;\r
6905 \r
6906 LRESULT CALLBACK\r
6907 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6908 {\r
6909   RECT rChild;\r
6910   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6911 \r
6912   switch (message) {\r
6913   case WM_INITDIALOG:\r
6914     GetWindowRect(hDlg, &rChild);\r
6915 \r
6916     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6917                                                              SWP_NOZORDER);\r
6918 \r
6919     /* \r
6920         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6921         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6922         and it doesn't work when you resize the dialog.\r
6923         For now, just give it a default position.\r
6924     */\r
6925     gothicDialog = hDlg;\r
6926     SetWindowText(hDlg, errorTitle);\r
6927     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6928     return FALSE;\r
6929 \r
6930   case WM_COMMAND:\r
6931     switch (LOWORD(wParam)) {\r
6932     case IDOK:\r
6933     case IDCANCEL:\r
6934       if (errorDialog == hDlg) errorDialog = NULL;\r
6935       DestroyWindow(hDlg);\r
6936       return TRUE;\r
6937 \r
6938     default:\r
6939       break;\r
6940     }\r
6941     break;\r
6942   }\r
6943   return FALSE;\r
6944 }\r
6945 \r
6946 VOID\r
6947 GothicPopUp(char *title, VariantClass variant)\r
6948 {\r
6949   FARPROC lpProc;\r
6950   static char *lastTitle;\r
6951 \r
6952   strncpy(errorTitle, title, sizeof(errorTitle));\r
6953   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6954 \r
6955   if(lastTitle != title && gothicDialog != NULL) {\r
6956     DestroyWindow(gothicDialog);\r
6957     gothicDialog = NULL;\r
6958   }\r
6959   if(variant != VariantNormal && gothicDialog == NULL) {\r
6960     title = lastTitle;\r
6961     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6962     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6963                  hwndMain, (DLGPROC)lpProc);\r
6964     FreeProcInstance(lpProc);\r
6965   }\r
6966 }\r
6967 #endif\r
6968 \r
6969 /*---------------------------------------------------------------------------*\\r
6970  *\r
6971  *  Ics Interaction console functions\r
6972  *\r
6973 \*---------------------------------------------------------------------------*/\r
6974 \r
6975 #define HISTORY_SIZE 64\r
6976 static char *history[HISTORY_SIZE];\r
6977 int histIn = 0, histP = 0;\r
6978 \r
6979 \r
6980 VOID\r
6981 SaveInHistory(char *cmd)\r
6982 {\r
6983   if (history[histIn] != NULL) {\r
6984     free(history[histIn]);\r
6985     history[histIn] = NULL;\r
6986   }\r
6987   if (*cmd == NULLCHAR) return;\r
6988   history[histIn] = StrSave(cmd);\r
6989   histIn = (histIn + 1) % HISTORY_SIZE;\r
6990   if (history[histIn] != NULL) {\r
6991     free(history[histIn]);\r
6992 \r
6993     history[histIn] = NULL;\r
6994   }\r
6995   histP = histIn;\r
6996 }\r
6997 \r
6998 char *\r
6999 PrevInHistory(char *cmd)\r
7000 {\r
7001   int newhp;\r
7002   if (histP == histIn) {\r
7003     if (history[histIn] != NULL) free(history[histIn]);\r
7004     history[histIn] = StrSave(cmd);\r
7005   }\r
7006   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7007   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7008   histP = newhp;\r
7009   return history[histP];\r
7010 }\r
7011 \r
7012 char *\r
7013 NextInHistory()\r
7014 {\r
7015   if (histP == histIn) return NULL;\r
7016   histP = (histP + 1) % HISTORY_SIZE;\r
7017   return history[histP];   \r
7018 }\r
7019 \r
7020 HMENU\r
7021 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7022 {\r
7023   HMENU hmenu, h;\r
7024   int i = 0;\r
7025   hmenu = LoadMenu(hInst, "TextMenu");\r
7026   h = GetSubMenu(hmenu, 0);\r
7027   while (e->item) {\r
7028     if (strcmp(e->item, "-") == 0) {\r
7029       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7030     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
7031       int flags = MF_STRING, j = 0;\r
7032       if (e->item[0] == '|') {\r
7033         flags |= MF_MENUBARBREAK;\r
7034         j++;\r
7035       }\r
7036       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
7037       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
7038     }\r
7039     e++;\r
7040     i++;\r
7041   } \r
7042   return hmenu;\r
7043 }\r
7044 \r
7045 WNDPROC consoleTextWindowProc;\r
7046 \r
7047 void\r
7048 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7049 {\r
7050   char buf[MSG_SIZ], name[MSG_SIZ];\r
7051   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7052   CHARRANGE sel;\r
7053 \r
7054   if (!getname) {\r
7055     SetWindowText(hInput, command);\r
7056     if (immediate) {\r
7057       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7058     } else {\r
7059       sel.cpMin = 999999;\r
7060       sel.cpMax = 999999;\r
7061       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7062       SetFocus(hInput);\r
7063     }\r
7064     return;\r
7065   }    \r
7066   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7067   if (sel.cpMin == sel.cpMax) {\r
7068     /* Expand to surrounding word */\r
7069     TEXTRANGE tr;\r
7070     do {\r
7071       tr.chrg.cpMax = sel.cpMin;\r
7072       tr.chrg.cpMin = --sel.cpMin;\r
7073       if (sel.cpMin < 0) break;\r
7074       tr.lpstrText = name;\r
7075       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7076     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7077     sel.cpMin++;\r
7078 \r
7079     do {\r
7080       tr.chrg.cpMin = sel.cpMax;\r
7081       tr.chrg.cpMax = ++sel.cpMax;\r
7082       tr.lpstrText = name;\r
7083       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7084     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7085     sel.cpMax--;\r
7086 \r
7087     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7088       MessageBeep(MB_ICONEXCLAMATION);\r
7089       return;\r
7090     }\r
7091     tr.chrg = sel;\r
7092     tr.lpstrText = name;\r
7093     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7094   } else {\r
7095     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7096       MessageBeep(MB_ICONEXCLAMATION);\r
7097       return;\r
7098     }\r
7099     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7100   }\r
7101   if (immediate) {\r
7102     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7103     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7104     SetWindowText(hInput, buf);\r
7105     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7106   } else {\r
7107     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7108       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7109     SetWindowText(hInput, buf);\r
7110     sel.cpMin = 999999;\r
7111     sel.cpMax = 999999;\r
7112     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7113     SetFocus(hInput);\r
7114   }\r
7115 }\r
7116 \r
7117 LRESULT CALLBACK \r
7118 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7119 {\r
7120   HWND hInput;\r
7121   CHARRANGE sel;\r
7122 \r
7123   switch (message) {\r
7124   case WM_KEYDOWN:\r
7125     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7126     if(wParam=='R') return 0;\r
7127     switch (wParam) {\r
7128     case VK_PRIOR:\r
7129       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7130       return 0;\r
7131     case VK_NEXT:\r
7132       sel.cpMin = 999999;\r
7133       sel.cpMax = 999999;\r
7134       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7135       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7136       return 0;\r
7137     }\r
7138     break;\r
7139   case WM_CHAR:\r
7140    if(wParam != '\022') {\r
7141     if (wParam == '\t') {\r
7142       if (GetKeyState(VK_SHIFT) < 0) {\r
7143         /* shifted */\r
7144         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7145         if (buttonDesc[0].hwnd) {\r
7146           SetFocus(buttonDesc[0].hwnd);\r
7147         } else {\r
7148           SetFocus(hwndMain);\r
7149         }\r
7150       } else {\r
7151         /* unshifted */\r
7152         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7153       }\r
7154     } else {\r
7155       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7156       JAWS_DELETE( SetFocus(hInput); )\r
7157       SendMessage(hInput, message, wParam, lParam);\r
7158     }\r
7159     return 0;\r
7160    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7161    lParam = -1;\r
7162   case WM_RBUTTONDOWN:\r
7163     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7164       /* Move selection here if it was empty */\r
7165       POINT pt;\r
7166       pt.x = LOWORD(lParam);\r
7167       pt.y = HIWORD(lParam);\r
7168       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7169       if (sel.cpMin == sel.cpMax) {\r
7170         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7171         sel.cpMax = sel.cpMin;\r
7172         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7173       }\r
7174       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7175 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7176       POINT pt;\r
7177       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7178       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7179       if (sel.cpMin == sel.cpMax) {\r
7180         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7181         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7182       }\r
7183       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7184         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7185       }\r
7186       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7187       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7188       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7189       MenuPopup(hwnd, pt, hmenu, -1);\r
7190 }\r
7191     }\r
7192     return 0;\r
7193   case WM_RBUTTONUP:\r
7194     if (GetKeyState(VK_SHIFT) & ~1) {\r
7195       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7196         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7197     }\r
7198     return 0;\r
7199   case WM_PASTE:\r
7200     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7201     SetFocus(hInput);\r
7202     return SendMessage(hInput, message, wParam, lParam);\r
7203   case WM_MBUTTONDOWN:\r
7204     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7205   case WM_COMMAND:\r
7206     switch (LOWORD(wParam)) {\r
7207     case IDM_QuickPaste:\r
7208       {\r
7209         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7210         if (sel.cpMin == sel.cpMax) {\r
7211           MessageBeep(MB_ICONEXCLAMATION);\r
7212           return 0;\r
7213         }\r
7214         SendMessage(hwnd, WM_COPY, 0, 0);\r
7215         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7216         SendMessage(hInput, WM_PASTE, 0, 0);\r
7217         SetFocus(hInput);\r
7218         return 0;\r
7219       }\r
7220     case IDM_Cut:\r
7221       SendMessage(hwnd, WM_CUT, 0, 0);\r
7222       return 0;\r
7223     case IDM_Paste:\r
7224       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7225       return 0;\r
7226     case IDM_Copy:\r
7227       SendMessage(hwnd, WM_COPY, 0, 0);\r
7228       return 0;\r
7229     default:\r
7230       {\r
7231         int i = LOWORD(wParam) - IDM_CommandX;\r
7232         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7233             icsTextMenuEntry[i].command != NULL) {\r
7234           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7235                    icsTextMenuEntry[i].getname,\r
7236                    icsTextMenuEntry[i].immediate);\r
7237           return 0;\r
7238         }\r
7239       }\r
7240       break;\r
7241     }\r
7242     break;\r
7243   }\r
7244   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7245 }\r
7246 \r
7247 WNDPROC consoleInputWindowProc;\r
7248 \r
7249 LRESULT CALLBACK\r
7250 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7251 {\r
7252   char buf[MSG_SIZ];\r
7253   char *p;\r
7254   static BOOL sendNextChar = FALSE;\r
7255   static BOOL quoteNextChar = FALSE;\r
7256   InputSource *is = consoleInputSource;\r
7257   CHARFORMAT cf;\r
7258   CHARRANGE sel;\r
7259 \r
7260   switch (message) {\r
7261   case WM_CHAR:\r
7262     if (!appData.localLineEditing || sendNextChar) {\r
7263       is->buf[0] = (CHAR) wParam;\r
7264       is->count = 1;\r
7265       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7266       sendNextChar = FALSE;\r
7267       return 0;\r
7268     }\r
7269     if (quoteNextChar) {\r
7270       buf[0] = (char) wParam;\r
7271       buf[1] = NULLCHAR;\r
7272       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7273       quoteNextChar = FALSE;\r
7274       return 0;\r
7275     }\r
7276     switch (wParam) {\r
7277     case '\r':   /* Enter key */\r
7278       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7279       if (consoleEcho) SaveInHistory(is->buf);\r
7280       is->buf[is->count++] = '\n';\r
7281       is->buf[is->count] = NULLCHAR;\r
7282       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7283       if (consoleEcho) {\r
7284         ConsoleOutput(is->buf, is->count, TRUE);\r
7285       } else if (appData.localLineEditing) {\r
7286         ConsoleOutput("\n", 1, TRUE);\r
7287       }\r
7288       /* fall thru */\r
7289     case '\033': /* Escape key */\r
7290       SetWindowText(hwnd, "");\r
7291       cf.cbSize = sizeof(CHARFORMAT);\r
7292       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7293       if (consoleEcho) {\r
7294         cf.crTextColor = textAttribs[ColorNormal].color;\r
7295       } else {\r
7296         cf.crTextColor = COLOR_ECHOOFF;\r
7297       }\r
7298       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7299       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7300       return 0;\r
7301     case '\t':   /* Tab key */\r
7302       if (GetKeyState(VK_SHIFT) < 0) {\r
7303         /* shifted */\r
7304         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7305       } else {\r
7306         /* unshifted */\r
7307         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7308         if (buttonDesc[0].hwnd) {\r
7309           SetFocus(buttonDesc[0].hwnd);\r
7310         } else {\r
7311           SetFocus(hwndMain);\r
7312         }\r
7313       }\r
7314       return 0;\r
7315     case '\023': /* Ctrl+S */\r
7316       sendNextChar = TRUE;\r
7317       return 0;\r
7318     case '\021': /* Ctrl+Q */\r
7319       quoteNextChar = TRUE;\r
7320       return 0;\r
7321     JAWS_REPLAY\r
7322     default:\r
7323       break;\r
7324     }\r
7325     break;\r
7326   case WM_KEYDOWN:\r
7327     switch (wParam) {\r
7328     case VK_UP:\r
7329       GetWindowText(hwnd, buf, MSG_SIZ);\r
7330       p = PrevInHistory(buf);\r
7331       if (p != NULL) {\r
7332         SetWindowText(hwnd, p);\r
7333         sel.cpMin = 999999;\r
7334         sel.cpMax = 999999;\r
7335         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7336         return 0;\r
7337       }\r
7338       break;\r
7339     case VK_DOWN:\r
7340       p = NextInHistory();\r
7341       if (p != NULL) {\r
7342         SetWindowText(hwnd, p);\r
7343         sel.cpMin = 999999;\r
7344         sel.cpMax = 999999;\r
7345         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7346         return 0;\r
7347       }\r
7348       break;\r
7349     case VK_HOME:\r
7350     case VK_END:\r
7351       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7352       /* fall thru */\r
7353     case VK_PRIOR:\r
7354     case VK_NEXT:\r
7355       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7356       return 0;\r
7357     }\r
7358     break;\r
7359   case WM_MBUTTONDOWN:\r
7360     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7361       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7362     break;\r
7363   case WM_RBUTTONUP:\r
7364     if (GetKeyState(VK_SHIFT) & ~1) {\r
7365       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7366         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7367     } else {\r
7368       POINT pt;\r
7369       HMENU hmenu;\r
7370       hmenu = LoadMenu(hInst, "InputMenu");\r
7371       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7372       if (sel.cpMin == sel.cpMax) {\r
7373         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7374         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7375       }\r
7376       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7377         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7378       }\r
7379       pt.x = LOWORD(lParam);\r
7380       pt.y = HIWORD(lParam);\r
7381       MenuPopup(hwnd, pt, hmenu, -1);\r
7382     }\r
7383     return 0;\r
7384   case WM_COMMAND:\r
7385     switch (LOWORD(wParam)) { \r
7386     case IDM_Undo:\r
7387       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7388       return 0;\r
7389     case IDM_SelectAll:\r
7390       sel.cpMin = 0;\r
7391       sel.cpMax = -1; /*999999?*/\r
7392       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7393       return 0;\r
7394     case IDM_Cut:\r
7395       SendMessage(hwnd, WM_CUT, 0, 0);\r
7396       return 0;\r
7397     case IDM_Paste:\r
7398       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7399       return 0;\r
7400     case IDM_Copy:\r
7401       SendMessage(hwnd, WM_COPY, 0, 0);\r
7402       return 0;\r
7403     }\r
7404     break;\r
7405   }\r
7406   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7407 }\r
7408 \r
7409 #define CO_MAX  100000\r
7410 #define CO_TRIM   1000\r
7411 \r
7412 LRESULT CALLBACK\r
7413 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7414 {\r
7415   static SnapData sd;\r
7416   HWND hText, hInput;\r
7417   RECT rect;\r
7418   static int sizeX, sizeY;\r
7419   int newSizeX, newSizeY;\r
7420   MINMAXINFO *mmi;\r
7421   WORD wMask;\r
7422 \r
7423   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7424   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7425 \r
7426   switch (message) {\r
7427   case WM_NOTIFY:\r
7428     if (((NMHDR*)lParam)->code == EN_LINK)\r
7429     {\r
7430       ENLINK *pLink = (ENLINK*)lParam;\r
7431       if (pLink->msg == WM_LBUTTONUP)\r
7432       {\r
7433         TEXTRANGE tr;\r
7434 \r
7435         tr.chrg = pLink->chrg;\r
7436         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7437         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7438         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7439         free(tr.lpstrText);\r
7440       }\r
7441     }\r
7442     break;\r
7443   case WM_INITDIALOG: /* message: initialize dialog box */\r
7444     hwndConsole = hDlg;\r
7445     SetFocus(hInput);\r
7446     consoleTextWindowProc = (WNDPROC)\r
7447       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7448     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7449     consoleInputWindowProc = (WNDPROC)\r
7450       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7451     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7452     Colorize(ColorNormal, TRUE);\r
7453     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7454     ChangedConsoleFont();\r
7455     GetClientRect(hDlg, &rect);\r
7456     sizeX = rect.right;\r
7457     sizeY = rect.bottom;\r
7458     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7459         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7460       WINDOWPLACEMENT wp;\r
7461       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7462       wp.length = sizeof(WINDOWPLACEMENT);\r
7463       wp.flags = 0;\r
7464       wp.showCmd = SW_SHOW;\r
7465       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7466       wp.rcNormalPosition.left = wpConsole.x;\r
7467       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7468       wp.rcNormalPosition.top = wpConsole.y;\r
7469       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7470       SetWindowPlacement(hDlg, &wp);\r
7471     }\r
7472 \r
7473    // [HGM] Chessknight's change 2004-07-13\r
7474    else { /* Determine Defaults */\r
7475        WINDOWPLACEMENT wp;\r
7476        wpConsole.x = wpMain.width + 1;\r
7477        wpConsole.y = wpMain.y;\r
7478        wpConsole.width = screenWidth -  wpMain.width;\r
7479        wpConsole.height = wpMain.height;\r
7480        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7481        wp.length = sizeof(WINDOWPLACEMENT);\r
7482        wp.flags = 0;\r
7483        wp.showCmd = SW_SHOW;\r
7484        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7485        wp.rcNormalPosition.left = wpConsole.x;\r
7486        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7487        wp.rcNormalPosition.top = wpConsole.y;\r
7488        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7489        SetWindowPlacement(hDlg, &wp);\r
7490     }\r
7491 \r
7492    // Allow hText to highlight URLs and send notifications on them\r
7493    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7494    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7495    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7496    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7497 \r
7498     return FALSE;\r
7499 \r
7500   case WM_SETFOCUS:\r
7501     SetFocus(hInput);\r
7502     return 0;\r
7503 \r
7504   case WM_CLOSE:\r
7505     ExitEvent(0);\r
7506     /* not reached */\r
7507     break;\r
7508 \r
7509   case WM_SIZE:\r
7510     if (IsIconic(hDlg)) break;\r
7511     newSizeX = LOWORD(lParam);\r
7512     newSizeY = HIWORD(lParam);\r
7513     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7514       RECT rectText, rectInput;\r
7515       POINT pt;\r
7516       int newTextHeight, newTextWidth;\r
7517       GetWindowRect(hText, &rectText);\r
7518       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7519       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7520       if (newTextHeight < 0) {\r
7521         newSizeY += -newTextHeight;\r
7522         newTextHeight = 0;\r
7523       }\r
7524       SetWindowPos(hText, NULL, 0, 0,\r
7525         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7526       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7527       pt.x = rectInput.left;\r
7528       pt.y = rectInput.top + newSizeY - sizeY;\r
7529       ScreenToClient(hDlg, &pt);\r
7530       SetWindowPos(hInput, NULL, \r
7531         pt.x, pt.y, /* needs client coords */   \r
7532         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7533         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7534     }\r
7535     sizeX = newSizeX;\r
7536     sizeY = newSizeY;\r
7537     break;\r
7538 \r
7539   case WM_GETMINMAXINFO:\r
7540     /* Prevent resizing window too small */\r
7541     mmi = (MINMAXINFO *) lParam;\r
7542     mmi->ptMinTrackSize.x = 100;\r
7543     mmi->ptMinTrackSize.y = 100;\r
7544     break;\r
7545 \r
7546   /* [AS] Snapping */\r
7547   case WM_ENTERSIZEMOVE:\r
7548     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7549 \r
7550   case WM_SIZING:\r
7551     return OnSizing( &sd, hDlg, wParam, lParam );\r
7552 \r
7553   case WM_MOVING:\r
7554     return OnMoving( &sd, hDlg, wParam, lParam );\r
7555 \r
7556   case WM_EXITSIZEMOVE:\r
7557         UpdateICSWidth(hText);\r
7558     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7559   }\r
7560 \r
7561   return DefWindowProc(hDlg, message, wParam, lParam);\r
7562 }\r
7563 \r
7564 \r
7565 VOID\r
7566 ConsoleCreate()\r
7567 {\r
7568   HWND hCons;\r
7569   if (hwndConsole) return;\r
7570   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7571   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7572 }\r
7573 \r
7574 \r
7575 VOID\r
7576 ConsoleOutput(char* data, int length, int forceVisible)\r
7577 {\r
7578   HWND hText;\r
7579   int trim, exlen;\r
7580   char *p, *q;\r
7581   char buf[CO_MAX+1];\r
7582   POINT pEnd;\r
7583   RECT rect;\r
7584   static int delayLF = 0;\r
7585   CHARRANGE savesel, sel;\r
7586 \r
7587   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7588   p = data;\r
7589   q = buf;\r
7590   if (delayLF) {\r
7591     *q++ = '\r';\r
7592     *q++ = '\n';\r
7593     delayLF = 0;\r
7594   }\r
7595   while (length--) {\r
7596     if (*p == '\n') {\r
7597       if (*++p) {\r
7598         *q++ = '\r';\r
7599         *q++ = '\n';\r
7600       } else {\r
7601         delayLF = 1;\r
7602       }\r
7603     } else if (*p == '\007') {\r
7604        MyPlaySound(&sounds[(int)SoundBell]);\r
7605        p++;\r
7606     } else {\r
7607       *q++ = *p++;\r
7608     }\r
7609   }\r
7610   *q = NULLCHAR;\r
7611   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7612   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7613   /* Save current selection */\r
7614   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7615   exlen = GetWindowTextLength(hText);\r
7616   /* Find out whether current end of text is visible */\r
7617   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7618   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7619   /* Trim existing text if it's too long */\r
7620   if (exlen + (q - buf) > CO_MAX) {\r
7621     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7622     sel.cpMin = 0;\r
7623     sel.cpMax = trim;\r
7624     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7625     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7626     exlen -= trim;\r
7627     savesel.cpMin -= trim;\r
7628     savesel.cpMax -= trim;\r
7629     if (exlen < 0) exlen = 0;\r
7630     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7631     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7632   }\r
7633   /* Append the new text */\r
7634   sel.cpMin = exlen;\r
7635   sel.cpMax = exlen;\r
7636   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7637   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7638   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7639   if (forceVisible || exlen == 0 ||\r
7640       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7641        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7642     /* Scroll to make new end of text visible if old end of text\r
7643        was visible or new text is an echo of user typein */\r
7644     sel.cpMin = 9999999;\r
7645     sel.cpMax = 9999999;\r
7646     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7647     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7648     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7649     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7650   }\r
7651   if (savesel.cpMax == exlen || forceVisible) {\r
7652     /* Move insert point to new end of text if it was at the old\r
7653        end of text or if the new text is an echo of user typein */\r
7654     sel.cpMin = 9999999;\r
7655     sel.cpMax = 9999999;\r
7656     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7657   } else {\r
7658     /* Restore previous selection */\r
7659     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7660   }\r
7661   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7662 }\r
7663 \r
7664 /*---------*/\r
7665 \r
7666 \r
7667 void\r
7668 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7669 {\r
7670   char buf[100];\r
7671   char *str;\r
7672   COLORREF oldFg, oldBg;\r
7673   HFONT oldFont;\r
7674   RECT rect;\r
7675 \r
7676   if(copyNumber > 1)\r
7677     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7678 \r
7679   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7680   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7681   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7682 \r
7683   rect.left = x;\r
7684   rect.right = x + squareSize;\r
7685   rect.top  = y;\r
7686   rect.bottom = y + squareSize;\r
7687   str = buf;\r
7688 \r
7689   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7690                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7691              y, ETO_CLIPPED|ETO_OPAQUE,\r
7692              &rect, str, strlen(str), NULL);\r
7693 \r
7694   (void) SetTextColor(hdc, oldFg);\r
7695   (void) SetBkColor(hdc, oldBg);\r
7696   (void) SelectObject(hdc, oldFont);\r
7697 }\r
7698 \r
7699 void\r
7700 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7701               RECT *rect, char *color, char *flagFell)\r
7702 {\r
7703   char buf[100];\r
7704   char *str;\r
7705   COLORREF oldFg, oldBg;\r
7706   HFONT oldFont;\r
7707 \r
7708   if (twoBoards && partnerUp) return;\r
7709   if (appData.clockMode) {\r
7710     if (tinyLayout == 2)\r
7711       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7712     else\r
7713       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7714     str = buf;\r
7715   } else {\r
7716     str = color;\r
7717   }\r
7718 \r
7719   if (highlight) {\r
7720     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7721     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7722   } else {\r
7723     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7724     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7725   }\r
7726 \r
7727   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7728 \r
7729   JAWS_SILENCE\r
7730 \r
7731   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7732              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7733              rect, str, strlen(str), NULL);\r
7734   if(logoHeight > 0 && appData.clockMode) {\r
7735       RECT r;\r
7736       str += strlen(color)+2;\r
7737       r.top = rect->top + logoHeight/2;\r
7738       r.left = rect->left;\r
7739       r.right = rect->right;\r
7740       r.bottom = rect->bottom;\r
7741       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7742                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7743                  &r, str, strlen(str), NULL);\r
7744   }\r
7745   (void) SetTextColor(hdc, oldFg);\r
7746   (void) SetBkColor(hdc, oldBg);\r
7747   (void) SelectObject(hdc, oldFont);\r
7748 }\r
7749 \r
7750 \r
7751 int\r
7752 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7753            OVERLAPPED *ovl)\r
7754 {\r
7755   int ok, err;\r
7756 \r
7757   /* [AS]  */\r
7758   if( count <= 0 ) {\r
7759     if (appData.debugMode) {\r
7760       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7761     }\r
7762 \r
7763     return ERROR_INVALID_USER_BUFFER;\r
7764   }\r
7765 \r
7766   ResetEvent(ovl->hEvent);\r
7767   ovl->Offset = ovl->OffsetHigh = 0;\r
7768   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7769   if (ok) {\r
7770     err = NO_ERROR;\r
7771   } else {\r
7772     err = GetLastError();\r
7773     if (err == ERROR_IO_PENDING) {\r
7774       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7775       if (ok)\r
7776         err = NO_ERROR;\r
7777       else\r
7778         err = GetLastError();\r
7779     }\r
7780   }\r
7781   return err;\r
7782 }\r
7783 \r
7784 int\r
7785 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7786             OVERLAPPED *ovl)\r
7787 {\r
7788   int ok, err;\r
7789 \r
7790   ResetEvent(ovl->hEvent);\r
7791   ovl->Offset = ovl->OffsetHigh = 0;\r
7792   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7793   if (ok) {\r
7794     err = NO_ERROR;\r
7795   } else {\r
7796     err = GetLastError();\r
7797     if (err == ERROR_IO_PENDING) {\r
7798       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7799       if (ok)\r
7800         err = NO_ERROR;\r
7801       else\r
7802         err = GetLastError();\r
7803     }\r
7804 \r
7805   }\r
7806   return err;\r
7807 }\r
7808 \r
7809 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7810 void CheckForInputBufferFull( InputSource * is )\r
7811 {\r
7812     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7813         /* Look for end of line */\r
7814         char * p = is->buf;\r
7815         \r
7816         while( p < is->next && *p != '\n' ) {\r
7817             p++;\r
7818         }\r
7819 \r
7820         if( p >= is->next ) {\r
7821             if (appData.debugMode) {\r
7822                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7823             }\r
7824 \r
7825             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7826             is->count = (DWORD) -1;\r
7827             is->next = is->buf;\r
7828         }\r
7829     }\r
7830 }\r
7831 \r
7832 DWORD\r
7833 InputThread(LPVOID arg)\r
7834 {\r
7835   InputSource *is;\r
7836   OVERLAPPED ovl;\r
7837 \r
7838   is = (InputSource *) arg;\r
7839   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7840   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7841   while (is->hThread != NULL) {\r
7842     is->error = DoReadFile(is->hFile, is->next,\r
7843                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7844                            &is->count, &ovl);\r
7845     if (is->error == NO_ERROR) {\r
7846       is->next += is->count;\r
7847     } else {\r
7848       if (is->error == ERROR_BROKEN_PIPE) {\r
7849         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7850         is->count = 0;\r
7851       } else {\r
7852         is->count = (DWORD) -1;\r
7853         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7854         break; \r
7855       }\r
7856     }\r
7857 \r
7858     CheckForInputBufferFull( is );\r
7859 \r
7860     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7861 \r
7862     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7863 \r
7864     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7865   }\r
7866 \r
7867   CloseHandle(ovl.hEvent);\r
7868   CloseHandle(is->hFile);\r
7869 \r
7870   if (appData.debugMode) {\r
7871     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7872   }\r
7873 \r
7874   return 0;\r
7875 }\r
7876 \r
7877 \r
7878 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7879 DWORD\r
7880 NonOvlInputThread(LPVOID arg)\r
7881 {\r
7882   InputSource *is;\r
7883   char *p, *q;\r
7884   int i;\r
7885   char prev;\r
7886 \r
7887   is = (InputSource *) arg;\r
7888   while (is->hThread != NULL) {\r
7889     is->error = ReadFile(is->hFile, is->next,\r
7890                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7891                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7892     if (is->error == NO_ERROR) {\r
7893       /* Change CRLF to LF */\r
7894       if (is->next > is->buf) {\r
7895         p = is->next - 1;\r
7896         i = is->count + 1;\r
7897       } else {\r
7898         p = is->next;\r
7899         i = is->count;\r
7900       }\r
7901       q = p;\r
7902       prev = NULLCHAR;\r
7903       while (i > 0) {\r
7904         if (prev == '\r' && *p == '\n') {\r
7905           *(q-1) = '\n';\r
7906           is->count--;\r
7907         } else { \r
7908           *q++ = *p;\r
7909         }\r
7910         prev = *p++;\r
7911         i--;\r
7912       }\r
7913       *q = NULLCHAR;\r
7914       is->next = q;\r
7915     } else {\r
7916       if (is->error == ERROR_BROKEN_PIPE) {\r
7917         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7918         is->count = 0; \r
7919       } else {\r
7920         is->count = (DWORD) -1;\r
7921       }\r
7922     }\r
7923 \r
7924     CheckForInputBufferFull( is );\r
7925 \r
7926     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7927 \r
7928     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7929 \r
7930     if (is->count < 0) break;  /* Quit on error */\r
7931   }\r
7932   CloseHandle(is->hFile);\r
7933   return 0;\r
7934 }\r
7935 \r
7936 DWORD\r
7937 SocketInputThread(LPVOID arg)\r
7938 {\r
7939   InputSource *is;\r
7940 \r
7941   is = (InputSource *) arg;\r
7942   while (is->hThread != NULL) {\r
7943     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7944     if ((int)is->count == SOCKET_ERROR) {\r
7945       is->count = (DWORD) -1;\r
7946       is->error = WSAGetLastError();\r
7947     } else {\r
7948       is->error = NO_ERROR;\r
7949       is->next += is->count;\r
7950       if (is->count == 0 && is->second == is) {\r
7951         /* End of file on stderr; quit with no message */\r
7952         break;\r
7953       }\r
7954     }\r
7955     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7956 \r
7957     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7958 \r
7959     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7960   }\r
7961   return 0;\r
7962 }\r
7963 \r
7964 VOID\r
7965 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7966 {\r
7967   InputSource *is;\r
7968 \r
7969   is = (InputSource *) lParam;\r
7970   if (is->lineByLine) {\r
7971     /* Feed in lines one by one */\r
7972     char *p = is->buf;\r
7973     char *q = p;\r
7974     while (q < is->next) {\r
7975       if (*q++ == '\n') {\r
7976         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7977         p = q;\r
7978       }\r
7979     }\r
7980     \r
7981     /* Move any partial line to the start of the buffer */\r
7982     q = is->buf;\r
7983     while (p < is->next) {\r
7984       *q++ = *p++;\r
7985     }\r
7986     is->next = q;\r
7987 \r
7988     if (is->error != NO_ERROR || is->count == 0) {\r
7989       /* Notify backend of the error.  Note: If there was a partial\r
7990          line at the end, it is not flushed through. */\r
7991       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7992     }\r
7993   } else {\r
7994     /* Feed in the whole chunk of input at once */\r
7995     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7996     is->next = is->buf;\r
7997   }\r
7998 }\r
7999 \r
8000 /*---------------------------------------------------------------------------*\\r
8001  *\r
8002  *  Menu enables. Used when setting various modes.\r
8003  *\r
8004 \*---------------------------------------------------------------------------*/\r
8005 \r
8006 typedef struct {\r
8007   int item;\r
8008   int flags;\r
8009 } Enables;\r
8010 \r
8011 VOID\r
8012 GreyRevert(Boolean grey)\r
8013 { // [HGM] vari: for retracting variations in local mode\r
8014   HMENU hmenu = GetMenu(hwndMain);\r
8015   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8016   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8017 }\r
8018 \r
8019 VOID\r
8020 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8021 {\r
8022   while (enab->item > 0) {\r
8023     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8024     enab++;\r
8025   }\r
8026 }\r
8027 \r
8028 Enables gnuEnables[] = {\r
8029   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8042 \r
8043 \r
8044   // Needed to switch from ncp to GNU mode on Engine Load\r
8045   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8046   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8047   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8048   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8049   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8051   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8052   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8053   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8054   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8055   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8056   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8057   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8058   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8059   { -1, -1 }\r
8060 };\r
8061 \r
8062 Enables icsEnables[] = {\r
8063   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8065   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8066   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8068   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8069   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8071   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8072   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8073   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8074   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8075   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8076   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8077   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8078   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8079   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8080   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8081   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8082   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8083   { -1, -1 }\r
8084 };\r
8085 \r
8086 #if ZIPPY\r
8087 Enables zippyEnables[] = {\r
8088   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8089   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8090   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8091   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8092   { -1, -1 }\r
8093 };\r
8094 #endif\r
8095 \r
8096 Enables ncpEnables[] = {\r
8097   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8104   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8105   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8106   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8107   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8108   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8109   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8110   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8111   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8112   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8113   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8114   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8115   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8116   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8117   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8118   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8119   { -1, -1 }\r
8120 };\r
8121 \r
8122 Enables trainingOnEnables[] = {\r
8123   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8124   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8125   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8126   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8127   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8128   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8129   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8130   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8131   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8132   { -1, -1 }\r
8133 };\r
8134 \r
8135 Enables trainingOffEnables[] = {\r
8136   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8137   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8138   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8139   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8140   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8141   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8142   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8143   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8144   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8145   { -1, -1 }\r
8146 };\r
8147 \r
8148 /* These modify either ncpEnables or gnuEnables */\r
8149 Enables cmailEnables[] = {\r
8150   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8151   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8152   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8153   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8154   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8155   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8156   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8157   { -1, -1 }\r
8158 };\r
8159 \r
8160 Enables machineThinkingEnables[] = {\r
8161   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8162   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8163   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8164   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8165   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8166   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8167   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8168   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8169   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8170   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8171   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8172   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8173   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8174 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8175   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8176   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8177   { -1, -1 }\r
8178 };\r
8179 \r
8180 Enables userThinkingEnables[] = {\r
8181   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8182   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8183   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8184   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8185   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8186   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8187   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8188   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8189   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8190   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8191   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8192   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8193   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8194 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8195   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8196   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8197   { -1, -1 }\r
8198 };\r
8199 \r
8200 /*---------------------------------------------------------------------------*\\r
8201  *\r
8202  *  Front-end interface functions exported by XBoard.\r
8203  *  Functions appear in same order as prototypes in frontend.h.\r
8204  * \r
8205 \*---------------------------------------------------------------------------*/\r
8206 VOID\r
8207 CheckMark(UINT item, int state)\r
8208 {\r
8209     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8210 }\r
8211 \r
8212 VOID\r
8213 ModeHighlight()\r
8214 {\r
8215   static UINT prevChecked = 0;\r
8216   static int prevPausing = 0;\r
8217   UINT nowChecked;\r
8218 \r
8219   if (pausing != prevPausing) {\r
8220     prevPausing = pausing;\r
8221     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8222                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8223     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8224   }\r
8225 \r
8226   switch (gameMode) {\r
8227   case BeginningOfGame:\r
8228     if (appData.icsActive)\r
8229       nowChecked = IDM_IcsClient;\r
8230     else if (appData.noChessProgram)\r
8231       nowChecked = IDM_EditGame;\r
8232     else\r
8233       nowChecked = IDM_MachineBlack;\r
8234     break;\r
8235   case MachinePlaysBlack:\r
8236     nowChecked = IDM_MachineBlack;\r
8237     break;\r
8238   case MachinePlaysWhite:\r
8239     nowChecked = IDM_MachineWhite;\r
8240     break;\r
8241   case TwoMachinesPlay:\r
8242     nowChecked = IDM_TwoMachines;\r
8243     break;\r
8244   case AnalyzeMode:\r
8245     nowChecked = IDM_AnalysisMode;\r
8246     break;\r
8247   case AnalyzeFile:\r
8248     nowChecked = IDM_AnalyzeFile;\r
8249     break;\r
8250   case EditGame:\r
8251     nowChecked = IDM_EditGame;\r
8252     break;\r
8253   case PlayFromGameFile:\r
8254     nowChecked = IDM_LoadGame;\r
8255     break;\r
8256   case EditPosition:\r
8257     nowChecked = IDM_EditPosition;\r
8258     break;\r
8259   case Training:\r
8260     nowChecked = IDM_Training;\r
8261     break;\r
8262   case IcsPlayingWhite:\r
8263   case IcsPlayingBlack:\r
8264   case IcsObserving:\r
8265   case IcsIdle:\r
8266     nowChecked = IDM_IcsClient;\r
8267     break;\r
8268   default:\r
8269   case EndOfGame:\r
8270     nowChecked = 0;\r
8271     break;\r
8272   }\r
8273   if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8274     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
8275   CheckMark(prevChecked, MF_UNCHECKED);\r
8276   CheckMark(nowChecked, MF_CHECKED);\r
8277   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8278 \r
8279   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8280     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8281                           MF_BYCOMMAND|MF_ENABLED);\r
8282   } else {\r
8283     (void) EnableMenuItem(GetMenu(hwndMain), \r
8284                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8285   }\r
8286 \r
8287   prevChecked = nowChecked;\r
8288 \r
8289   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8290   if (appData.icsActive) {\r
8291        if (appData.icsEngineAnalyze) {\r
8292                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8293        } else {\r
8294                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8295        }\r
8296   }\r
8297   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8298 }\r
8299 \r
8300 VOID\r
8301 SetICSMode()\r
8302 {\r
8303   HMENU hmenu = GetMenu(hwndMain);\r
8304   SetMenuEnables(hmenu, icsEnables);\r
8305   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8306     MF_BYCOMMAND|MF_ENABLED);\r
8307 #if ZIPPY\r
8308   if (appData.zippyPlay) {\r
8309     SetMenuEnables(hmenu, zippyEnables);\r
8310     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8311          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8312           MF_BYCOMMAND|MF_ENABLED);\r
8313   }\r
8314 #endif\r
8315 }\r
8316 \r
8317 VOID\r
8318 SetGNUMode()\r
8319 {\r
8320   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8321 }\r
8322 \r
8323 VOID\r
8324 SetNCPMode()\r
8325 {\r
8326   HMENU hmenu = GetMenu(hwndMain);\r
8327   SetMenuEnables(hmenu, ncpEnables);\r
8328     DrawMenuBar(hwndMain);\r
8329 }\r
8330 \r
8331 VOID\r
8332 SetCmailMode()\r
8333 {\r
8334   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8335 }\r
8336 \r
8337 VOID \r
8338 SetTrainingModeOn()\r
8339 {\r
8340   int i;\r
8341   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8342   for (i = 0; i < N_BUTTONS; i++) {\r
8343     if (buttonDesc[i].hwnd != NULL)\r
8344       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8345   }\r
8346   CommentPopDown();\r
8347 }\r
8348 \r
8349 VOID SetTrainingModeOff()\r
8350 {\r
8351   int i;\r
8352   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8353   for (i = 0; i < N_BUTTONS; i++) {\r
8354     if (buttonDesc[i].hwnd != NULL)\r
8355       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8356   }\r
8357 }\r
8358 \r
8359 \r
8360 VOID\r
8361 SetUserThinkingEnables()\r
8362 {\r
8363   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8364 }\r
8365 \r
8366 VOID\r
8367 SetMachineThinkingEnables()\r
8368 {\r
8369   HMENU hMenu = GetMenu(hwndMain);\r
8370   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8371 \r
8372   SetMenuEnables(hMenu, machineThinkingEnables);\r
8373 \r
8374   if (gameMode == MachinePlaysBlack) {\r
8375     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8376   } else if (gameMode == MachinePlaysWhite) {\r
8377     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8378   } else if (gameMode == TwoMachinesPlay) {\r
8379     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8380   }\r
8381 }\r
8382 \r
8383 \r
8384 VOID\r
8385 DisplayTitle(char *str)\r
8386 {\r
8387   char title[MSG_SIZ], *host;\r
8388   if (str[0] != NULLCHAR) {\r
8389     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8390   } else if (appData.icsActive) {\r
8391     if (appData.icsCommPort[0] != NULLCHAR)\r
8392       host = "ICS";\r
8393     else \r
8394       host = appData.icsHost;\r
8395       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8396   } else if (appData.noChessProgram) {\r
8397     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8398   } else {\r
8399     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8400     strcat(title, ": ");\r
8401     strcat(title, first.tidy);\r
8402   }\r
8403   SetWindowText(hwndMain, title);\r
8404 }\r
8405 \r
8406 \r
8407 VOID\r
8408 DisplayMessage(char *str1, char *str2)\r
8409 {\r
8410   HDC hdc;\r
8411   HFONT oldFont;\r
8412   int remain = MESSAGE_TEXT_MAX - 1;\r
8413   int len;\r
8414 \r
8415   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8416   messageText[0] = NULLCHAR;\r
8417   if (*str1) {\r
8418     len = strlen(str1);\r
8419     if (len > remain) len = remain;\r
8420     strncpy(messageText, str1, len);\r
8421     messageText[len] = NULLCHAR;\r
8422     remain -= len;\r
8423   }\r
8424   if (*str2 && remain >= 2) {\r
8425     if (*str1) {\r
8426       strcat(messageText, "  ");\r
8427       remain -= 2;\r
8428     }\r
8429     len = strlen(str2);\r
8430     if (len > remain) len = remain;\r
8431     strncat(messageText, str2, len);\r
8432   }\r
8433   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8434   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8435 \r
8436   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8437 \r
8438   SAYMACHINEMOVE();\r
8439 \r
8440   hdc = GetDC(hwndMain);\r
8441   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8442   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8443              &messageRect, messageText, strlen(messageText), NULL);\r
8444   (void) SelectObject(hdc, oldFont);\r
8445   (void) ReleaseDC(hwndMain, hdc);\r
8446 }\r
8447 \r
8448 VOID\r
8449 DisplayError(char *str, int error)\r
8450 {\r
8451   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8452   int len;\r
8453 \r
8454   if (error == 0) {\r
8455     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8456   } else {\r
8457     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8458                         NULL, error, LANG_NEUTRAL,\r
8459                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8460     if (len > 0) {\r
8461       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8462     } else {\r
8463       ErrorMap *em = errmap;\r
8464       while (em->err != 0 && em->err != error) em++;\r
8465       if (em->err != 0) {\r
8466         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8467       } else {\r
8468         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8469       }\r
8470     }\r
8471   }\r
8472   \r
8473   ErrorPopUp(_("Error"), buf);\r
8474 }\r
8475 \r
8476 \r
8477 VOID\r
8478 DisplayMoveError(char *str)\r
8479 {\r
8480   fromX = fromY = -1;\r
8481   ClearHighlights();\r
8482   DrawPosition(FALSE, NULL);\r
8483   if (appData.popupMoveErrors) {\r
8484     ErrorPopUp(_("Error"), str);\r
8485   } else {\r
8486     DisplayMessage(str, "");\r
8487     moveErrorMessageUp = TRUE;\r
8488   }\r
8489 }\r
8490 \r
8491 VOID\r
8492 DisplayFatalError(char *str, int error, int exitStatus)\r
8493 {\r
8494   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8495   int len;\r
8496   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8497 \r
8498   if (error != 0) {\r
8499     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8500                         NULL, error, LANG_NEUTRAL,\r
8501                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8502     if (len > 0) {\r
8503       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8504     } else {\r
8505       ErrorMap *em = errmap;\r
8506       while (em->err != 0 && em->err != error) em++;\r
8507       if (em->err != 0) {\r
8508         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8509       } else {\r
8510         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8511       }\r
8512     }\r
8513     str = buf;\r
8514   }\r
8515   if (appData.debugMode) {\r
8516     fprintf(debugFP, "%s: %s\n", label, str);\r
8517   }\r
8518   if (appData.popupExitMessage) {\r
8519     if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!\r
8520     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8521                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8522   }\r
8523   ExitEvent(exitStatus);\r
8524 }\r
8525 \r
8526 \r
8527 VOID\r
8528 DisplayInformation(char *str)\r
8529 {\r
8530   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8531 }\r
8532 \r
8533 \r
8534 VOID\r
8535 DisplayNote(char *str)\r
8536 {\r
8537   ErrorPopUp(_("Note"), str);\r
8538 }\r
8539 \r
8540 \r
8541 typedef struct {\r
8542   char *title, *question, *replyPrefix;\r
8543   ProcRef pr;\r
8544 } QuestionParams;\r
8545 \r
8546 LRESULT CALLBACK\r
8547 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8548 {\r
8549   static QuestionParams *qp;\r
8550   char reply[MSG_SIZ];\r
8551   int len, err;\r
8552 \r
8553   switch (message) {\r
8554   case WM_INITDIALOG:\r
8555     qp = (QuestionParams *) lParam;\r
8556     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8557     Translate(hDlg, DLG_Question);\r
8558     SetWindowText(hDlg, qp->title);\r
8559     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8560     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8561     return FALSE;\r
8562 \r
8563   case WM_COMMAND:\r
8564     switch (LOWORD(wParam)) {\r
8565     case IDOK:\r
8566       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8567       if (*reply) strcat(reply, " ");\r
8568       len = strlen(reply);\r
8569       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8570       strcat(reply, "\n");\r
8571       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8572       EndDialog(hDlg, TRUE);\r
8573       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8574       return TRUE;\r
8575     case IDCANCEL:\r
8576       EndDialog(hDlg, FALSE);\r
8577       return TRUE;\r
8578     default:\r
8579       break;\r
8580     }\r
8581     break;\r
8582   }\r
8583   return FALSE;\r
8584 }\r
8585 \r
8586 VOID\r
8587 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8588 {\r
8589     QuestionParams qp;\r
8590     FARPROC lpProc;\r
8591     \r
8592     qp.title = title;\r
8593     qp.question = question;\r
8594     qp.replyPrefix = replyPrefix;\r
8595     qp.pr = pr;\r
8596     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8597     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8598       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8599     FreeProcInstance(lpProc);\r
8600 }\r
8601 \r
8602 /* [AS] Pick FRC position */\r
8603 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8604 {\r
8605     static int * lpIndexFRC;\r
8606     BOOL index_is_ok;\r
8607     char buf[16];\r
8608 \r
8609     switch( message )\r
8610     {\r
8611     case WM_INITDIALOG:\r
8612         lpIndexFRC = (int *) lParam;\r
8613 \r
8614         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8615         Translate(hDlg, DLG_NewGameFRC);\r
8616 \r
8617         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8618         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8619         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8620         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8621 \r
8622         break;\r
8623 \r
8624     case WM_COMMAND:\r
8625         switch( LOWORD(wParam) ) {\r
8626         case IDOK:\r
8627             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8628             EndDialog( hDlg, 0 );\r
8629             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8630             return TRUE;\r
8631         case IDCANCEL:\r
8632             EndDialog( hDlg, 1 );   \r
8633             return TRUE;\r
8634         case IDC_NFG_Edit:\r
8635             if( HIWORD(wParam) == EN_CHANGE ) {\r
8636                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8637 \r
8638                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8639             }\r
8640             return TRUE;\r
8641         case IDC_NFG_Random:\r
8642           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8643             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8644             return TRUE;\r
8645         }\r
8646 \r
8647         break;\r
8648     }\r
8649 \r
8650     return FALSE;\r
8651 }\r
8652 \r
8653 int NewGameFRC()\r
8654 {\r
8655     int result;\r
8656     int index = appData.defaultFrcPosition;\r
8657     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8658 \r
8659     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8660 \r
8661     if( result == 0 ) {\r
8662         appData.defaultFrcPosition = index;\r
8663     }\r
8664 \r
8665     return result;\r
8666 }\r
8667 \r
8668 /* [AS] Game list options. Refactored by HGM */\r
8669 \r
8670 HWND gameListOptionsDialog;\r
8671 \r
8672 // low-level front-end: clear text edit / list widget\r
8673 void\r
8674 \r
8675 GLT_ClearList()\r
8676 {\r
8677     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8678 }\r
8679 \r
8680 // low-level front-end: clear text edit / list widget\r
8681 void\r
8682 GLT_DeSelectList()\r
8683 {\r
8684     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8685 }\r
8686 \r
8687 // low-level front-end: append line to text edit / list widget\r
8688 void\r
8689 GLT_AddToList( char *name )\r
8690 {\r
8691     if( name != 0 ) {\r
8692             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8693     }\r
8694 }\r
8695 \r
8696 // low-level front-end: get line from text edit / list widget\r
8697 Boolean\r
8698 GLT_GetFromList( int index, char *name )\r
8699 {\r
8700     if( name != 0 ) {\r
8701             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8702                 return TRUE;\r
8703     }\r
8704     return FALSE;\r
8705 }\r
8706 \r
8707 void GLT_MoveSelection( HWND hDlg, int delta )\r
8708 {\r
8709     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8710     int idx2 = idx1 + delta;\r
8711     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8712 \r
8713     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8714         char buf[128];\r
8715 \r
8716         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8717         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8718         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8719         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8720     }\r
8721 }\r
8722 \r
8723 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8724 {\r
8725     switch( message )\r
8726     {\r
8727     case WM_INITDIALOG:\r
8728         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8729         \r
8730         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8731         Translate(hDlg, DLG_GameListOptions);\r
8732 \r
8733         /* Initialize list */\r
8734         GLT_TagsToList( lpUserGLT );\r
8735 \r
8736         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8737 \r
8738         break;\r
8739 \r
8740     case WM_COMMAND:\r
8741         switch( LOWORD(wParam) ) {\r
8742         case IDOK:\r
8743             GLT_ParseList();\r
8744             EndDialog( hDlg, 0 );\r
8745             return TRUE;\r
8746         case IDCANCEL:\r
8747             EndDialog( hDlg, 1 );\r
8748             return TRUE;\r
8749 \r
8750         case IDC_GLT_Default:\r
8751             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8752             return TRUE;\r
8753 \r
8754         case IDC_GLT_Restore:\r
8755             GLT_TagsToList( appData.gameListTags );\r
8756             return TRUE;\r
8757 \r
8758         case IDC_GLT_Up:\r
8759             GLT_MoveSelection( hDlg, -1 );\r
8760             return TRUE;\r
8761 \r
8762         case IDC_GLT_Down:\r
8763             GLT_MoveSelection( hDlg, +1 );\r
8764             return TRUE;\r
8765         }\r
8766 \r
8767         break;\r
8768     }\r
8769 \r
8770     return FALSE;\r
8771 }\r
8772 \r
8773 int GameListOptions()\r
8774 {\r
8775     int result;\r
8776     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8777 \r
8778       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8779 \r
8780     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8781 \r
8782     if( result == 0 ) {\r
8783         char *oldTags = appData.gameListTags;\r
8784         /* [AS] Memory leak here! */\r
8785         appData.gameListTags = strdup( lpUserGLT ); \r
8786         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8787             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8788     }\r
8789 \r
8790     return result;\r
8791 }\r
8792 \r
8793 VOID\r
8794 DisplayIcsInteractionTitle(char *str)\r
8795 {\r
8796   char consoleTitle[MSG_SIZ];\r
8797 \r
8798     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8799     SetWindowText(hwndConsole, consoleTitle);\r
8800 \r
8801     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8802       char buf[MSG_SIZ], *p = buf, *q;\r
8803         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8804       do {\r
8805         q = strchr(p, ';');\r
8806         if(q) *q++ = 0;\r
8807         if(*p) ChatPopUp(p);\r
8808       } while(p=q);\r
8809     }\r
8810 \r
8811     SetActiveWindow(hwndMain);\r
8812 }\r
8813 \r
8814 void\r
8815 DrawPosition(int fullRedraw, Board board)\r
8816 {\r
8817   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8818 }\r
8819 \r
8820 void NotifyFrontendLogin()\r
8821 {\r
8822         if (hwndConsole)\r
8823                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8824 }\r
8825 \r
8826 VOID\r
8827 ResetFrontEnd()\r
8828 {\r
8829   fromX = fromY = -1;\r
8830   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8831     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8832     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8833     dragInfo.lastpos = dragInfo.pos;\r
8834     dragInfo.start.x = dragInfo.start.y = -1;\r
8835     dragInfo.from = dragInfo.start;\r
8836     ReleaseCapture();\r
8837     DrawPosition(TRUE, NULL);\r
8838   }\r
8839   TagsPopDown();\r
8840 }\r
8841 \r
8842 \r
8843 VOID\r
8844 CommentPopUp(char *title, char *str)\r
8845 {\r
8846   HWND hwnd = GetActiveWindow();\r
8847   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8848   SAY(str);\r
8849   SetActiveWindow(hwnd);\r
8850 }\r
8851 \r
8852 VOID\r
8853 CommentPopDown(void)\r
8854 {\r
8855   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8856   if (commentDialog) {\r
8857     ShowWindow(commentDialog, SW_HIDE);\r
8858   }\r
8859   commentUp = FALSE;\r
8860 }\r
8861 \r
8862 VOID\r
8863 EditCommentPopUp(int index, char *title, char *str)\r
8864 {\r
8865   EitherCommentPopUp(index, title, str, TRUE);\r
8866 }\r
8867 \r
8868 \r
8869 int\r
8870 Roar()\r
8871 {\r
8872   MyPlaySound(&sounds[(int)SoundRoar]);\r
8873   return 1;\r
8874 }\r
8875 \r
8876 VOID\r
8877 RingBell()\r
8878 {\r
8879   MyPlaySound(&sounds[(int)SoundMove]);\r
8880 }\r
8881 \r
8882 VOID PlayIcsWinSound()\r
8883 {\r
8884   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8885 }\r
8886 \r
8887 VOID PlayIcsLossSound()\r
8888 {\r
8889   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8890 }\r
8891 \r
8892 VOID PlayIcsDrawSound()\r
8893 {\r
8894   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8895 }\r
8896 \r
8897 VOID PlayIcsUnfinishedSound()\r
8898 {\r
8899   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8900 }\r
8901 \r
8902 VOID\r
8903 PlayAlarmSound()\r
8904 {\r
8905   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8906 }\r
8907 \r
8908 VOID\r
8909 PlayTellSound()\r
8910 {\r
8911   MyPlaySound(&textAttribs[ColorTell].sound);\r
8912 }\r
8913 \r
8914 \r
8915 VOID\r
8916 EchoOn()\r
8917 {\r
8918   HWND hInput;\r
8919   consoleEcho = TRUE;\r
8920   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8921   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8922   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8923 }\r
8924 \r
8925 \r
8926 VOID\r
8927 EchoOff()\r
8928 {\r
8929   CHARFORMAT cf;\r
8930   HWND hInput;\r
8931   consoleEcho = FALSE;\r
8932   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8933   /* This works OK: set text and background both to the same color */\r
8934   cf = consoleCF;\r
8935   cf.crTextColor = COLOR_ECHOOFF;\r
8936   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8937   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8938 }\r
8939 \r
8940 /* No Raw()...? */\r
8941 \r
8942 void Colorize(ColorClass cc, int continuation)\r
8943 {\r
8944   currentColorClass = cc;\r
8945   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8946   consoleCF.crTextColor = textAttribs[cc].color;\r
8947   consoleCF.dwEffects = textAttribs[cc].effects;\r
8948   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8949 }\r
8950 \r
8951 char *\r
8952 UserName()\r
8953 {\r
8954   static char buf[MSG_SIZ];\r
8955   DWORD bufsiz = MSG_SIZ;\r
8956 \r
8957   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8958         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8959   }\r
8960   if (!GetUserName(buf, &bufsiz)) {\r
8961     /*DisplayError("Error getting user name", GetLastError());*/\r
8962     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8963   }\r
8964   return buf;\r
8965 }\r
8966 \r
8967 char *\r
8968 HostName()\r
8969 {\r
8970   static char buf[MSG_SIZ];\r
8971   DWORD bufsiz = MSG_SIZ;\r
8972 \r
8973   if (!GetComputerName(buf, &bufsiz)) {\r
8974     /*DisplayError("Error getting host name", GetLastError());*/\r
8975     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8976   }\r
8977   return buf;\r
8978 }\r
8979 \r
8980 \r
8981 int\r
8982 ClockTimerRunning()\r
8983 {\r
8984   return clockTimerEvent != 0;\r
8985 }\r
8986 \r
8987 int\r
8988 StopClockTimer()\r
8989 {\r
8990   if (clockTimerEvent == 0) return FALSE;\r
8991   KillTimer(hwndMain, clockTimerEvent);\r
8992   clockTimerEvent = 0;\r
8993   return TRUE;\r
8994 }\r
8995 \r
8996 void\r
8997 StartClockTimer(long millisec)\r
8998 {\r
8999   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9000                              (UINT) millisec, NULL);\r
9001 }\r
9002 \r
9003 void\r
9004 DisplayWhiteClock(long timeRemaining, int highlight)\r
9005 {\r
9006   HDC hdc;\r
9007   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9008 \r
9009   if(appData.noGUI) return;\r
9010   hdc = GetDC(hwndMain);\r
9011   if (!IsIconic(hwndMain)) {\r
9012     DisplayAClock(hdc, timeRemaining, highlight, \r
9013                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
9014   }\r
9015   if (highlight && iconCurrent == iconBlack) {\r
9016     iconCurrent = iconWhite;\r
9017     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9018     if (IsIconic(hwndMain)) {\r
9019       DrawIcon(hdc, 2, 2, iconCurrent);\r
9020     }\r
9021   }\r
9022   (void) ReleaseDC(hwndMain, hdc);\r
9023   if (hwndConsole)\r
9024     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9025 }\r
9026 \r
9027 void\r
9028 DisplayBlackClock(long timeRemaining, int highlight)\r
9029 {\r
9030   HDC hdc;\r
9031   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9032 \r
9033 \r
9034   if(appData.noGUI) return;\r
9035   hdc = GetDC(hwndMain);\r
9036   if (!IsIconic(hwndMain)) {\r
9037     DisplayAClock(hdc, timeRemaining, highlight, \r
9038                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
9039   }\r
9040   if (highlight && iconCurrent == iconWhite) {\r
9041     iconCurrent = iconBlack;\r
9042     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9043     if (IsIconic(hwndMain)) {\r
9044       DrawIcon(hdc, 2, 2, iconCurrent);\r
9045     }\r
9046   }\r
9047   (void) ReleaseDC(hwndMain, hdc);\r
9048   if (hwndConsole)\r
9049     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9050 }\r
9051 \r
9052 \r
9053 int\r
9054 LoadGameTimerRunning()\r
9055 {\r
9056   return loadGameTimerEvent != 0;\r
9057 }\r
9058 \r
9059 int\r
9060 StopLoadGameTimer()\r
9061 {\r
9062   if (loadGameTimerEvent == 0) return FALSE;\r
9063   KillTimer(hwndMain, loadGameTimerEvent);\r
9064   loadGameTimerEvent = 0;\r
9065   return TRUE;\r
9066 }\r
9067 \r
9068 void\r
9069 StartLoadGameTimer(long millisec)\r
9070 {\r
9071   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9072                                 (UINT) millisec, NULL);\r
9073 }\r
9074 \r
9075 void\r
9076 AutoSaveGame()\r
9077 {\r
9078   char *defName;\r
9079   FILE *f;\r
9080   char fileTitle[MSG_SIZ];\r
9081 \r
9082   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9083   f = OpenFileDialog(hwndMain, "a", defName,\r
9084                      appData.oldSaveStyle ? "gam" : "pgn",\r
9085                      GAME_FILT, \r
9086                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9087   if (f != NULL) {\r
9088     SaveGame(f, 0, "");\r
9089     fclose(f);\r
9090   }\r
9091 }\r
9092 \r
9093 \r
9094 void\r
9095 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9096 {\r
9097   if (delayedTimerEvent != 0) {\r
9098     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9099       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9100     }\r
9101     KillTimer(hwndMain, delayedTimerEvent);\r
9102     delayedTimerEvent = 0;\r
9103     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9104     delayedTimerCallback();\r
9105   }\r
9106   delayedTimerCallback = cb;\r
9107   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9108                                 (UINT) millisec, NULL);\r
9109 }\r
9110 \r
9111 DelayedEventCallback\r
9112 GetDelayedEvent()\r
9113 {\r
9114   if (delayedTimerEvent) {\r
9115     return delayedTimerCallback;\r
9116   } else {\r
9117     return NULL;\r
9118   }\r
9119 }\r
9120 \r
9121 void\r
9122 CancelDelayedEvent()\r
9123 {\r
9124   if (delayedTimerEvent) {\r
9125     KillTimer(hwndMain, delayedTimerEvent);\r
9126     delayedTimerEvent = 0;\r
9127   }\r
9128 }\r
9129 \r
9130 DWORD GetWin32Priority(int nice)\r
9131 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9132 /*\r
9133 REALTIME_PRIORITY_CLASS     0x00000100\r
9134 HIGH_PRIORITY_CLASS         0x00000080\r
9135 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9136 NORMAL_PRIORITY_CLASS       0x00000020\r
9137 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9138 IDLE_PRIORITY_CLASS         0x00000040\r
9139 */\r
9140         if (nice < -15) return 0x00000080;\r
9141         if (nice < 0)   return 0x00008000;\r
9142 \r
9143         if (nice == 0)  return 0x00000020;\r
9144         if (nice < 15)  return 0x00004000;\r
9145         return 0x00000040;\r
9146 }\r
9147 \r
9148 void RunCommand(char *cmdLine)\r
9149 {\r
9150   /* Now create the child process. */\r
9151   STARTUPINFO siStartInfo;\r
9152   PROCESS_INFORMATION piProcInfo;\r
9153 \r
9154   siStartInfo.cb = sizeof(STARTUPINFO);\r
9155   siStartInfo.lpReserved = NULL;\r
9156   siStartInfo.lpDesktop = NULL;\r
9157   siStartInfo.lpTitle = NULL;\r
9158   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9159   siStartInfo.cbReserved2 = 0;\r
9160   siStartInfo.lpReserved2 = NULL;\r
9161   siStartInfo.hStdInput = NULL;\r
9162   siStartInfo.hStdOutput = NULL;\r
9163   siStartInfo.hStdError = NULL;\r
9164 \r
9165   CreateProcess(NULL,\r
9166                 cmdLine,           /* command line */\r
9167                 NULL,      /* process security attributes */\r
9168                 NULL,      /* primary thread security attrs */\r
9169                 TRUE,      /* handles are inherited */\r
9170                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9171                 NULL,      /* use parent's environment */\r
9172                 NULL,\r
9173                 &siStartInfo, /* STARTUPINFO pointer */\r
9174                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9175 \r
9176   CloseHandle(piProcInfo.hThread);\r
9177 }\r
9178 \r
9179 /* Start a child process running the given program.\r
9180    The process's standard output can be read from "from", and its\r
9181    standard input can be written to "to".\r
9182    Exit with fatal error if anything goes wrong.\r
9183    Returns an opaque pointer that can be used to destroy the process\r
9184    later.\r
9185 */\r
9186 int\r
9187 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9188 {\r
9189 #define BUFSIZE 4096\r
9190 \r
9191   HANDLE hChildStdinRd, hChildStdinWr,\r
9192     hChildStdoutRd, hChildStdoutWr;\r
9193   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9194   SECURITY_ATTRIBUTES saAttr;\r
9195   BOOL fSuccess;\r
9196   PROCESS_INFORMATION piProcInfo;\r
9197   STARTUPINFO siStartInfo;\r
9198   ChildProc *cp;\r
9199   char buf[MSG_SIZ];\r
9200   DWORD err;\r
9201 \r
9202   if (appData.debugMode) {\r
9203     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9204   }\r
9205 \r
9206   *pr = NoProc;\r
9207 \r
9208   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9209   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9210   saAttr.bInheritHandle = TRUE;\r
9211   saAttr.lpSecurityDescriptor = NULL;\r
9212 \r
9213   /*\r
9214    * The steps for redirecting child's STDOUT:\r
9215    *     1. Create anonymous pipe to be STDOUT for child.\r
9216    *     2. Create a noninheritable duplicate of read handle,\r
9217    *         and close the inheritable read handle.\r
9218    */\r
9219 \r
9220   /* Create a pipe for the child's STDOUT. */\r
9221   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9222     return GetLastError();\r
9223   }\r
9224 \r
9225   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9226   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9227                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9228                              FALSE,     /* not inherited */\r
9229                              DUPLICATE_SAME_ACCESS);\r
9230   if (! fSuccess) {\r
9231     return GetLastError();\r
9232   }\r
9233   CloseHandle(hChildStdoutRd);\r
9234 \r
9235   /*\r
9236    * The steps for redirecting child's STDIN:\r
9237    *     1. Create anonymous pipe to be STDIN for child.\r
9238    *     2. Create a noninheritable duplicate of write handle,\r
9239    *         and close the inheritable write handle.\r
9240    */\r
9241 \r
9242   /* Create a pipe for the child's STDIN. */\r
9243   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9244     return GetLastError();\r
9245   }\r
9246 \r
9247   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9248   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9249                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9250                              FALSE,     /* not inherited */\r
9251                              DUPLICATE_SAME_ACCESS);\r
9252   if (! fSuccess) {\r
9253     return GetLastError();\r
9254   }\r
9255   CloseHandle(hChildStdinWr);\r
9256 \r
9257   /* Arrange to (1) look in dir for the child .exe file, and\r
9258    * (2) have dir be the child's working directory.  Interpret\r
9259    * dir relative to the directory WinBoard loaded from. */\r
9260   GetCurrentDirectory(MSG_SIZ, buf);\r
9261   SetCurrentDirectory(installDir);\r
9262   SetCurrentDirectory(dir);\r
9263 \r
9264   /* Now create the child process. */\r
9265 \r
9266   siStartInfo.cb = sizeof(STARTUPINFO);\r
9267   siStartInfo.lpReserved = NULL;\r
9268   siStartInfo.lpDesktop = NULL;\r
9269   siStartInfo.lpTitle = NULL;\r
9270   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9271   siStartInfo.cbReserved2 = 0;\r
9272   siStartInfo.lpReserved2 = NULL;\r
9273   siStartInfo.hStdInput = hChildStdinRd;\r
9274   siStartInfo.hStdOutput = hChildStdoutWr;\r
9275   siStartInfo.hStdError = hChildStdoutWr;\r
9276 \r
9277   fSuccess = CreateProcess(NULL,\r
9278                            cmdLine,        /* command line */\r
9279                            NULL,           /* process security attributes */\r
9280                            NULL,           /* primary thread security attrs */\r
9281                            TRUE,           /* handles are inherited */\r
9282                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9283                            NULL,           /* use parent's environment */\r
9284                            NULL,\r
9285                            &siStartInfo, /* STARTUPINFO pointer */\r
9286                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9287 \r
9288   err = GetLastError();\r
9289   SetCurrentDirectory(buf); /* return to prev directory */\r
9290   if (! fSuccess) {\r
9291     return err;\r
9292   }\r
9293 \r
9294   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9295     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9296     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9297   }\r
9298 \r
9299   /* Close the handles we don't need in the parent */\r
9300   CloseHandle(piProcInfo.hThread);\r
9301   CloseHandle(hChildStdinRd);\r
9302   CloseHandle(hChildStdoutWr);\r
9303 \r
9304   /* Prepare return value */\r
9305   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9306   cp->kind = CPReal;\r
9307   cp->hProcess = piProcInfo.hProcess;\r
9308   cp->pid = piProcInfo.dwProcessId;\r
9309   cp->hFrom = hChildStdoutRdDup;\r
9310   cp->hTo = hChildStdinWrDup;\r
9311 \r
9312   *pr = (void *) cp;\r
9313 \r
9314   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9315      2000 where engines sometimes don't see the initial command(s)\r
9316      from WinBoard and hang.  I don't understand how that can happen,\r
9317      but the Sleep is harmless, so I've put it in.  Others have also\r
9318      reported what may be the same problem, so hopefully this will fix\r
9319      it for them too.  */\r
9320   Sleep(500);\r
9321 \r
9322   return NO_ERROR;\r
9323 }\r
9324 \r
9325 \r
9326 void\r
9327 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9328 {\r
9329   ChildProc *cp; int result;\r
9330 \r
9331   cp = (ChildProc *) pr;\r
9332   if (cp == NULL) return;\r
9333 \r
9334   switch (cp->kind) {\r
9335   case CPReal:\r
9336     /* TerminateProcess is considered harmful, so... */\r
9337     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9338     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9339     /* The following doesn't work because the chess program\r
9340        doesn't "have the same console" as WinBoard.  Maybe\r
9341        we could arrange for this even though neither WinBoard\r
9342        nor the chess program uses a console for stdio? */\r
9343     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9344 \r
9345     /* [AS] Special termination modes for misbehaving programs... */\r
9346     if( signal & 8 ) { \r
9347         result = TerminateProcess( cp->hProcess, 0 );\r
9348 \r
9349         if ( appData.debugMode) {\r
9350             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9351         }\r
9352     }\r
9353     else if( signal & 4 ) {\r
9354         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9355 \r
9356         if( dw != WAIT_OBJECT_0 ) {\r
9357             result = TerminateProcess( cp->hProcess, 0 );\r
9358 \r
9359             if ( appData.debugMode) {\r
9360                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9361             }\r
9362 \r
9363         }\r
9364     }\r
9365 \r
9366     CloseHandle(cp->hProcess);\r
9367     break;\r
9368 \r
9369   case CPComm:\r
9370     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9371     break;\r
9372 \r
9373   case CPSock:\r
9374     closesocket(cp->sock);\r
9375     WSACleanup();\r
9376     break;\r
9377 \r
9378   case CPRcmd:\r
9379     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9380     closesocket(cp->sock);\r
9381     closesocket(cp->sock2);\r
9382     WSACleanup();\r
9383     break;\r
9384   }\r
9385   free(cp);\r
9386 }\r
9387 \r
9388 void\r
9389 InterruptChildProcess(ProcRef pr)\r
9390 {\r
9391   ChildProc *cp;\r
9392 \r
9393   cp = (ChildProc *) pr;\r
9394   if (cp == NULL) return;\r
9395   switch (cp->kind) {\r
9396   case CPReal:\r
9397     /* The following doesn't work because the chess program\r
9398        doesn't "have the same console" as WinBoard.  Maybe\r
9399        we could arrange for this even though neither WinBoard\r
9400        nor the chess program uses a console for stdio */\r
9401     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9402     break;\r
9403 \r
9404   case CPComm:\r
9405   case CPSock:\r
9406     /* Can't interrupt */\r
9407     break;\r
9408 \r
9409   case CPRcmd:\r
9410     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9411     break;\r
9412   }\r
9413 }\r
9414 \r
9415 \r
9416 int\r
9417 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9418 {\r
9419   char cmdLine[MSG_SIZ];\r
9420 \r
9421   if (port[0] == NULLCHAR) {\r
9422     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9423   } else {\r
9424     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9425   }\r
9426   return StartChildProcess(cmdLine, "", pr);\r
9427 }\r
9428 \r
9429 \r
9430 /* Code to open TCP sockets */\r
9431 \r
9432 int\r
9433 OpenTCP(char *host, char *port, ProcRef *pr)\r
9434 {\r
9435   ChildProc *cp;\r
9436   int err;\r
9437   SOCKET s;\r
9438 \r
9439   struct sockaddr_in sa, mysa;\r
9440   struct hostent FAR *hp;\r
9441   unsigned short uport;\r
9442   WORD wVersionRequested;\r
9443   WSADATA wsaData;\r
9444 \r
9445   /* Initialize socket DLL */\r
9446   wVersionRequested = MAKEWORD(1, 1);\r
9447   err = WSAStartup(wVersionRequested, &wsaData);\r
9448   if (err != 0) return err;\r
9449 \r
9450   /* Make socket */\r
9451   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9452     err = WSAGetLastError();\r
9453     WSACleanup();\r
9454     return err;\r
9455   }\r
9456 \r
9457   /* Bind local address using (mostly) don't-care values.\r
9458    */\r
9459   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9460   mysa.sin_family = AF_INET;\r
9461   mysa.sin_addr.s_addr = INADDR_ANY;\r
9462   uport = (unsigned short) 0;\r
9463   mysa.sin_port = htons(uport);\r
9464   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9465       == SOCKET_ERROR) {\r
9466     err = WSAGetLastError();\r
9467     WSACleanup();\r
9468     return err;\r
9469   }\r
9470 \r
9471   /* Resolve remote host name */\r
9472   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9473   if (!(hp = gethostbyname(host))) {\r
9474     unsigned int b0, b1, b2, b3;\r
9475 \r
9476     err = WSAGetLastError();\r
9477 \r
9478     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9479       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9480       hp->h_addrtype = AF_INET;\r
9481       hp->h_length = 4;\r
9482       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9483       hp->h_addr_list[0] = (char *) malloc(4);\r
9484       hp->h_addr_list[0][0] = (char) b0;\r
9485       hp->h_addr_list[0][1] = (char) b1;\r
9486       hp->h_addr_list[0][2] = (char) b2;\r
9487       hp->h_addr_list[0][3] = (char) b3;\r
9488     } else {\r
9489       WSACleanup();\r
9490       return err;\r
9491     }\r
9492   }\r
9493   sa.sin_family = hp->h_addrtype;\r
9494   uport = (unsigned short) atoi(port);\r
9495   sa.sin_port = htons(uport);\r
9496   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9497 \r
9498   /* Make connection */\r
9499   if (connect(s, (struct sockaddr *) &sa,\r
9500               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9501     err = WSAGetLastError();\r
9502     WSACleanup();\r
9503     return err;\r
9504   }\r
9505 \r
9506   /* Prepare return value */\r
9507   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9508   cp->kind = CPSock;\r
9509   cp->sock = s;\r
9510   *pr = (ProcRef *) cp;\r
9511 \r
9512   return NO_ERROR;\r
9513 }\r
9514 \r
9515 int\r
9516 OpenCommPort(char *name, ProcRef *pr)\r
9517 {\r
9518   HANDLE h;\r
9519   COMMTIMEOUTS ct;\r
9520   ChildProc *cp;\r
9521   char fullname[MSG_SIZ];\r
9522 \r
9523   if (*name != '\\')\r
9524     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9525   else\r
9526     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9527 \r
9528   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9529                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9530   if (h == (HANDLE) -1) {\r
9531     return GetLastError();\r
9532   }\r
9533   hCommPort = h;\r
9534 \r
9535   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9536 \r
9537   /* Accumulate characters until a 100ms pause, then parse */\r
9538   ct.ReadIntervalTimeout = 100;\r
9539   ct.ReadTotalTimeoutMultiplier = 0;\r
9540   ct.ReadTotalTimeoutConstant = 0;\r
9541   ct.WriteTotalTimeoutMultiplier = 0;\r
9542   ct.WriteTotalTimeoutConstant = 0;\r
9543   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9544 \r
9545   /* Prepare return value */\r
9546   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9547   cp->kind = CPComm;\r
9548   cp->hFrom = h;\r
9549   cp->hTo = h;\r
9550   *pr = (ProcRef *) cp;\r
9551 \r
9552   return NO_ERROR;\r
9553 }\r
9554 \r
9555 int\r
9556 OpenLoopback(ProcRef *pr)\r
9557 {\r
9558   DisplayFatalError(_("Not implemented"), 0, 1);\r
9559   return NO_ERROR;\r
9560 }\r
9561 \r
9562 \r
9563 int\r
9564 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9565 {\r
9566   ChildProc *cp;\r
9567   int err;\r
9568   SOCKET s, s2, s3;\r
9569   struct sockaddr_in sa, mysa;\r
9570   struct hostent FAR *hp;\r
9571   unsigned short uport;\r
9572   WORD wVersionRequested;\r
9573   WSADATA wsaData;\r
9574   int fromPort;\r
9575   char stderrPortStr[MSG_SIZ];\r
9576 \r
9577   /* Initialize socket DLL */\r
9578   wVersionRequested = MAKEWORD(1, 1);\r
9579   err = WSAStartup(wVersionRequested, &wsaData);\r
9580   if (err != 0) return err;\r
9581 \r
9582   /* Resolve remote host name */\r
9583   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9584   if (!(hp = gethostbyname(host))) {\r
9585     unsigned int b0, b1, b2, b3;\r
9586 \r
9587     err = WSAGetLastError();\r
9588 \r
9589     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9590       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9591       hp->h_addrtype = AF_INET;\r
9592       hp->h_length = 4;\r
9593       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9594       hp->h_addr_list[0] = (char *) malloc(4);\r
9595       hp->h_addr_list[0][0] = (char) b0;\r
9596       hp->h_addr_list[0][1] = (char) b1;\r
9597       hp->h_addr_list[0][2] = (char) b2;\r
9598       hp->h_addr_list[0][3] = (char) b3;\r
9599     } else {\r
9600       WSACleanup();\r
9601       return err;\r
9602     }\r
9603   }\r
9604   sa.sin_family = hp->h_addrtype;\r
9605   uport = (unsigned short) 514;\r
9606   sa.sin_port = htons(uport);\r
9607   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9608 \r
9609   /* Bind local socket to unused "privileged" port address\r
9610    */\r
9611   s = INVALID_SOCKET;\r
9612   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9613   mysa.sin_family = AF_INET;\r
9614   mysa.sin_addr.s_addr = INADDR_ANY;\r
9615   for (fromPort = 1023;; fromPort--) {\r
9616     if (fromPort < 0) {\r
9617       WSACleanup();\r
9618       return WSAEADDRINUSE;\r
9619     }\r
9620     if (s == INVALID_SOCKET) {\r
9621       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9622         err = WSAGetLastError();\r
9623         WSACleanup();\r
9624         return err;\r
9625       }\r
9626     }\r
9627     uport = (unsigned short) fromPort;\r
9628     mysa.sin_port = htons(uport);\r
9629     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9630         == SOCKET_ERROR) {\r
9631       err = WSAGetLastError();\r
9632       if (err == WSAEADDRINUSE) continue;\r
9633       WSACleanup();\r
9634       return err;\r
9635     }\r
9636     if (connect(s, (struct sockaddr *) &sa,\r
9637       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9638       err = WSAGetLastError();\r
9639       if (err == WSAEADDRINUSE) {\r
9640         closesocket(s);\r
9641         s = -1;\r
9642         continue;\r
9643       }\r
9644       WSACleanup();\r
9645       return err;\r
9646     }\r
9647     break;\r
9648   }\r
9649 \r
9650   /* Bind stderr local socket to unused "privileged" port address\r
9651    */\r
9652   s2 = INVALID_SOCKET;\r
9653   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9654   mysa.sin_family = AF_INET;\r
9655   mysa.sin_addr.s_addr = INADDR_ANY;\r
9656   for (fromPort = 1023;; fromPort--) {\r
9657     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9658     if (fromPort < 0) {\r
9659       (void) closesocket(s);\r
9660       WSACleanup();\r
9661       return WSAEADDRINUSE;\r
9662     }\r
9663     if (s2 == INVALID_SOCKET) {\r
9664       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9665         err = WSAGetLastError();\r
9666         closesocket(s);\r
9667         WSACleanup();\r
9668         return err;\r
9669       }\r
9670     }\r
9671     uport = (unsigned short) fromPort;\r
9672     mysa.sin_port = htons(uport);\r
9673     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9674         == SOCKET_ERROR) {\r
9675       err = WSAGetLastError();\r
9676       if (err == WSAEADDRINUSE) continue;\r
9677       (void) closesocket(s);\r
9678       WSACleanup();\r
9679       return err;\r
9680     }\r
9681     if (listen(s2, 1) == SOCKET_ERROR) {\r
9682       err = WSAGetLastError();\r
9683       if (err == WSAEADDRINUSE) {\r
9684         closesocket(s2);\r
9685         s2 = INVALID_SOCKET;\r
9686         continue;\r
9687       }\r
9688       (void) closesocket(s);\r
9689       (void) closesocket(s2);\r
9690       WSACleanup();\r
9691       return err;\r
9692     }\r
9693     break;\r
9694   }\r
9695   prevStderrPort = fromPort; // remember port used\r
9696   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9697 \r
9698   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9699     err = WSAGetLastError();\r
9700     (void) closesocket(s);\r
9701     (void) closesocket(s2);\r
9702     WSACleanup();\r
9703     return err;\r
9704   }\r
9705 \r
9706   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9707     err = WSAGetLastError();\r
9708     (void) closesocket(s);\r
9709     (void) closesocket(s2);\r
9710     WSACleanup();\r
9711     return err;\r
9712   }\r
9713   if (*user == NULLCHAR) user = UserName();\r
9714   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9715     err = WSAGetLastError();\r
9716     (void) closesocket(s);\r
9717     (void) closesocket(s2);\r
9718     WSACleanup();\r
9719     return err;\r
9720   }\r
9721   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9722     err = WSAGetLastError();\r
9723     (void) closesocket(s);\r
9724     (void) closesocket(s2);\r
9725     WSACleanup();\r
9726     return err;\r
9727   }\r
9728 \r
9729   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9730     err = WSAGetLastError();\r
9731     (void) closesocket(s);\r
9732     (void) closesocket(s2);\r
9733     WSACleanup();\r
9734     return err;\r
9735   }\r
9736   (void) closesocket(s2);  /* Stop listening */\r
9737 \r
9738   /* Prepare return value */\r
9739   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9740   cp->kind = CPRcmd;\r
9741   cp->sock = s;\r
9742   cp->sock2 = s3;\r
9743   *pr = (ProcRef *) cp;\r
9744 \r
9745   return NO_ERROR;\r
9746 }\r
9747 \r
9748 \r
9749 InputSourceRef\r
9750 AddInputSource(ProcRef pr, int lineByLine,\r
9751                InputCallback func, VOIDSTAR closure)\r
9752 {\r
9753   InputSource *is, *is2 = NULL;\r
9754   ChildProc *cp = (ChildProc *) pr;\r
9755 \r
9756   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9757   is->lineByLine = lineByLine;\r
9758   is->func = func;\r
9759   is->closure = closure;\r
9760   is->second = NULL;\r
9761   is->next = is->buf;\r
9762   if (pr == NoProc) {\r
9763     is->kind = CPReal;\r
9764     consoleInputSource = is;\r
9765   } else {\r
9766     is->kind = cp->kind;\r
9767     /* \r
9768         [AS] Try to avoid a race condition if the thread is given control too early:\r
9769         we create all threads suspended so that the is->hThread variable can be\r
9770         safely assigned, then let the threads start with ResumeThread.\r
9771     */\r
9772     switch (cp->kind) {\r
9773     case CPReal:\r
9774       is->hFile = cp->hFrom;\r
9775       cp->hFrom = NULL; /* now owned by InputThread */\r
9776       is->hThread =\r
9777         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9778                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9779       break;\r
9780 \r
9781     case CPComm:\r
9782       is->hFile = cp->hFrom;\r
9783       cp->hFrom = NULL; /* now owned by InputThread */\r
9784       is->hThread =\r
9785         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9786                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9787       break;\r
9788 \r
9789     case CPSock:\r
9790       is->sock = cp->sock;\r
9791       is->hThread =\r
9792         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9793                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9794       break;\r
9795 \r
9796     case CPRcmd:\r
9797       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9798       *is2 = *is;\r
9799       is->sock = cp->sock;\r
9800       is->second = is2;\r
9801       is2->sock = cp->sock2;\r
9802       is2->second = is2;\r
9803       is->hThread =\r
9804         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9805                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9806       is2->hThread =\r
9807         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9808                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9809       break;\r
9810     }\r
9811 \r
9812     if( is->hThread != NULL ) {\r
9813         ResumeThread( is->hThread );\r
9814     }\r
9815 \r
9816     if( is2 != NULL && is2->hThread != NULL ) {\r
9817         ResumeThread( is2->hThread );\r
9818     }\r
9819   }\r
9820 \r
9821   return (InputSourceRef) is;\r
9822 }\r
9823 \r
9824 void\r
9825 RemoveInputSource(InputSourceRef isr)\r
9826 {\r
9827   InputSource *is;\r
9828 \r
9829   is = (InputSource *) isr;\r
9830   is->hThread = NULL;  /* tell thread to stop */\r
9831   CloseHandle(is->hThread);\r
9832   if (is->second != NULL) {\r
9833     is->second->hThread = NULL;\r
9834     CloseHandle(is->second->hThread);\r
9835   }\r
9836 }\r
9837 \r
9838 int no_wrap(char *message, int count)\r
9839 {\r
9840     ConsoleOutput(message, count, FALSE);\r
9841     return count;\r
9842 }\r
9843 \r
9844 int\r
9845 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9846 {\r
9847   DWORD dOutCount;\r
9848   int outCount = SOCKET_ERROR;\r
9849   ChildProc *cp = (ChildProc *) pr;\r
9850   static OVERLAPPED ovl;\r
9851 \r
9852   static int line = 0;\r
9853 \r
9854   if (pr == NoProc)\r
9855   {\r
9856     if (appData.noJoin || !appData.useInternalWrap)\r
9857       return no_wrap(message, count);\r
9858     else\r
9859     {\r
9860       int width = get_term_width();\r
9861       int len = wrap(NULL, message, count, width, &line);\r
9862       char *msg = malloc(len);\r
9863       int dbgchk;\r
9864 \r
9865       if (!msg)\r
9866         return no_wrap(message, count);\r
9867       else\r
9868       {\r
9869         dbgchk = wrap(msg, message, count, width, &line);\r
9870         if (dbgchk != len && appData.debugMode)\r
9871             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9872         ConsoleOutput(msg, len, FALSE);\r
9873         free(msg);\r
9874         return len;\r
9875       }\r
9876     }\r
9877   }\r
9878 \r
9879   if (ovl.hEvent == NULL) {\r
9880     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9881   }\r
9882   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9883 \r
9884   switch (cp->kind) {\r
9885   case CPSock:\r
9886   case CPRcmd:\r
9887     outCount = send(cp->sock, message, count, 0);\r
9888     if (outCount == SOCKET_ERROR) {\r
9889       *outError = WSAGetLastError();\r
9890     } else {\r
9891       *outError = NO_ERROR;\r
9892     }\r
9893     break;\r
9894 \r
9895   case CPReal:\r
9896     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9897                   &dOutCount, NULL)) {\r
9898       *outError = NO_ERROR;\r
9899       outCount = (int) dOutCount;\r
9900     } else {\r
9901       *outError = GetLastError();\r
9902     }\r
9903     break;\r
9904 \r
9905   case CPComm:\r
9906     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9907                             &dOutCount, &ovl);\r
9908     if (*outError == NO_ERROR) {\r
9909       outCount = (int) dOutCount;\r
9910     }\r
9911     break;\r
9912   }\r
9913   return outCount;\r
9914 }\r
9915 \r
9916 void\r
9917 DoSleep(int n)\r
9918 {\r
9919     if(n != 0) Sleep(n);\r
9920 }\r
9921 \r
9922 int\r
9923 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9924                        long msdelay)\r
9925 {\r
9926   /* Ignore delay, not implemented for WinBoard */\r
9927   return OutputToProcess(pr, message, count, outError);\r
9928 }\r
9929 \r
9930 \r
9931 void\r
9932 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9933                         char *buf, int count, int error)\r
9934 {\r
9935   DisplayFatalError(_("Not implemented"), 0, 1);\r
9936 }\r
9937 \r
9938 /* see wgamelist.c for Game List functions */\r
9939 /* see wedittags.c for Edit Tags functions */\r
9940 \r
9941 \r
9942 int\r
9943 ICSInitScript()\r
9944 {\r
9945   FILE *f;\r
9946   char buf[MSG_SIZ];\r
9947   char *dummy;\r
9948 \r
9949   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9950     f = fopen(buf, "r");\r
9951     if (f != NULL) {\r
9952       ProcessICSInitScript(f);\r
9953       fclose(f);\r
9954       return TRUE;\r
9955     }\r
9956   }\r
9957   return FALSE;\r
9958 }\r
9959 \r
9960 \r
9961 VOID\r
9962 StartAnalysisClock()\r
9963 {\r
9964   if (analysisTimerEvent) return;\r
9965   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9966                                         (UINT) 2000, NULL);\r
9967 }\r
9968 \r
9969 VOID\r
9970 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9971 {\r
9972   highlightInfo.sq[0].x = fromX;\r
9973   highlightInfo.sq[0].y = fromY;\r
9974   highlightInfo.sq[1].x = toX;\r
9975   highlightInfo.sq[1].y = toY;\r
9976 }\r
9977 \r
9978 VOID\r
9979 ClearHighlights()\r
9980 {\r
9981   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9982     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9983 }\r
9984 \r
9985 VOID\r
9986 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9987 {\r
9988   premoveHighlightInfo.sq[0].x = fromX;\r
9989   premoveHighlightInfo.sq[0].y = fromY;\r
9990   premoveHighlightInfo.sq[1].x = toX;\r
9991   premoveHighlightInfo.sq[1].y = toY;\r
9992 }\r
9993 \r
9994 VOID\r
9995 ClearPremoveHighlights()\r
9996 {\r
9997   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9998     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9999 }\r
10000 \r
10001 VOID\r
10002 ShutDownFrontEnd()\r
10003 {\r
10004   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10005   DeleteClipboardTempFiles();\r
10006 }\r
10007 \r
10008 void\r
10009 BoardToTop()\r
10010 {\r
10011     if (IsIconic(hwndMain))\r
10012       ShowWindow(hwndMain, SW_RESTORE);\r
10013 \r
10014     SetActiveWindow(hwndMain);\r
10015 }\r
10016 \r
10017 /*\r
10018  * Prototypes for animation support routines\r
10019  */\r
10020 static void ScreenSquare(int column, int row, POINT * pt);\r
10021 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10022      POINT frames[], int * nFrames);\r
10023 \r
10024 \r
10025 #define kFactor 4\r
10026 \r
10027 void\r
10028 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
10029 {       // [HGM] atomic: animate blast wave\r
10030         int i;\r
10031 \r
10032         explodeInfo.fromX = fromX;\r
10033         explodeInfo.fromY = fromY;\r
10034         explodeInfo.toX = toX;\r
10035         explodeInfo.toY = toY;\r
10036         for(i=1; i<4*kFactor; i++) {\r
10037             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
10038             DrawPosition(FALSE, board);\r
10039             Sleep(appData.animSpeed);\r
10040         }\r
10041         explodeInfo.radius = 0;\r
10042         DrawPosition(TRUE, board);\r
10043 }\r
10044 \r
10045 void\r
10046 AnimateMove(board, fromX, fromY, toX, toY)\r
10047      Board board;\r
10048      int fromX;\r
10049      int fromY;\r
10050      int toX;\r
10051      int toY;\r
10052 {\r
10053   ChessSquare piece, victim = EmptySquare, victim2 = EmptySquare;\r
10054   int x = toX, y = toY, x2 = kill2X;\r
10055   POINT start, finish, mid;\r
10056   POINT frames[kFactor * 2 + 1];\r
10057   int nFrames, n;\r
10058 \r
10059   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10060 \r
10061   if (!appData.animate) return;\r
10062   if (doingSizing) return;\r
10063   if (fromY < 0 || fromX < 0) return;\r
10064   piece = board[fromY][fromX];\r
10065   if (piece >= EmptySquare) return;\r
10066 \r
10067   if(x2 >= 0) toX = kill2X, toY = kill2Y,  victim = board[killY][killX], victim2 = board[kill2Y][kill2X]; else\r
10068   if(killX >= 0) toX = killX, toY = killY, victim = board[killY][killX]; // [HGM] lion: first to kill square\r
10069 \r
10070   animInfo.from.x = fromX;\r
10071   animInfo.from.y = fromY;\r
10072 \r
10073 again:\r
10074 \r
10075   ScreenSquare(fromX, fromY, &start);\r
10076   ScreenSquare(toX, toY, &finish);\r
10077 \r
10078   /* All moves except knight jumps move in straight line */\r
10079   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10080     mid.x = start.x + (finish.x - start.x) / 2;\r
10081     mid.y = start.y + (finish.y - start.y) / 2;\r
10082   } else {\r
10083     /* Knight: make straight movement then diagonal */\r
10084     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10085        mid.x = start.x + (finish.x - start.x) / 2;\r
10086        mid.y = start.y;\r
10087      } else {\r
10088        mid.x = start.x;\r
10089        mid.y = start.y + (finish.y - start.y) / 2;\r
10090      }\r
10091   }\r
10092   \r
10093   /* Don't use as many frames for very short moves */\r
10094   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10095     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10096   else\r
10097     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10098 \r
10099   animInfo.to.x = toX;\r
10100   animInfo.to.y = toY;\r
10101   animInfo.lastpos = start;\r
10102   animInfo.piece = piece;\r
10103   for (n = 0; n < nFrames; n++) {\r
10104     animInfo.pos = frames[n];\r
10105     DrawPosition(FALSE, board);\r
10106     animInfo.lastpos = animInfo.pos;\r
10107     Sleep(appData.animSpeed);\r
10108   }\r
10109   animInfo.pos = finish;\r
10110   DrawPosition(FALSE, board);\r
10111 \r
10112   if(toX == x2 && toY == kill2Y) {\r
10113     fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1;\r
10114     board[kill2Y][kill2X] = EmptySquare; goto again;\r
10115   } // second leg\r
10116   if(toX != x || toY != y) {\r
10117     fromX = toX; fromY = toY; toX = x; toY = y;\r
10118     board[killY][killX] = EmptySquare; goto again;\r
10119   } // second leg\r
10120 \r
10121 if(victim2 != EmptySquare) board[kill2Y][kill2X] = victim2;\r
10122 if(victim  != EmptySquare) board[killY][killX] = victim;\r
10123 \r
10124   animInfo.piece = EmptySquare;\r
10125   Explode(board, fromX, fromY, toX, toY);\r
10126 }\r
10127 \r
10128 /*      Convert board position to corner of screen rect and color       */\r
10129 \r
10130 static void\r
10131 ScreenSquare(column, row, pt)\r
10132      int column; int row; POINT * pt;\r
10133 {\r
10134   if (flipView) {\r
10135     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10136     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10137   } else {\r
10138     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10139     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10140   }\r
10141 }\r
10142 \r
10143 /*      Generate a series of frame coords from start->mid->finish.\r
10144         The movement rate doubles until the half way point is\r
10145         reached, then halves back down to the final destination,\r
10146         which gives a nice slow in/out effect. The algorithmn\r
10147         may seem to generate too many intermediates for short\r
10148         moves, but remember that the purpose is to attract the\r
10149         viewers attention to the piece about to be moved and\r
10150         then to where it ends up. Too few frames would be less\r
10151         noticeable.                                             */\r
10152 \r
10153 static void\r
10154 Tween(start, mid, finish, factor, frames, nFrames)\r
10155      POINT * start; POINT * mid;\r
10156      POINT * finish; int factor;\r
10157      POINT frames[]; int * nFrames;\r
10158 {\r
10159   int n, fraction = 1, count = 0;\r
10160 \r
10161   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10162   for (n = 0; n < factor; n++)\r
10163     fraction *= 2;\r
10164   for (n = 0; n < factor; n++) {\r
10165     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10166     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10167     count ++;\r
10168     fraction = fraction / 2;\r
10169   }\r
10170   \r
10171   /* Midpoint */\r
10172   frames[count] = *mid;\r
10173   count ++;\r
10174   \r
10175   /* Slow out, stepping 1/2, then 1/4, ... */\r
10176   fraction = 2;\r
10177   for (n = 0; n < factor; n++) {\r
10178     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10179     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10180     count ++;\r
10181     fraction = fraction * 2;\r
10182   }\r
10183   *nFrames = count;\r
10184 }\r
10185 \r
10186 void\r
10187 SettingsPopUp(ChessProgramState *cps)\r
10188 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10189       EngineOptionsPopup(savedHwnd, cps);\r
10190 }\r
10191 \r
10192 int flock(int fid, int code)\r
10193 {\r
10194     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10195     OVERLAPPED ov;\r
10196     ov.hEvent = NULL;\r
10197     ov.Offset = 0;\r
10198     ov.OffsetHigh = 0;\r
10199     switch(code) {\r
10200       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10201 \r
10202       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10203       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10204       default: return -1;\r
10205     }\r
10206     return 0;\r
10207 }\r
10208 \r
10209 char *\r
10210 Col2Text (int n)\r
10211 {\r
10212     static int i=0;\r
10213     static char col[8][20];\r
10214     COLORREF color = *(COLORREF *) colorVariable[n];\r
10215     i = i+1 & 7;\r
10216     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10217     return col[i];\r
10218 }\r
10219 \r
10220 void\r
10221 ActivateTheme (int new)\r
10222 {   // Redo initialization of features depending on options that can occur in themes\r
10223    InitTextures();\r
10224    if(new) InitDrawingColors();\r
10225    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10226    InitDrawingSizes(boardSize, 0);\r
10227    InvalidateRect(hwndMain, NULL, TRUE);\r
10228 }\r