Logout from ICS after fatal error
[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 VOID\r
3758 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3759 {\r
3760   static Board lastReq[2], lastDrawn[2];\r
3761   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3762   static int lastDrawnFlipView = 0;\r
3763   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3764   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3765   HDC tmphdc;\r
3766   HDC hdcmem;\r
3767   HBITMAP bufferBitmap;\r
3768   HBITMAP oldBitmap;\r
3769   RECT Rect;\r
3770   HRGN clips[MAX_CLIPS];\r
3771   ChessSquare dragged_piece = EmptySquare;\r
3772   int nr = twoBoards*partnerUp;\r
3773 \r
3774   /* I'm undecided on this - this function figures out whether a full\r
3775    * repaint is necessary on its own, so there's no real reason to have the\r
3776    * caller tell it that.  I think this can safely be set to FALSE - but\r
3777    * if we trust the callers not to request full repaints unnessesarily, then\r
3778    * we could skip some clipping work.  In other words, only request a full\r
3779    * redraw when the majority of pieces have changed positions (ie. flip, \r
3780    * gamestart and similar)  --Hawk\r
3781    */\r
3782   Boolean fullrepaint = repaint;\r
3783 \r
3784   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3785 \r
3786   if( DrawPositionNeedsFullRepaint() ) {\r
3787       fullrepaint = TRUE;\r
3788   }\r
3789 \r
3790   if (board == NULL) {\r
3791     if (!lastReqValid[nr]) {\r
3792       return;\r
3793     }\r
3794     board = lastReq[nr];\r
3795   } else {\r
3796     CopyBoard(lastReq[nr], board);\r
3797     lastReqValid[nr] = 1;\r
3798   }\r
3799 \r
3800   if (doingSizing) {\r
3801     return;\r
3802   }\r
3803 \r
3804   if (IsIconic(hwndMain)) {\r
3805     return;\r
3806   }\r
3807 \r
3808   if (hdc == NULL) {\r
3809     hdc = GetDC(hwndMain);\r
3810     if (!appData.monoMode) {\r
3811       SelectPalette(hdc, hPal, FALSE);\r
3812       RealizePalette(hdc);\r
3813     }\r
3814     releaseDC = TRUE;\r
3815   } else {\r
3816     releaseDC = FALSE;\r
3817   }\r
3818 \r
3819   /* Create some work-DCs */\r
3820   hdcmem = CreateCompatibleDC(hdc);\r
3821   tmphdc = CreateCompatibleDC(hdc);\r
3822 \r
3823   /* If dragging is in progress, we temporarely remove the piece */\r
3824   /* [HGM] or temporarily decrease count if stacked              */\r
3825   /*       !! Moved to before board compare !!                   */\r
3826   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3827     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3828     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3829             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3830         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3831     } else \r
3832     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3833             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3834         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3835     } else \r
3836         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3837   }\r
3838 \r
3839   /* Figure out which squares need updating by comparing the \r
3840    * newest board with the last drawn board and checking if\r
3841    * flipping has changed.\r
3842    */\r
3843   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3844     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3845       for (column = 0; column < BOARD_WIDTH; column++) {\r
3846         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3847           SquareToPos(row, column, &x, &y);\r
3848           clips[num_clips++] =\r
3849             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3850         }\r
3851       }\r
3852     }\r
3853    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3854     for (i=0; i<2; i++) {\r
3855       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3856           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3857         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3858             lastDrawnHighlight.sq[i].y >= 0) {\r
3859           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3860                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3861           clips[num_clips++] =\r
3862             CreateRectRgn(x - lineGap, y - lineGap, \r
3863                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3864         }\r
3865         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3866           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3867           clips[num_clips++] =\r
3868             CreateRectRgn(x - lineGap, y - lineGap, \r
3869                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3870         }\r
3871       }\r
3872     }\r
3873     for (i=0; i<2; i++) {\r
3874       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3875           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3876         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3877             lastDrawnPremove.sq[i].y >= 0) {\r
3878           SquareToPos(lastDrawnPremove.sq[i].y,\r
3879                       lastDrawnPremove.sq[i].x, &x, &y);\r
3880           clips[num_clips++] =\r
3881             CreateRectRgn(x - lineGap, y - lineGap, \r
3882                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3883         }\r
3884         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3885             premoveHighlightInfo.sq[i].y >= 0) {\r
3886           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3887                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3888           clips[num_clips++] =\r
3889             CreateRectRgn(x - lineGap, y - lineGap, \r
3890                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3891         }\r
3892       }\r
3893     }\r
3894    } else { // nr == 1\r
3895         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3896         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3897         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3898         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3899       for (i=0; i<2; i++) {\r
3900         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3901             partnerHighlightInfo.sq[i].y >= 0) {\r
3902           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3903                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3904           clips[num_clips++] =\r
3905             CreateRectRgn(x - lineGap, y - lineGap, \r
3906                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3907         }\r
3908         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3909             oldPartnerHighlight.sq[i].y >= 0) {\r
3910           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3911                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3912           clips[num_clips++] =\r
3913             CreateRectRgn(x - lineGap, y - lineGap, \r
3914                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3915         }\r
3916       }\r
3917    }\r
3918   } else {\r
3919     fullrepaint = TRUE;\r
3920   }\r
3921 \r
3922   /* Create a buffer bitmap - this is the actual bitmap\r
3923    * being written to.  When all the work is done, we can\r
3924    * copy it to the real DC (the screen).  This avoids\r
3925    * the problems with flickering.\r
3926    */\r
3927   GetClientRect(hwndMain, &Rect);\r
3928   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3929                                         Rect.bottom-Rect.top+1);\r
3930   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3931   if (!appData.monoMode) {\r
3932     SelectPalette(hdcmem, hPal, FALSE);\r
3933   }\r
3934 \r
3935   /* Create clips for dragging */\r
3936   if (!fullrepaint) {\r
3937     if (dragInfo.from.x >= 0) {\r
3938       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3939       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3940     }\r
3941     if (dragInfo.start.x >= 0) {\r
3942       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3943       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3944     }\r
3945     if (dragInfo.pos.x >= 0) {\r
3946       x = dragInfo.pos.x - squareSize / 2;\r
3947       y = dragInfo.pos.y - squareSize / 2;\r
3948       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3949     }\r
3950     if (dragInfo.lastpos.x >= 0) {\r
3951       x = dragInfo.lastpos.x - squareSize / 2;\r
3952       y = dragInfo.lastpos.y - squareSize / 2;\r
3953       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3954     }\r
3955   }\r
3956 \r
3957   /* Are we animating a move?  \r
3958    * If so, \r
3959    *   - remove the piece from the board (temporarely)\r
3960    *   - calculate the clipping region\r
3961    */\r
3962   if (!fullrepaint) {\r
3963     if (animInfo.piece != EmptySquare) {\r
3964       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3965       x = boardRect.left + animInfo.lastpos.x;\r
3966       y = boardRect.top + animInfo.lastpos.y;\r
3967       x2 = boardRect.left + animInfo.pos.x;\r
3968       y2 = boardRect.top + animInfo.pos.y;\r
3969       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3970       /* Slight kludge.  The real problem is that after AnimateMove is\r
3971          done, the position on the screen does not match lastDrawn.\r
3972          This currently causes trouble only on e.p. captures in\r
3973          atomic, where the piece moves to an empty square and then\r
3974          explodes.  The old and new positions both had an empty square\r
3975          at the destination, but animation has drawn a piece there and\r
3976          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3977       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3978     }\r
3979   }\r
3980 \r
3981   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3982   if (num_clips == 0)\r
3983     fullrepaint = TRUE;\r
3984 \r
3985   /* Set clipping on the memory DC */\r
3986   if (!fullrepaint) {\r
3987     SelectClipRgn(hdcmem, clips[0]);\r
3988     for (x = 1; x < num_clips; x++) {\r
3989       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3990         abort();  // this should never ever happen!\r
3991     }\r
3992   }\r
3993 \r
3994   /* Do all the drawing to the memory DC */\r
3995   if(explodeInfo.radius) { // [HGM] atomic\r
3996         HBRUSH oldBrush;\r
3997         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3998         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3999         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4000         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4001         x += squareSize/2;\r
4002         y += squareSize/2;\r
4003         if(!fullrepaint) {\r
4004           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4005           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4006         }\r
4007         DrawGridOnDC(hdcmem);\r
4008         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4009         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4010         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4011         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
4012         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4013         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4014         SelectObject(hdcmem, oldBrush);\r
4015   } else {\r
4016     if(border) DrawBackgroundOnDC(hdcmem);\r
4017     DrawGridOnDC(hdcmem);\r
4018     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
4019         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
4020         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
4021     } else {\r
4022         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
4023         oldPartnerHighlight = partnerHighlightInfo;\r
4024     }\r
4025     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4026   }\r
4027   if(nr == 0) // [HGM] dual: markers only on left board\r
4028   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4029     for (column = 0; column < BOARD_WIDTH; column++) {\r
4030         if (marker[row][column]) { // marker changes only occur with full repaint!\r
4031             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
4032             SquareToPos(row, column, &x, &y);\r
4033             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
4034                           x + 3*squareSize/4, y + 3*squareSize/4);\r
4035             SelectObject(hdcmem, oldBrush);\r
4036         }\r
4037     }\r
4038   }\r
4039 \r
4040   if( appData.highlightMoveWithArrow ) {\r
4041 \r
4042     DrawArrowHighlight(hdcmem);\r
4043   }\r
4044 \r
4045   DrawCoordsOnDC(hdcmem);\r
4046 \r
4047   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4048                  /* to make sure lastDrawn contains what is actually drawn */\r
4049 \r
4050   /* Put the dragged piece back into place and draw it (out of place!) */\r
4051     if (dragged_piece != EmptySquare) {\r
4052     /* [HGM] or restack */\r
4053     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4054                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4055     else\r
4056     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4057                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4058 \r
4059     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4060     x = dragInfo.pos.x - squareSize / 2;\r
4061     y = dragInfo.pos.y - squareSize / 2;\r
4062     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4063                   ((int) dragInfo.piece < (int) BlackPawn), \r
4064                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4065   }   \r
4066   \r
4067   /* Put the animated piece back into place and draw it */\r
4068   if (animInfo.piece != EmptySquare) {\r
4069     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4070     x = boardRect.left + animInfo.pos.x;\r
4071     y = boardRect.top + animInfo.pos.y;\r
4072     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4073                   ((int) animInfo.piece < (int) BlackPawn),\r
4074                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4075   }\r
4076 \r
4077   /* Release the bufferBitmap by selecting in the old bitmap \r
4078    * and delete the memory DC\r
4079    */\r
4080   SelectObject(hdcmem, oldBitmap);\r
4081   DeleteDC(hdcmem);\r
4082 \r
4083   /* Set clipping on the target DC */\r
4084   if (!fullrepaint) {\r
4085     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4086         RECT rect;\r
4087         GetRgnBox(clips[x], &rect);\r
4088         DeleteObject(clips[x]);\r
4089         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4090                           rect.right + wpMain.width/2, rect.bottom);\r
4091     }\r
4092     SelectClipRgn(hdc, clips[0]);\r
4093     for (x = 1; x < num_clips; x++) {\r
4094       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4095         abort();   // this should never ever happen!\r
4096     } \r
4097   }\r
4098 \r
4099   /* Copy the new bitmap onto the screen in one go.\r
4100    * This way we avoid any flickering\r
4101    */\r
4102   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4103   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4104          boardRect.right - boardRect.left,\r
4105          boardRect.bottom - boardRect.top,\r
4106          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4107   if(saveDiagFlag) { \r
4108     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4109     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4110     HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);\r
4111 \r
4112     bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);\r
4113     obmp = SelectObject(tmp, bufferBitmap);\r
4114     BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,\r
4115            tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);\r
4116     GetObject(bufferBitmap, sizeof(b), &b);\r
4117     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4118         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4119         bih.biWidth = b.bmWidth;\r
4120         bih.biHeight = b.bmHeight;\r
4121         bih.biPlanes = 1;\r
4122         bih.biBitCount = b.bmBitsPixel;\r
4123         bih.biCompression = 0;\r
4124         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4125         bih.biXPelsPerMeter = 0;\r
4126         bih.biYPelsPerMeter = 0;\r
4127         bih.biClrUsed = 0;\r
4128         bih.biClrImportant = 0;\r
4129 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4130 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4131         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4132 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4133 \r
4134         wb = b.bmWidthBytes;\r
4135         // count colors\r
4136         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4137                 int k = ((int*) pData)[i];\r
4138                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4139                 if(j >= 16) break;\r
4140                 color[j] = k;\r
4141                 if(j >= nrColors) nrColors = j+1;\r
4142         }\r
4143         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4144                 INT p = 0;\r
4145                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4146                     for(w=0; w<(wb>>2); w+=2) {\r
4147                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4148                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4149                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4150                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4151                         pData[p++] = m | j<<4;\r
4152                     }\r
4153                     while(p&3) pData[p++] = 0;\r
4154                 }\r
4155                 fac = 3;\r
4156                 wb = ((wb+31)>>5)<<2;\r
4157         }\r
4158         // write BITMAPFILEHEADER\r
4159         fprintf(diagFile, "BM");\r
4160         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4161         fputDW(diagFile, 0);\r
4162         fputDW(diagFile, 0x36 + (fac?64:0));\r
4163         // write BITMAPINFOHEADER\r
4164         fputDW(diagFile, 40);\r
4165         fputDW(diagFile, b.bmWidth);\r
4166         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4167         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4168         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4169         fputDW(diagFile, 0);\r
4170         fputDW(diagFile, 0);\r
4171         fputDW(diagFile, 0);\r
4172         fputDW(diagFile, 0);\r
4173         fputDW(diagFile, 0);\r
4174         fputDW(diagFile, 0);\r
4175         // write color table\r
4176         if(fac)\r
4177         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4178         // write bitmap data\r
4179         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4180                 fputc(pData[i], diagFile);\r
4181         free(pData);\r
4182      }\r
4183      DeleteObject(bufferBitmap); bufferBitmap = src;\r
4184      SelectObject(tmp, obmp);\r
4185      DeleteDC(tmp);\r
4186   }\r
4187 \r
4188   SelectObject(tmphdc, oldBitmap);\r
4189 \r
4190   /* Massive cleanup */\r
4191   for (x = 0; x < num_clips; x++)\r
4192     DeleteObject(clips[x]);\r
4193 \r
4194   DeleteDC(tmphdc);\r
4195   DeleteObject(bufferBitmap);\r
4196 \r
4197   if (releaseDC) \r
4198     ReleaseDC(hwndMain, hdc);\r
4199   \r
4200   if (lastDrawnFlipView != flipView && nr == 0) {\r
4201     if (flipView)\r
4202       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4203     else\r
4204       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4205   }\r
4206 \r
4207 /*  CopyBoard(lastDrawn, board);*/\r
4208   lastDrawnHighlight = highlightInfo;\r
4209   lastDrawnPremove   = premoveHighlightInfo;\r
4210   lastDrawnFlipView = flipView;\r
4211   lastDrawnValid[nr] = 1;\r
4212 }\r
4213 \r
4214 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4215 int\r
4216 SaveDiagram(f)\r
4217      FILE *f;\r
4218 {\r
4219     saveDiagFlag = 1; diagFile = f;\r
4220     HDCDrawPosition(NULL, TRUE, NULL);\r
4221     saveDiagFlag = 0;\r
4222 \r
4223     fclose(f);\r
4224     return TRUE;\r
4225 }\r
4226 \r
4227 \r
4228 /*---------------------------------------------------------------------------*\\r
4229 | CLIENT PAINT PROCEDURE\r
4230 |   This is the main event-handler for the WM_PAINT message.\r
4231 |\r
4232 \*---------------------------------------------------------------------------*/\r
4233 VOID\r
4234 PaintProc(HWND hwnd)\r
4235 {\r
4236   HDC         hdc;\r
4237   PAINTSTRUCT ps;\r
4238   HFONT       oldFont;\r
4239 \r
4240   if((hdc = BeginPaint(hwnd, &ps))) {\r
4241     if (IsIconic(hwnd)) {\r
4242       DrawIcon(hdc, 2, 2, iconCurrent);\r
4243     } else {\r
4244       if (!appData.monoMode) {\r
4245         SelectPalette(hdc, hPal, FALSE);\r
4246         RealizePalette(hdc);\r
4247       }\r
4248       HDCDrawPosition(hdc, 1, NULL);\r
4249       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4250         flipView = !flipView; partnerUp = !partnerUp;\r
4251         HDCDrawPosition(hdc, 1, NULL);\r
4252         flipView = !flipView; partnerUp = !partnerUp;\r
4253       }\r
4254       oldFont =\r
4255         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4256       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4257                  ETO_CLIPPED|ETO_OPAQUE,\r
4258                  &messageRect, messageText, strlen(messageText), NULL);\r
4259       SelectObject(hdc, oldFont);\r
4260       DisplayBothClocks();\r
4261       DisplayLogos();\r
4262     }\r
4263     EndPaint(hwnd,&ps);\r
4264   }\r
4265 \r
4266   return;\r
4267 }\r
4268 \r
4269 \r
4270 /*\r
4271  * If the user selects on a border boundary, return -1; if off the board,\r
4272  *   return -2.  Otherwise map the event coordinate to the square.\r
4273  * The offset boardRect.left or boardRect.top must already have been\r
4274  *   subtracted from x.\r
4275  */\r
4276 int EventToSquare(x, limit)\r
4277      int x, limit;\r
4278 {\r
4279   if (x <= border)\r
4280     return -2;\r
4281   if (x < lineGap + border)\r
4282     return -1;\r
4283   x -= lineGap + border;\r
4284   if ((x % (squareSize + lineGap)) >= squareSize)\r
4285     return -1;\r
4286   x /= (squareSize + lineGap);\r
4287     if (x >= limit)\r
4288     return -2;\r
4289   return x;\r
4290 }\r
4291 \r
4292 typedef struct {\r
4293   char piece;\r
4294   int command;\r
4295   char* name;\r
4296 } DropEnable;\r
4297 \r
4298 DropEnable dropEnables[] = {\r
4299   { 'P', DP_Pawn, N_("Pawn") },\r
4300   { 'N', DP_Knight, N_("Knight") },\r
4301   { 'B', DP_Bishop, N_("Bishop") },\r
4302   { 'R', DP_Rook, N_("Rook") },\r
4303   { 'Q', DP_Queen, N_("Queen") },\r
4304 };\r
4305 \r
4306 VOID\r
4307 SetupDropMenu(HMENU hmenu)\r
4308 {\r
4309   int i, count, enable;\r
4310   char *p;\r
4311   extern char white_holding[], black_holding[];\r
4312   char item[MSG_SIZ];\r
4313 \r
4314   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4315     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4316                dropEnables[i].piece);\r
4317     count = 0;\r
4318     while (p && *p++ == dropEnables[i].piece) count++;\r
4319       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4320     enable = count > 0 || !appData.testLegality\r
4321       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4322                       && !appData.icsActive);\r
4323     ModifyMenu(hmenu, dropEnables[i].command,\r
4324                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4325                dropEnables[i].command, item);\r
4326   }\r
4327 }\r
4328 \r
4329 void DragPieceBegin(int x, int y, Boolean instantly)\r
4330 {\r
4331       dragInfo.lastpos.x = boardRect.left + x;\r
4332       dragInfo.lastpos.y = boardRect.top + y;\r
4333       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4334       dragInfo.from.x = fromX;\r
4335       dragInfo.from.y = fromY;\r
4336       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4337       dragInfo.start = dragInfo.from;\r
4338       SetCapture(hwndMain);\r
4339 }\r
4340 \r
4341 void DragPieceEnd(int x, int y)\r
4342 {\r
4343     ReleaseCapture();\r
4344     dragInfo.start.x = dragInfo.start.y = -1;\r
4345     dragInfo.from = dragInfo.start;\r
4346     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4347 }\r
4348 \r
4349 void ChangeDragPiece(ChessSquare piece)\r
4350 {\r
4351     dragInfo.piece = piece;\r
4352 }\r
4353 \r
4354 /* Event handler for mouse messages */\r
4355 VOID\r
4356 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4357 {\r
4358   int x, y, menuNr;\r
4359   POINT pt;\r
4360   static int recursive = 0;\r
4361   HMENU hmenu;\r
4362   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4363 \r
4364   if (recursive) {\r
4365     if (message == WM_MBUTTONUP) {\r
4366       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4367          to the middle button: we simulate pressing the left button too!\r
4368          */\r
4369       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4370       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4371     }\r
4372     return;\r
4373   }\r
4374   recursive++;\r
4375   \r
4376   pt.x = LOWORD(lParam);\r
4377   pt.y = HIWORD(lParam);\r
4378   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4379   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4380   if (!flipView && y >= 0) {\r
4381     y = BOARD_HEIGHT - 1 - y;\r
4382   }\r
4383   if (flipView && x >= 0) {\r
4384     x = BOARD_WIDTH - 1 - x;\r
4385   }\r
4386 \r
4387   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4388   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4389 \r
4390   switch (message) {\r
4391   case WM_LBUTTONDOWN:\r
4392       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4393         ClockClick(flipClock); break;\r
4394       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4395         ClockClick(!flipClock); break;\r
4396       }\r
4397     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4398       dragInfo.start.x = dragInfo.start.y = -1;\r
4399       dragInfo.from = dragInfo.start;\r
4400     }\r
4401     if(fromX == -1 && frozen) { // not sure where this is for\r
4402                 fromX = fromY = -1; \r
4403       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4404       break;\r
4405     }\r
4406       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4407       DrawPosition(TRUE, NULL);\r
4408     break;\r
4409 \r
4410   case WM_LBUTTONUP:\r
4411       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4412       DrawPosition(TRUE, NULL);\r
4413     break;\r
4414 \r
4415   case WM_MOUSEMOVE:\r
4416     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4417     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4418     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4419     if ((appData.animateDragging || appData.highlightDragging)\r
4420         && (wParam & MK_LBUTTON || dragging == 2)\r
4421         && dragInfo.from.x >= 0) \r
4422     {\r
4423       BOOL full_repaint = FALSE;\r
4424 \r
4425       if (appData.animateDragging) {\r
4426         dragInfo.pos = pt;\r
4427       }\r
4428       if (appData.highlightDragging) {\r
4429         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4430         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4431             full_repaint = TRUE;\r
4432         }\r
4433       }\r
4434       \r
4435       DrawPosition( full_repaint, NULL);\r
4436       \r
4437       dragInfo.lastpos = dragInfo.pos;\r
4438     }\r
4439     break;\r
4440 \r
4441   case WM_MOUSEWHEEL: // [DM]\r
4442     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4443        /* Mouse Wheel is being rolled forward\r
4444         * Play moves forward\r
4445         */\r
4446        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4447                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4448        /* Mouse Wheel is being rolled backward\r
4449         * Play moves backward\r
4450         */\r
4451        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4452                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4453     }\r
4454     break;\r
4455 \r
4456   case WM_MBUTTONUP:\r
4457   case WM_RBUTTONUP:\r
4458     ReleaseCapture();\r
4459     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4460     break;\r
4461  \r
4462   case WM_MBUTTONDOWN:\r
4463   case WM_RBUTTONDOWN:\r
4464     ErrorPopDown();\r
4465     ReleaseCapture();\r
4466     fromX = fromY = -1;\r
4467     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4468     dragInfo.start.x = dragInfo.start.y = -1;\r
4469     dragInfo.from = dragInfo.start;\r
4470     dragInfo.lastpos = dragInfo.pos;\r
4471     if (appData.highlightDragging) {\r
4472       ClearHighlights();\r
4473     }\r
4474     if(y == -2) {\r
4475       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4476       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4477           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4478       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4479           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4480       }\r
4481       break;\r
4482     }\r
4483     DrawPosition(TRUE, NULL);\r
4484 \r
4485     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4486     switch (menuNr) {\r
4487     case 0:\r
4488       if (message == WM_MBUTTONDOWN) {\r
4489         buttonCount = 3;  /* even if system didn't think so */\r
4490         if (wParam & MK_SHIFT) \r
4491           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4492         else\r
4493           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4494       } else { /* message == WM_RBUTTONDOWN */\r
4495         /* Just have one menu, on the right button.  Windows users don't\r
4496            think to try the middle one, and sometimes other software steals\r
4497            it, or it doesn't really exist. */\r
4498         if(gameInfo.variant != VariantShogi)\r
4499             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4500         else\r
4501             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4502       }\r
4503       break;\r
4504     case 2:\r
4505       SetCapture(hwndMain);\r
4506       break;\r
4507     case 1:\r
4508       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4509       SetupDropMenu(hmenu);\r
4510       MenuPopup(hwnd, pt, hmenu, -1);\r
4511     default:\r
4512       break;\r
4513     }\r
4514     break;\r
4515   }\r
4516 \r
4517   recursive--;\r
4518 }\r
4519 \r
4520 /* Preprocess messages for buttons in main window */\r
4521 LRESULT CALLBACK\r
4522 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4523 {\r
4524   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4525   int i, dir;\r
4526 \r
4527   for (i=0; i<N_BUTTONS; i++) {\r
4528     if (buttonDesc[i].id == id) break;\r
4529   }\r
4530   if (i == N_BUTTONS) return 0;\r
4531   switch (message) {\r
4532   case WM_KEYDOWN:\r
4533     switch (wParam) {\r
4534     case VK_LEFT:\r
4535     case VK_RIGHT:\r
4536       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4537       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4538       return TRUE;\r
4539     }\r
4540     break;\r
4541   case WM_CHAR:\r
4542     switch (wParam) {\r
4543     case '\r':\r
4544       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4545       return TRUE;\r
4546     default:\r
4547       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4548         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4549         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4550         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4551         SetFocus(h);\r
4552         SendMessage(h, WM_CHAR, wParam, lParam);\r
4553         return TRUE;\r
4554       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4555         TypeInEvent((char)wParam);\r
4556       }\r
4557       break;\r
4558     }\r
4559     break;\r
4560   }\r
4561   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4562 }\r
4563 \r
4564 static int promoStyle;\r
4565 \r
4566 /* Process messages for Promotion dialog box */\r
4567 LRESULT CALLBACK\r
4568 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4569 {\r
4570 \r
4571   char promoChar;\r
4572 \r
4573   switch (message) {\r
4574 \r
4575   case WM_INITDIALOG: /* message: initialize dialog box */\r
4576     /* Center the dialog over the application window */\r
4577     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4578     Translate(hDlg, DLG_PromotionKing);\r
4579     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4580       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4581        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4582        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4583                SW_SHOW : SW_HIDE);\r
4584     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4585     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4586        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4587          PieceToChar(WhiteAngel) != '~') ||\r
4588         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4589          PieceToChar(BlackAngel) != '~')   ) ?\r
4590                SW_SHOW : SW_HIDE);\r
4591     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4592        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4593          PieceToChar(WhiteMarshall) != '~') ||\r
4594         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4595          PieceToChar(BlackMarshall) != '~')   ) ?\r
4596                SW_SHOW : SW_HIDE);\r
4597     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4598     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4599     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4600     if(promoStyle) {\r
4601         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4602         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4603         SetWindowText(hDlg, "Promote?");\r
4604     }\r
4605     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4606        gameInfo.variant == VariantSuper ?\r
4607                SW_SHOW : SW_HIDE);\r
4608     return TRUE;\r
4609 \r
4610   case WM_COMMAND: /* message: received a command */\r
4611     switch (LOWORD(wParam)) {\r
4612     case IDCANCEL:\r
4613       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4614       ClearHighlights();\r
4615       DrawPosition(FALSE, NULL);\r
4616       return TRUE;\r
4617     case PB_King:\r
4618       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4619       break;\r
4620     case PB_Queen:\r
4621       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4622       break;\r
4623     case PB_Rook:\r
4624       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4625       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4626       break;\r
4627     case PB_Bishop:\r
4628       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4629       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4630       break;\r
4631     case PB_Chancellor:\r
4632       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4633       break;\r
4634     case PB_Archbishop:\r
4635       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4636       break;\r
4637     case PB_Knight:\r
4638       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4639                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4640       break;\r
4641     default:\r
4642       return FALSE;\r
4643     }\r
4644     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4645     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4646     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4647     fromX = fromY = -1;\r
4648     if (!appData.highlightLastMove) {\r
4649       ClearHighlights();\r
4650       DrawPosition(FALSE, NULL);\r
4651     }\r
4652     return TRUE;\r
4653   }\r
4654   return FALSE;\r
4655 }\r
4656 \r
4657 /* Pop up promotion dialog */\r
4658 VOID\r
4659 PromotionPopup(HWND hwnd)\r
4660 {\r
4661   FARPROC lpProc;\r
4662 \r
4663   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4664   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4665     hwnd, (DLGPROC)lpProc);\r
4666   FreeProcInstance(lpProc);\r
4667 }\r
4668 \r
4669 void\r
4670 PromotionPopUp(char choice)\r
4671 {\r
4672   promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
4673   DrawPosition(TRUE, NULL);\r
4674   PromotionPopup(hwndMain);\r
4675 }\r
4676 \r
4677 VOID\r
4678 LoadGameDialog(HWND hwnd, char* title)\r
4679 {\r
4680   UINT number = 0;\r
4681   FILE *f;\r
4682   char fileTitle[MSG_SIZ];\r
4683   f = OpenFileDialog(hwnd, "rb", "",\r
4684                      appData.oldSaveStyle ? "gam" : "pgn",\r
4685                      GAME_FILT,\r
4686                      title, &number, fileTitle, NULL);\r
4687   if (f != NULL) {\r
4688     cmailMsgLoaded = FALSE;\r
4689     if (number == 0) {\r
4690       int error = GameListBuild(f);\r
4691       if (error) {\r
4692         DisplayError(_("Cannot build game list"), error);\r
4693       } else if (!ListEmpty(&gameList) &&\r
4694                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4695         GameListPopUp(f, fileTitle);\r
4696         return;\r
4697       }\r
4698       GameListDestroy();\r
4699       number = 1;\r
4700     }\r
4701     LoadGame(f, number, fileTitle, FALSE);\r
4702   }\r
4703 }\r
4704 \r
4705 int get_term_width()\r
4706 {\r
4707     HDC hdc;\r
4708     TEXTMETRIC tm;\r
4709     RECT rc;\r
4710     HFONT hfont, hold_font;\r
4711     LOGFONT lf;\r
4712     HWND hText;\r
4713 \r
4714     if (hwndConsole)\r
4715         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4716     else\r
4717         return 79;\r
4718 \r
4719     // get the text metrics\r
4720     hdc = GetDC(hText);\r
4721     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4722     if (consoleCF.dwEffects & CFE_BOLD)\r
4723         lf.lfWeight = FW_BOLD;\r
4724     if (consoleCF.dwEffects & CFE_ITALIC)\r
4725         lf.lfItalic = TRUE;\r
4726     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4727         lf.lfStrikeOut = TRUE;\r
4728     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4729         lf.lfUnderline = TRUE;\r
4730     hfont = CreateFontIndirect(&lf);\r
4731     hold_font = SelectObject(hdc, hfont);\r
4732     GetTextMetrics(hdc, &tm);\r
4733     SelectObject(hdc, hold_font);\r
4734     DeleteObject(hfont);\r
4735     ReleaseDC(hText, hdc);\r
4736 \r
4737     // get the rectangle\r
4738     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4739 \r
4740     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4741 }\r
4742 \r
4743 void UpdateICSWidth(HWND hText)\r
4744 {\r
4745     LONG old_width, new_width;\r
4746 \r
4747     new_width = get_term_width(hText, FALSE);\r
4748     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4749     if (new_width != old_width)\r
4750     {\r
4751         ics_update_width(new_width);\r
4752         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4753     }\r
4754 }\r
4755 \r
4756 VOID\r
4757 ChangedConsoleFont()\r
4758 {\r
4759   CHARFORMAT cfmt;\r
4760   CHARRANGE tmpsel, sel;\r
4761   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4762   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4763   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4764   PARAFORMAT paraf;\r
4765 \r
4766   cfmt.cbSize = sizeof(CHARFORMAT);\r
4767   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4768     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4769                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4770   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4771    * size.  This was undocumented in the version of MSVC++ that I had\r
4772    * when I wrote the code, but is apparently documented now.\r
4773    */\r
4774   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4775   cfmt.bCharSet = f->lf.lfCharSet;\r
4776   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4777   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4778   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4779   /* Why are the following seemingly needed too? */\r
4780   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4781   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4782   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4783   tmpsel.cpMin = 0;\r
4784   tmpsel.cpMax = -1; /*999999?*/\r
4785   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4786   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4787   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4788    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4789    */\r
4790   paraf.cbSize = sizeof(paraf);\r
4791   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4792   paraf.dxStartIndent = 0;\r
4793   paraf.dxOffset = WRAP_INDENT;\r
4794   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4795   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4796   UpdateICSWidth(hText);\r
4797 }\r
4798 \r
4799 /*---------------------------------------------------------------------------*\\r
4800  *\r
4801  * Window Proc for main window\r
4802  *\r
4803 \*---------------------------------------------------------------------------*/\r
4804 \r
4805 /* Process messages for main window, etc. */\r
4806 LRESULT CALLBACK\r
4807 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4808 {\r
4809   FARPROC lpProc;\r
4810   int wmId;\r
4811   char *defName;\r
4812   FILE *f;\r
4813   UINT number;\r
4814   char fileTitle[MSG_SIZ];\r
4815   static SnapData sd;\r
4816   static int peek=0;\r
4817 \r
4818   switch (message) {\r
4819 \r
4820   case WM_PAINT: /* message: repaint portion of window */\r
4821     PaintProc(hwnd);\r
4822     break;\r
4823 \r
4824   case WM_ERASEBKGND:\r
4825     if (IsIconic(hwnd)) {\r
4826       /* Cheat; change the message */\r
4827       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4828     } else {\r
4829       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4830     }\r
4831     break;\r
4832 \r
4833   case WM_LBUTTONDOWN:\r
4834   case WM_MBUTTONDOWN:\r
4835   case WM_RBUTTONDOWN:\r
4836   case WM_LBUTTONUP:\r
4837   case WM_MBUTTONUP:\r
4838   case WM_RBUTTONUP:\r
4839   case WM_MOUSEMOVE:\r
4840   case WM_MOUSEWHEEL:\r
4841     MouseEvent(hwnd, message, wParam, lParam);\r
4842     break;\r
4843 \r
4844   case WM_KEYUP:\r
4845     if((char)wParam == '\b') {\r
4846       ForwardEvent(); peek = 0;\r
4847     }\r
4848 \r
4849     JAWS_KBUP_NAVIGATION\r
4850 \r
4851     break;\r
4852 \r
4853   case WM_KEYDOWN:\r
4854     if((char)wParam == '\b') {\r
4855       if(!peek) BackwardEvent(), peek = 1;\r
4856     }\r
4857 \r
4858     JAWS_KBDOWN_NAVIGATION\r
4859 \r
4860     break;\r
4861 \r
4862   case WM_CHAR:\r
4863     \r
4864     JAWS_ALT_INTERCEPT\r
4865 \r
4866     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4867         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4868         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4869         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4870         SetFocus(h);\r
4871         SendMessage(h, message, wParam, lParam);\r
4872     } else if(lParam != KF_REPEAT) {\r
4873         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4874                 TypeInEvent((char)wParam);\r
4875         } else if((char)wParam == 003) CopyGameToClipboard();\r
4876          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4877     }\r
4878 \r
4879     break;\r
4880 \r
4881   case WM_PALETTECHANGED:\r
4882     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4883       int nnew;\r
4884       HDC hdc = GetDC(hwndMain);\r
4885       SelectPalette(hdc, hPal, TRUE);\r
4886       nnew = RealizePalette(hdc);\r
4887       if (nnew > 0) {\r
4888         paletteChanged = TRUE;\r
4889 \r
4890         InvalidateRect(hwnd, &boardRect, FALSE);\r
4891       }\r
4892       ReleaseDC(hwnd, hdc);\r
4893     }\r
4894     break;\r
4895 \r
4896   case WM_QUERYNEWPALETTE:\r
4897     if (!appData.monoMode /*&& paletteChanged*/) {\r
4898       int nnew;\r
4899       HDC hdc = GetDC(hwndMain);\r
4900       paletteChanged = FALSE;\r
4901       SelectPalette(hdc, hPal, FALSE);\r
4902       nnew = RealizePalette(hdc);\r
4903       if (nnew > 0) {\r
4904         InvalidateRect(hwnd, &boardRect, FALSE);\r
4905       }\r
4906       ReleaseDC(hwnd, hdc);\r
4907       return TRUE;\r
4908     }\r
4909     return FALSE;\r
4910 \r
4911   case WM_COMMAND: /* message: command from application menu */\r
4912     wmId    = LOWORD(wParam);\r
4913 \r
4914     switch (wmId) {\r
4915     case IDM_NewGame:\r
4916       ResetGameEvent();\r
4917       SAY("new game enter a move to play against the computer with white");\r
4918       break;\r
4919 \r
4920     case IDM_NewGameFRC:\r
4921       if( NewGameFRC() == 0 ) {\r
4922         ResetGameEvent();\r
4923       }\r
4924       break;\r
4925 \r
4926     case IDM_NewVariant:\r
4927       NewVariantPopup(hwnd);\r
4928       break;\r
4929 \r
4930     case IDM_LoadGame:\r
4931       LoadGameDialog(hwnd, _("Load Game from File"));\r
4932       break;\r
4933 \r
4934     case IDM_LoadNextGame:\r
4935       ReloadGame(1);\r
4936       break;\r
4937 \r
4938     case IDM_LoadPrevGame:\r
4939       ReloadGame(-1);\r
4940       break;\r
4941 \r
4942     case IDM_ReloadGame:\r
4943       ReloadGame(0);\r
4944       break;\r
4945 \r
4946     case IDM_LoadPosition:\r
4947       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4948         Reset(FALSE, TRUE);\r
4949       }\r
4950       number = 1;\r
4951       f = OpenFileDialog(hwnd, "rb", "",\r
4952                          appData.oldSaveStyle ? "pos" : "fen",\r
4953                          POSITION_FILT,\r
4954                          _("Load Position from File"), &number, fileTitle, NULL);\r
4955       if (f != NULL) {\r
4956         LoadPosition(f, number, fileTitle);\r
4957       }\r
4958       break;\r
4959 \r
4960     case IDM_LoadNextPosition:\r
4961       ReloadPosition(1);\r
4962       break;\r
4963 \r
4964     case IDM_LoadPrevPosition:\r
4965       ReloadPosition(-1);\r
4966       break;\r
4967 \r
4968     case IDM_ReloadPosition:\r
4969       ReloadPosition(0);\r
4970       break;\r
4971 \r
4972     case IDM_SaveGame:\r
4973       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4974       f = OpenFileDialog(hwnd, "a", defName,\r
4975                          appData.oldSaveStyle ? "gam" : "pgn",\r
4976                          GAME_FILT,\r
4977                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4978       if (f != NULL) {\r
4979         SaveGame(f, 0, "");\r
4980       }\r
4981       break;\r
4982 \r
4983     case IDM_SavePosition:\r
4984       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4985       f = OpenFileDialog(hwnd, "a", defName,\r
4986                          appData.oldSaveStyle ? "pos" : "fen",\r
4987                          POSITION_FILT,\r
4988                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4989       if (f != NULL) {\r
4990         SavePosition(f, 0, "");\r
4991       }\r
4992       break;\r
4993 \r
4994     case IDM_SaveDiagram:\r
4995       defName = "diagram";\r
4996       f = OpenFileDialog(hwnd, "wb", defName,\r
4997                          "bmp",\r
4998                          DIAGRAM_FILT,\r
4999                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
5000       if (f != NULL) {\r
5001         SaveDiagram(f);\r
5002       }\r
5003       break;\r
5004 \r
5005     case IDM_SaveSelected:\r
5006       f = OpenFileDialog(hwnd, "a", "",\r
5007                          "pgn",\r
5008                          GAME_FILT,\r
5009                          _("Save Game to File"), NULL, fileTitle, NULL);\r
5010       if (f != NULL) {\r
5011         SaveSelected(f, 0, "");\r
5012       }\r
5013       break;\r
5014 \r
5015     case IDM_CreateBook:\r
5016       CreateBookEvent();\r
5017       break;\r
5018 \r
5019     case IDM_CopyGame:\r
5020       CopyGameToClipboard();\r
5021       break;\r
5022 \r
5023     case IDM_PasteGame:\r
5024       PasteGameFromClipboard();\r
5025       break;\r
5026 \r
5027     case IDM_CopyGameListToClipboard:\r
5028       CopyGameListToClipboard();\r
5029       break;\r
5030 \r
5031     /* [AS] Autodetect FEN or PGN data */\r
5032     case IDM_PasteAny:\r
5033       PasteGameOrFENFromClipboard();\r
5034       break;\r
5035 \r
5036     /* [AS] Move history */\r
5037     case IDM_ShowMoveHistory:\r
5038         if( MoveHistoryIsUp() ) {\r
5039             MoveHistoryPopDown();\r
5040         }\r
5041         else {\r
5042             MoveHistoryPopUp();\r
5043         }\r
5044         break;\r
5045 \r
5046     /* [AS] Eval graph */\r
5047     case IDM_ShowEvalGraph:\r
5048         if( EvalGraphIsUp() ) {\r
5049             EvalGraphPopDown();\r
5050         }\r
5051         else {\r
5052             EvalGraphPopUp();\r
5053             SetFocus(hwndMain);\r
5054         }\r
5055         break;\r
5056 \r
5057     /* [AS] Engine output */\r
5058     case IDM_ShowEngineOutput:\r
5059         if( EngineOutputIsUp() ) {\r
5060             EngineOutputPopDown();\r
5061         }\r
5062         else {\r
5063             EngineOutputPopUp();\r
5064         }\r
5065         break;\r
5066 \r
5067     /* [AS] User adjudication */\r
5068     case IDM_UserAdjudication_White:\r
5069         UserAdjudicationEvent( +1 );\r
5070         break;\r
5071 \r
5072     case IDM_UserAdjudication_Black:\r
5073         UserAdjudicationEvent( -1 );\r
5074         break;\r
5075 \r
5076     case IDM_UserAdjudication_Draw:\r
5077         UserAdjudicationEvent( 0 );\r
5078         break;\r
5079 \r
5080     /* [AS] Game list options dialog */\r
5081     case IDM_GameListOptions:\r
5082       GameListOptions();\r
5083       break;\r
5084 \r
5085     case IDM_NewChat:\r
5086       ChatPopUp(NULL);\r
5087       break;\r
5088 \r
5089     case IDM_CopyPosition:\r
5090       CopyFENToClipboard();\r
5091       break;\r
5092 \r
5093     case IDM_PastePosition:\r
5094       PasteFENFromClipboard();\r
5095       break;\r
5096 \r
5097     case IDM_MailMove:\r
5098       MailMoveEvent();\r
5099       break;\r
5100 \r
5101     case IDM_ReloadCMailMsg:\r
5102       Reset(TRUE, TRUE);\r
5103       ReloadCmailMsgEvent(FALSE);\r
5104       break;\r
5105 \r
5106     case IDM_Minimize:\r
5107       ShowWindow(hwnd, SW_MINIMIZE);\r
5108       break;\r
5109 \r
5110     case IDM_Exit:\r
5111       ExitEvent(0);\r
5112       break;\r
5113 \r
5114     case IDM_MachineWhite:\r
5115       MachineWhiteEvent();\r
5116       /*\r
5117        * refresh the tags dialog only if it's visible\r
5118        */\r
5119       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5120           char *tags;\r
5121           tags = PGNTags(&gameInfo);\r
5122           TagsPopUp(tags, CmailMsg());\r
5123           free(tags);\r
5124       }\r
5125       SAY("computer starts playing white");\r
5126       break;\r
5127 \r
5128     case IDM_MachineBlack:\r
5129       MachineBlackEvent();\r
5130       /*\r
5131        * refresh the tags dialog only if it's visible\r
5132        */\r
5133       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5134           char *tags;\r
5135           tags = PGNTags(&gameInfo);\r
5136           TagsPopUp(tags, CmailMsg());\r
5137           free(tags);\r
5138       }\r
5139       SAY("computer starts playing black");\r
5140       break;\r
5141 \r
5142     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5143       if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
5144       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5145       break;\r
5146 \r
5147     case IDM_TwoMachines:\r
5148       TwoMachinesEvent();\r
5149       /*\r
5150 \r
5151        * refresh the tags dialog only if it's visible\r
5152        */\r
5153       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5154           char *tags;\r
5155           tags = PGNTags(&gameInfo);\r
5156           TagsPopUp(tags, CmailMsg());\r
5157           free(tags);\r
5158       }\r
5159       SAY("computer starts playing both sides");\r
5160       break;\r
5161 \r
5162     case IDM_AnalysisMode:\r
5163       if(AnalyzeModeEvent()) {\r
5164         SAY("analyzing current position");\r
5165       }\r
5166       break;\r
5167 \r
5168     case IDM_AnalyzeFile:\r
5169       AnalyzeFileEvent();\r
5170       break;\r
5171 \r
5172     case IDM_IcsClient:\r
5173       IcsClientEvent();\r
5174       break;\r
5175 \r
5176     case IDM_EditGame:\r
5177     case IDM_EditGame2:\r
5178       EditGameEvent();\r
5179       SAY("edit game");\r
5180       break;\r
5181 \r
5182     case IDM_EditPosition:\r
5183     case IDM_EditPosition2:\r
5184       EditPositionEvent();\r
5185       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5186       break;\r
5187 \r
5188     case IDM_Training:\r
5189       TrainingEvent();\r
5190       break;\r
5191 \r
5192     case IDM_ShowGameList:\r
5193       ShowGameListProc();\r
5194       break;\r
5195 \r
5196     case IDM_EditProgs1:\r
5197       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5198       break;\r
5199 \r
5200     case IDM_LoadProg1:\r
5201      LoadEnginePopUp(hwndMain, 0);\r
5202       break;\r
5203 \r
5204     case IDM_LoadProg2:\r
5205      LoadEnginePopUp(hwndMain, 1);\r
5206       break;\r
5207 \r
5208     case IDM_EditServers:\r
5209       EditTagsPopUp(icsNames, &icsNames);\r
5210       break;\r
5211 \r
5212     case IDM_EditTags:\r
5213     case IDM_Tags:\r
5214       EditTagsProc();\r
5215       break;\r
5216 \r
5217     case IDM_EditBook:\r
5218       EditBookEvent();\r
5219       break;\r
5220 \r
5221     case IDM_EditComment:\r
5222     case IDM_Comment:\r
5223       if (commentUp && editComment) {\r
5224         CommentPopDown();\r
5225       } else {\r
5226         EditCommentEvent();\r
5227       }\r
5228       break;\r
5229 \r
5230     case IDM_Pause:\r
5231       PauseEvent();\r
5232       break;\r
5233 \r
5234     case IDM_Accept:\r
5235       AcceptEvent();\r
5236       break;\r
5237 \r
5238     case IDM_Decline:\r
5239       DeclineEvent();\r
5240       break;\r
5241 \r
5242     case IDM_Rematch:\r
5243 \r
5244       RematchEvent();\r
5245       break;\r
5246 \r
5247     case IDM_CallFlag:\r
5248       CallFlagEvent();\r
5249       break;\r
5250 \r
5251     case IDM_Draw:\r
5252       DrawEvent();\r
5253       break;\r
5254 \r
5255     case IDM_Adjourn:\r
5256       AdjournEvent();\r
5257       break;\r
5258 \r
5259     case IDM_Abort:\r
5260       AbortEvent();\r
5261       break;\r
5262 \r
5263     case IDM_Resign:\r
5264       ResignEvent();\r
5265       break;\r
5266 \r
5267     case IDM_StopObserving:\r
5268       StopObservingEvent();\r
5269       break;\r
5270 \r
5271     case IDM_StopExamining:\r
5272       StopExaminingEvent();\r
5273       break;\r
5274 \r
5275     case IDM_Upload:\r
5276       UploadGameEvent();\r
5277       break;\r
5278 \r
5279     case IDM_TypeInMove:\r
5280       TypeInEvent('\000');\r
5281       break;\r
5282 \r
5283     case IDM_TypeInName:\r
5284       PopUpNameDialog('\000');\r
5285       break;\r
5286 \r
5287     case IDM_Backward:\r
5288       BackwardEvent();\r
5289       SetFocus(hwndMain);\r
5290       break;\r
5291 \r
5292     JAWS_MENU_ITEMS\r
5293 \r
5294     case IDM_Forward:\r
5295       ForwardEvent();\r
5296       SetFocus(hwndMain);\r
5297       break;\r
5298 \r
5299     case IDM_ToStart:\r
5300       ToStartEvent();\r
5301       SetFocus(hwndMain);\r
5302       break;\r
5303 \r
5304     case IDM_ToEnd:\r
5305       ToEndEvent();\r
5306       SetFocus(hwndMain);\r
5307       break;\r
5308 \r
5309     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5310     case OPT_GameListPrev:\r
5311       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5312       break;\r
5313 \r
5314     case IDM_Revert:\r
5315       RevertEvent(FALSE);\r
5316       break;\r
5317 \r
5318     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5319       RevertEvent(TRUE);\r
5320       break;\r
5321 \r
5322     case IDM_TruncateGame:\r
5323       TruncateGameEvent();\r
5324       break;\r
5325 \r
5326     case IDM_MoveNow:\r
5327       MoveNowEvent();\r
5328       break;\r
5329 \r
5330     case IDM_RetractMove:\r
5331       RetractMoveEvent();\r
5332       break;\r
5333 \r
5334     case IDM_FlipView:\r
5335       flipView = !flipView;\r
5336       DrawPosition(FALSE, NULL);\r
5337       break;\r
5338 \r
5339     case IDM_FlipClock:\r
5340       flipClock = !flipClock;\r
5341       DisplayBothClocks();\r
5342       DisplayLogos();\r
5343       break;\r
5344 \r
5345     case IDM_MuteSounds:\r
5346       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5347       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5348                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5349       break;\r
5350 \r
5351     case IDM_GeneralOptions:\r
5352       GeneralOptionsPopup(hwnd);\r
5353       DrawPosition(TRUE, NULL);\r
5354       break;\r
5355 \r
5356     case IDM_BoardOptions:\r
5357       BoardOptionsPopup(hwnd);\r
5358       break;\r
5359 \r
5360     case IDM_ThemeOptions:\r
5361       ThemeOptionsPopup(hwnd);\r
5362       break;\r
5363 \r
5364     case IDM_EnginePlayOptions:\r
5365       EnginePlayOptionsPopup(hwnd);\r
5366       break;\r
5367 \r
5368     case IDM_Engine1Options:\r
5369       EngineOptionsPopup(hwnd, &first);\r
5370       break;\r
5371 \r
5372     case IDM_Engine2Options:\r
5373       savedHwnd = hwnd;\r
5374       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5375       EngineOptionsPopup(hwnd, &second);\r
5376       break;\r
5377 \r
5378     case IDM_OptionsUCI:\r
5379       UciOptionsPopup(hwnd);\r
5380       break;\r
5381 \r
5382     case IDM_Tourney:\r
5383       TourneyPopup(hwnd);\r
5384       break;\r
5385 \r
5386     case IDM_IcsOptions:\r
5387       IcsOptionsPopup(hwnd);\r
5388       break;\r
5389 \r
5390     case IDM_Fonts:\r
5391       FontsOptionsPopup(hwnd);\r
5392       break;\r
5393 \r
5394     case IDM_Sounds:\r
5395       SoundOptionsPopup(hwnd);\r
5396       break;\r
5397 \r
5398     case IDM_CommPort:\r
5399       CommPortOptionsPopup(hwnd);\r
5400       break;\r
5401 \r
5402     case IDM_LoadOptions:\r
5403       LoadOptionsPopup(hwnd);\r
5404       break;\r
5405 \r
5406     case IDM_SaveOptions:\r
5407       SaveOptionsPopup(hwnd);\r
5408       break;\r
5409 \r
5410     case IDM_TimeControl:\r
5411       TimeControlOptionsPopup(hwnd);\r
5412       break;\r
5413 \r
5414     case IDM_SaveSettings:\r
5415       SaveSettings(settingsFileName);\r
5416       break;\r
5417 \r
5418     case IDM_SaveSettingsOnExit:\r
5419       saveSettingsOnExit = !saveSettingsOnExit;\r
5420       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5421                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5422                                          MF_CHECKED : MF_UNCHECKED));\r
5423       break;\r
5424 \r
5425     case IDM_Hint:\r
5426       HintEvent();\r
5427       break;\r
5428 \r
5429     case IDM_Book:\r
5430       BookEvent();\r
5431       break;\r
5432 \r
5433     case IDM_AboutGame:\r
5434       AboutGameEvent();\r
5435       break;\r
5436 \r
5437     case IDM_Debug:\r
5438       appData.debugMode = !appData.debugMode;\r
5439       if (appData.debugMode) {\r
5440         char dir[MSG_SIZ];\r
5441         GetCurrentDirectory(MSG_SIZ, dir);\r
5442         SetCurrentDirectory(installDir);\r
5443         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5444         SetCurrentDirectory(dir);\r
5445         setbuf(debugFP, NULL);\r
5446       } else {\r
5447         fclose(debugFP);\r
5448         debugFP = NULL;\r
5449       }\r
5450       break;\r
5451 \r
5452     case IDM_HELPCONTENTS:\r
5453       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5454           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5455           MessageBox (GetFocus(),\r
5456                     _("Unable to activate help"),\r
5457                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5458       }\r
5459       break;\r
5460 \r
5461     case IDM_HELPSEARCH:\r
5462         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5463             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5464         MessageBox (GetFocus(),\r
5465                     _("Unable to activate help"),\r
5466                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5467       }\r
5468       break;\r
5469 \r
5470     case IDM_HELPHELP:\r
5471       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5472         MessageBox (GetFocus(),\r
5473                     _("Unable to activate help"),\r
5474                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5475       }\r
5476       break;\r
5477 \r
5478     case IDM_ABOUT:\r
5479       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5480       DialogBox(hInst, \r
5481         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5482         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5483       FreeProcInstance(lpProc);\r
5484       break;\r
5485 \r
5486     case IDM_DirectCommand1:\r
5487       AskQuestionEvent(_("Direct Command"),\r
5488                        _("Send to chess program:"), "", "1");\r
5489       break;\r
5490     case IDM_DirectCommand2:\r
5491       AskQuestionEvent(_("Direct Command"),\r
5492                        _("Send to second chess program:"), "", "2");\r
5493       break;\r
5494 \r
5495     case EP_WhitePawn:\r
5496       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5497       fromX = fromY = -1;\r
5498       break;\r
5499 \r
5500     case EP_WhiteKnight:\r
5501       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5502       fromX = fromY = -1;\r
5503       break;\r
5504 \r
5505     case EP_WhiteBishop:\r
5506       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5507       fromX = fromY = -1;\r
5508       break;\r
5509 \r
5510     case EP_WhiteRook:\r
5511       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5512       fromX = fromY = -1;\r
5513       break;\r
5514 \r
5515     case EP_WhiteQueen:\r
5516       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5517       fromX = fromY = -1;\r
5518       break;\r
5519 \r
5520     case EP_WhiteFerz:\r
5521       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5522       fromX = fromY = -1;\r
5523       break;\r
5524 \r
5525     case EP_WhiteWazir:\r
5526       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5527       fromX = fromY = -1;\r
5528       break;\r
5529 \r
5530     case EP_WhiteAlfil:\r
5531       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5532       fromX = fromY = -1;\r
5533       break;\r
5534 \r
5535     case EP_WhiteCannon:\r
5536       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5537       fromX = fromY = -1;\r
5538       break;\r
5539 \r
5540     case EP_WhiteCardinal:\r
5541       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5542       fromX = fromY = -1;\r
5543       break;\r
5544 \r
5545     case EP_WhiteMarshall:\r
5546       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5547       fromX = fromY = -1;\r
5548       break;\r
5549 \r
5550     case EP_WhiteKing:\r
5551       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5552       fromX = fromY = -1;\r
5553       break;\r
5554 \r
5555     case EP_BlackPawn:\r
5556       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5557       fromX = fromY = -1;\r
5558       break;\r
5559 \r
5560     case EP_BlackKnight:\r
5561       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5562       fromX = fromY = -1;\r
5563       break;\r
5564 \r
5565     case EP_BlackBishop:\r
5566       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5567       fromX = fromY = -1;\r
5568       break;\r
5569 \r
5570     case EP_BlackRook:\r
5571       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5572       fromX = fromY = -1;\r
5573       break;\r
5574 \r
5575     case EP_BlackQueen:\r
5576       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5577       fromX = fromY = -1;\r
5578       break;\r
5579 \r
5580     case EP_BlackFerz:\r
5581       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5582       fromX = fromY = -1;\r
5583       break;\r
5584 \r
5585     case EP_BlackWazir:\r
5586       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5587       fromX = fromY = -1;\r
5588       break;\r
5589 \r
5590     case EP_BlackAlfil:\r
5591       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5592       fromX = fromY = -1;\r
5593       break;\r
5594 \r
5595     case EP_BlackCannon:\r
5596       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5597       fromX = fromY = -1;\r
5598       break;\r
5599 \r
5600     case EP_BlackCardinal:\r
5601       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5602       fromX = fromY = -1;\r
5603       break;\r
5604 \r
5605     case EP_BlackMarshall:\r
5606       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5607       fromX = fromY = -1;\r
5608       break;\r
5609 \r
5610     case EP_BlackKing:\r
5611       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5612       fromX = fromY = -1;\r
5613       break;\r
5614 \r
5615     case EP_EmptySquare:\r
5616       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5617       fromX = fromY = -1;\r
5618       break;\r
5619 \r
5620     case EP_ClearBoard:\r
5621       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5622       fromX = fromY = -1;\r
5623       break;\r
5624 \r
5625     case EP_White:\r
5626       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5627       fromX = fromY = -1;\r
5628       break;\r
5629 \r
5630     case EP_Black:\r
5631       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5632       fromX = fromY = -1;\r
5633       break;\r
5634 \r
5635     case EP_Promote:\r
5636       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5637       fromX = fromY = -1;\r
5638       break;\r
5639 \r
5640     case EP_Demote:\r
5641       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5642       fromX = fromY = -1;\r
5643       break;\r
5644 \r
5645     case DP_Pawn:\r
5646       DropMenuEvent(WhitePawn, fromX, fromY);\r
5647       fromX = fromY = -1;\r
5648       break;\r
5649 \r
5650     case DP_Knight:\r
5651       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5652       fromX = fromY = -1;\r
5653       break;\r
5654 \r
5655     case DP_Bishop:\r
5656       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5657       fromX = fromY = -1;\r
5658       break;\r
5659 \r
5660     case DP_Rook:\r
5661       DropMenuEvent(WhiteRook, fromX, fromY);\r
5662       fromX = fromY = -1;\r
5663       break;\r
5664 \r
5665     case DP_Queen:\r
5666       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5667       fromX = fromY = -1;\r
5668       break;\r
5669 \r
5670     case IDM_English:\r
5671       barbaric = 0; appData.language = "";\r
5672       TranslateMenus(0);\r
5673       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5674       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5675       lastChecked = wmId;\r
5676       break;\r
5677 \r
5678     default:\r
5679       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5680           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5681       else\r
5682       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5683           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5684           TranslateMenus(0);\r
5685           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5686           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5687           lastChecked = wmId;\r
5688           break;\r
5689       }\r
5690       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5691     }\r
5692     break;\r
5693 \r
5694   case WM_TIMER:\r
5695     switch (wParam) {\r
5696     case CLOCK_TIMER_ID:\r
5697       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5698       clockTimerEvent = 0;\r
5699       DecrementClocks(); /* call into back end */\r
5700       break;\r
5701     case LOAD_GAME_TIMER_ID:\r
5702       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5703       loadGameTimerEvent = 0;\r
5704       AutoPlayGameLoop(); /* call into back end */\r
5705       break;\r
5706     case ANALYSIS_TIMER_ID:\r
5707       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5708                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5709         AnalysisPeriodicEvent(0);\r
5710       } else {\r
5711         KillTimer(hwnd, analysisTimerEvent);\r
5712         analysisTimerEvent = 0;\r
5713       }\r
5714       break;\r
5715     case DELAYED_TIMER_ID:\r
5716       KillTimer(hwnd, delayedTimerEvent);\r
5717       delayedTimerEvent = 0;\r
5718       delayedTimerCallback();\r
5719       break;\r
5720     }\r
5721     break;\r
5722 \r
5723   case WM_USER_Input:\r
5724     InputEvent(hwnd, message, wParam, lParam);\r
5725     break;\r
5726 \r
5727   /* [AS] Also move "attached" child windows */\r
5728   case WM_WINDOWPOSCHANGING:\r
5729 \r
5730     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5731         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5732 \r
5733         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5734             /* Window is moving */\r
5735             RECT rcMain;\r
5736 \r
5737 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5738             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5739             rcMain.right  = wpMain.x + wpMain.width;\r
5740             rcMain.top    = wpMain.y;\r
5741             rcMain.bottom = wpMain.y + wpMain.height;\r
5742             \r
5743             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5744             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5745             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5746             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5747             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5748             wpMain.x = lpwp->x;\r
5749             wpMain.y = lpwp->y;\r
5750 \r
5751         }\r
5752     }\r
5753     break;\r
5754 \r
5755   /* [AS] Snapping */\r
5756   case WM_ENTERSIZEMOVE:\r
5757     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5758     if (hwnd == hwndMain) {\r
5759       doingSizing = TRUE;\r
5760       lastSizing = 0;\r
5761     }\r
5762     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5763     break;\r
5764 \r
5765   case WM_SIZING:\r
5766     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5767     if (hwnd == hwndMain) {\r
5768       lastSizing = wParam;\r
5769     }\r
5770     break;\r
5771 \r
5772   case WM_MOVING:\r
5773     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5774       return OnMoving( &sd, hwnd, wParam, lParam );\r
5775 \r
5776   case WM_EXITSIZEMOVE:\r
5777     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5778     if (hwnd == hwndMain) {\r
5779       RECT client;\r
5780       doingSizing = FALSE;\r
5781       InvalidateRect(hwnd, &boardRect, FALSE);\r
5782       GetClientRect(hwnd, &client);\r
5783       ResizeBoard(client.right, client.bottom, lastSizing);\r
5784       lastSizing = 0;\r
5785       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5786     }\r
5787     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5788     break;\r
5789 \r
5790   case WM_DESTROY: /* message: window being destroyed */\r
5791     PostQuitMessage(0);\r
5792     break;\r
5793 \r
5794   case WM_CLOSE:\r
5795     if (hwnd == hwndMain) {\r
5796       ExitEvent(0);\r
5797     }\r
5798     break;\r
5799 \r
5800   default:      /* Passes it on if unprocessed */\r
5801     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5802   }\r
5803 \r
5804 \r
5805   return 0;\r
5806 }\r
5807 \r
5808 /*---------------------------------------------------------------------------*\\r
5809  *\r
5810  * Misc utility routines\r
5811  *\r
5812 \*---------------------------------------------------------------------------*/\r
5813 \r
5814 /*\r
5815  * Decent random number generator, at least not as bad as Windows\r
5816  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5817  */\r
5818 unsigned int randstate;\r
5819 \r
5820 int\r
5821 myrandom(void)\r
5822 {\r
5823   randstate = randstate * 1664525 + 1013904223;\r
5824   return (int) randstate & 0x7fffffff;\r
5825 }\r
5826 \r
5827 void\r
5828 mysrandom(unsigned int seed)\r
5829 {\r
5830   randstate = seed;\r
5831 }\r
5832 \r
5833 \r
5834 /* \r
5835  * returns TRUE if user selects a different color, FALSE otherwise \r
5836  */\r
5837 \r
5838 BOOL\r
5839 ChangeColor(HWND hwnd, COLORREF *which)\r
5840 {\r
5841   static BOOL firstTime = TRUE;\r
5842   static DWORD customColors[16];\r
5843   CHOOSECOLOR cc;\r
5844   COLORREF newcolor;\r
5845   int i;\r
5846   ColorClass ccl;\r
5847 \r
5848   if (firstTime) {\r
5849     /* Make initial colors in use available as custom colors */\r
5850     /* Should we put the compiled-in defaults here instead? */\r
5851     i = 0;\r
5852     customColors[i++] = lightSquareColor & 0xffffff;\r
5853     customColors[i++] = darkSquareColor & 0xffffff;\r
5854     customColors[i++] = whitePieceColor & 0xffffff;\r
5855     customColors[i++] = blackPieceColor & 0xffffff;\r
5856     customColors[i++] = highlightSquareColor & 0xffffff;\r
5857     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5858 \r
5859     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5860       customColors[i++] = textAttribs[ccl].color;\r
5861     }\r
5862     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5863     firstTime = FALSE;\r
5864   }\r
5865 \r
5866   cc.lStructSize = sizeof(cc);\r
5867   cc.hwndOwner = hwnd;\r
5868   cc.hInstance = NULL;\r
5869   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5870   cc.lpCustColors = (LPDWORD) customColors;\r
5871   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5872 \r
5873   if (!ChooseColor(&cc)) return FALSE;\r
5874 \r
5875   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5876   if (newcolor == *which) return FALSE;\r
5877   *which = newcolor;\r
5878   return TRUE;\r
5879 \r
5880   /*\r
5881   InitDrawingColors();\r
5882   InvalidateRect(hwnd, &boardRect, FALSE);\r
5883   */\r
5884 }\r
5885 \r
5886 BOOLEAN\r
5887 MyLoadSound(MySound *ms)\r
5888 {\r
5889   BOOL ok = FALSE;\r
5890   struct stat st;\r
5891   FILE *f;\r
5892 \r
5893   if (ms->data && ms->flag) free(ms->data);\r
5894   ms->data = NULL;\r
5895 \r
5896   switch (ms->name[0]) {\r
5897   case NULLCHAR:\r
5898     /* Silence */\r
5899     ok = TRUE;\r
5900     break;\r
5901   case '$':\r
5902     /* System sound from Control Panel.  Don't preload here. */\r
5903     ok = TRUE;\r
5904     break;\r
5905   case '!':\r
5906     if (ms->name[1] == NULLCHAR) {\r
5907       /* "!" alone = silence */\r
5908       ok = TRUE;\r
5909     } else {\r
5910       /* Builtin wave resource.  Error if not found. */\r
5911       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5912       if (h == NULL) break;\r
5913       ms->data = (void *)LoadResource(hInst, h);\r
5914       ms->flag = 0; // not maloced, so cannot be freed!\r
5915       if (h == NULL) break;\r
5916       ok = TRUE;\r
5917     }\r
5918     break;\r
5919   default:\r
5920     /* .wav file.  Error if not found. */\r
5921     f = fopen(ms->name, "rb");\r
5922     if (f == NULL) break;\r
5923     if (fstat(fileno(f), &st) < 0) break;\r
5924     ms->data = malloc(st.st_size);\r
5925     ms->flag = 1;\r
5926     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5927     fclose(f);\r
5928     ok = TRUE;\r
5929     break;\r
5930   }\r
5931   if (!ok) {\r
5932     char buf[MSG_SIZ];\r
5933       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5934     DisplayError(buf, GetLastError());\r
5935   }\r
5936   return ok;\r
5937 }\r
5938 \r
5939 BOOLEAN\r
5940 MyPlaySound(MySound *ms)\r
5941 {\r
5942   BOOLEAN ok = FALSE;\r
5943 \r
5944   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5945   switch (ms->name[0]) {\r
5946   case NULLCHAR:\r
5947         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5948     /* Silence */\r
5949     ok = TRUE;\r
5950     break;\r
5951   case '$':\r
5952     /* System sound from Control Panel (deprecated feature).\r
5953        "$" alone or an unset sound name gets default beep (still in use). */\r
5954     if (ms->name[1]) {\r
5955       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5956     }\r
5957     if (!ok) ok = MessageBeep(MB_OK);\r
5958     break; \r
5959   case '!':\r
5960     /* Builtin wave resource, or "!" alone for silence */\r
5961     if (ms->name[1]) {\r
5962       if (ms->data == NULL) return FALSE;\r
5963       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5964     } else {\r
5965       ok = TRUE;\r
5966     }\r
5967     break;\r
5968   default:\r
5969     /* .wav file.  Error if not found. */\r
5970     if (ms->data == NULL) return FALSE;\r
5971     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5972     break;\r
5973   }\r
5974   /* Don't print an error: this can happen innocently if the sound driver\r
5975      is busy; for instance, if another instance of WinBoard is playing\r
5976      a sound at about the same time. */\r
5977   return ok;\r
5978 }\r
5979 \r
5980 \r
5981 LRESULT CALLBACK\r
5982 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5983 {\r
5984   BOOL ok;\r
5985   OPENFILENAME *ofn;\r
5986   static UINT *number; /* gross that this is static */\r
5987 \r
5988   switch (message) {\r
5989   case WM_INITDIALOG: /* message: initialize dialog box */\r
5990     /* Center the dialog over the application window */\r
5991     ofn = (OPENFILENAME *) lParam;\r
5992     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5993       number = (UINT *) ofn->lCustData;\r
5994       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5995     } else {\r
5996       number = NULL;\r
5997     }\r
5998     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5999     Translate(hDlg, 1536);\r
6000     return FALSE;  /* Allow for further processing */\r
6001 \r
6002   case WM_COMMAND:\r
6003     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6004       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6005     }\r
6006     return FALSE;  /* Allow for further processing */\r
6007   }\r
6008   return FALSE;\r
6009 }\r
6010 \r
6011 UINT APIENTRY\r
6012 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6013 {\r
6014   static UINT *number;\r
6015   OPENFILENAME *ofname;\r
6016   OFNOTIFY *ofnot;\r
6017   switch (uiMsg) {\r
6018   case WM_INITDIALOG:\r
6019     Translate(hdlg, DLG_IndexNumber);\r
6020     ofname = (OPENFILENAME *)lParam;\r
6021     number = (UINT *)(ofname->lCustData);\r
6022     break;\r
6023   case WM_NOTIFY:\r
6024     ofnot = (OFNOTIFY *)lParam;\r
6025     if (ofnot->hdr.code == CDN_FILEOK) {\r
6026       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6027     }\r
6028     break;\r
6029   }\r
6030   return 0;\r
6031 }\r
6032 \r
6033 \r
6034 FILE *\r
6035 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6036                char *nameFilt, char *dlgTitle, UINT *number,\r
6037                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6038 {\r
6039   OPENFILENAME openFileName;\r
6040   char buf1[MSG_SIZ];\r
6041   FILE *f;\r
6042 \r
6043   if (fileName == NULL) fileName = buf1;\r
6044   if (defName == NULL) {\r
6045     safeStrCpy(fileName, "*.", 3 );\r
6046     strcat(fileName, defExt);\r
6047   } else {\r
6048     safeStrCpy(fileName, defName, MSG_SIZ );\r
6049   }\r
6050     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
6051   if (number) *number = 0;\r
6052 \r
6053   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6054   openFileName.hwndOwner         = hwnd;\r
6055   openFileName.hInstance         = (HANDLE) hInst;\r
6056   openFileName.lpstrFilter       = nameFilt;\r
6057   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6058   openFileName.nMaxCustFilter    = 0L;\r
6059   openFileName.nFilterIndex      = 1L;\r
6060   openFileName.lpstrFile         = fileName;\r
6061   openFileName.nMaxFile          = MSG_SIZ;\r
6062   openFileName.lpstrFileTitle    = fileTitle;\r
6063   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6064   openFileName.lpstrInitialDir   = NULL;\r
6065   openFileName.lpstrTitle        = dlgTitle;\r
6066   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6067     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6068     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6069     | (oldDialog ? 0 : OFN_EXPLORER);\r
6070   openFileName.nFileOffset       = 0;\r
6071   openFileName.nFileExtension    = 0;\r
6072   openFileName.lpstrDefExt       = defExt;\r
6073   openFileName.lCustData         = (LONG) number;\r
6074   openFileName.lpfnHook          = oldDialog ?\r
6075     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6076   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6077 \r
6078   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6079                         GetOpenFileName(&openFileName)) {\r
6080     /* open the file */\r
6081     f = fopen(openFileName.lpstrFile, write);\r
6082     if (f == NULL) {\r
6083       MessageBox(hwnd, _("File open failed"), NULL,\r
6084                  MB_OK|MB_ICONEXCLAMATION);\r
6085       return NULL;\r
6086     }\r
6087   } else {\r
6088     int err = CommDlgExtendedError();\r
6089     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6090     return FALSE;\r
6091   }\r
6092   return f;\r
6093 }\r
6094 \r
6095 \r
6096 \r
6097 VOID APIENTRY\r
6098 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6099 {\r
6100   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6101 \r
6102   /*\r
6103    * Get the first pop-up menu in the menu template. This is the\r
6104    * menu that TrackPopupMenu displays.\r
6105    */\r
6106   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6107   TranslateOneMenu(10, hmenuTrackPopup);\r
6108 \r
6109   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6110 \r
6111   /*\r
6112    * TrackPopup uses screen coordinates, so convert the\r
6113    * coordinates of the mouse click to screen coordinates.\r
6114    */\r
6115   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6116 \r
6117   /* Draw and track the floating pop-up menu. */\r
6118   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6119                  pt.x, pt.y, 0, hwnd, NULL);\r
6120 \r
6121   /* Destroy the menu.*/\r
6122   DestroyMenu(hmenu);\r
6123 }\r
6124    \r
6125 typedef struct {\r
6126   HWND hDlg, hText;\r
6127   int sizeX, sizeY, newSizeX, newSizeY;\r
6128   HDWP hdwp;\r
6129 } ResizeEditPlusButtonsClosure;\r
6130 \r
6131 BOOL CALLBACK\r
6132 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6133 {\r
6134   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6135   RECT rect;\r
6136   POINT pt;\r
6137 \r
6138   if (hChild == cl->hText) return TRUE;\r
6139   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6140   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6141   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6142   ScreenToClient(cl->hDlg, &pt);\r
6143   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6144     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6145   return TRUE;\r
6146 }\r
6147 \r
6148 /* Resize a dialog that has a (rich) edit field filling most of\r
6149    the top, with a row of buttons below */\r
6150 VOID\r
6151 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6152 {\r
6153   RECT rectText;\r
6154   int newTextHeight, newTextWidth;\r
6155   ResizeEditPlusButtonsClosure cl;\r
6156   \r
6157   /*if (IsIconic(hDlg)) return;*/\r
6158   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6159   \r
6160   cl.hdwp = BeginDeferWindowPos(8);\r
6161 \r
6162   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6163   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6164   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6165   if (newTextHeight < 0) {\r
6166     newSizeY += -newTextHeight;\r
6167     newTextHeight = 0;\r
6168   }\r
6169   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6170     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6171 \r
6172   cl.hDlg = hDlg;\r
6173   cl.hText = hText;\r
6174   cl.sizeX = sizeX;\r
6175   cl.sizeY = sizeY;\r
6176   cl.newSizeX = newSizeX;\r
6177   cl.newSizeY = newSizeY;\r
6178   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6179 \r
6180   EndDeferWindowPos(cl.hdwp);\r
6181 }\r
6182 \r
6183 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6184 {\r
6185     RECT    rChild, rParent;\r
6186     int     wChild, hChild, wParent, hParent;\r
6187     int     wScreen, hScreen, xNew, yNew;\r
6188     HDC     hdc;\r
6189 \r
6190     /* Get the Height and Width of the child window */\r
6191     GetWindowRect (hwndChild, &rChild);\r
6192     wChild = rChild.right - rChild.left;\r
6193     hChild = rChild.bottom - rChild.top;\r
6194 \r
6195     /* Get the Height and Width of the parent window */\r
6196     GetWindowRect (hwndParent, &rParent);\r
6197     wParent = rParent.right - rParent.left;\r
6198     hParent = rParent.bottom - rParent.top;\r
6199 \r
6200     /* Get the display limits */\r
6201     hdc = GetDC (hwndChild);\r
6202     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6203     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6204     ReleaseDC(hwndChild, hdc);\r
6205 \r
6206     /* Calculate new X position, then adjust for screen */\r
6207     xNew = rParent.left + ((wParent - wChild) /2);\r
6208     if (xNew < 0) {\r
6209         xNew = 0;\r
6210     } else if ((xNew+wChild) > wScreen) {\r
6211         xNew = wScreen - wChild;\r
6212     }\r
6213 \r
6214     /* Calculate new Y position, then adjust for screen */\r
6215     if( mode == 0 ) {\r
6216         yNew = rParent.top  + ((hParent - hChild) /2);\r
6217     }\r
6218     else {\r
6219         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6220     }\r
6221 \r
6222     if (yNew < 0) {\r
6223         yNew = 0;\r
6224     } else if ((yNew+hChild) > hScreen) {\r
6225         yNew = hScreen - hChild;\r
6226     }\r
6227 \r
6228     /* Set it, and return */\r
6229     return SetWindowPos (hwndChild, NULL,\r
6230                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6231 }\r
6232 \r
6233 /* Center one window over another */\r
6234 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6235 {\r
6236     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6237 }\r
6238 \r
6239 /*---------------------------------------------------------------------------*\\r
6240  *\r
6241  * Startup Dialog functions\r
6242  *\r
6243 \*---------------------------------------------------------------------------*/\r
6244 void\r
6245 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6246 {\r
6247   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6248 \r
6249   while (*cd != NULL) {\r
6250     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6251     cd++;\r
6252   }\r
6253 }\r
6254 \r
6255 void\r
6256 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6257 {\r
6258   char buf1[MAX_ARG_LEN];\r
6259   int len;\r
6260 \r
6261   if (str[0] == '@') {\r
6262     FILE* f = fopen(str + 1, "r");\r
6263     if (f == NULL) {\r
6264       DisplayFatalError(str + 1, errno, 2);\r
6265       return;\r
6266     }\r
6267     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6268     fclose(f);\r
6269     buf1[len] = NULLCHAR;\r
6270     str = buf1;\r
6271   }\r
6272 \r
6273   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6274 \r
6275   for (;;) {\r
6276     char buf[MSG_SIZ];\r
6277     char *end = strchr(str, '\n');\r
6278     if (end == NULL) return;\r
6279     memcpy(buf, str, end - str);\r
6280     buf[end - str] = NULLCHAR;\r
6281     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6282     str = end + 1;\r
6283   }\r
6284 }\r
6285 \r
6286 void\r
6287 SetStartupDialogEnables(HWND hDlg)\r
6288 {\r
6289   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6290     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6291     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6292   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6293     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6294   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6295     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6296   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6297     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6298   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6299     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6300     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6301     IsDlgButtonChecked(hDlg, OPT_View));\r
6302 }\r
6303 \r
6304 char *\r
6305 QuoteForFilename(char *filename)\r
6306 {\r
6307   int dquote, space;\r
6308   dquote = strchr(filename, '"') != NULL;\r
6309   space = strchr(filename, ' ') != NULL;\r
6310   if (dquote || space) {\r
6311     if (dquote) {\r
6312       return "'";\r
6313     } else {\r
6314       return "\"";\r
6315     }\r
6316   } else {\r
6317     return "";\r
6318   }\r
6319 }\r
6320 \r
6321 VOID\r
6322 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6323 {\r
6324   char buf[MSG_SIZ];\r
6325   char *q;\r
6326 \r
6327   InitComboStringsFromOption(hwndCombo, nthnames);\r
6328   q = QuoteForFilename(nthcp);\r
6329     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6330   if (*nthdir != NULLCHAR) {\r
6331     q = QuoteForFilename(nthdir);\r
6332       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6333   }\r
6334   if (*nthcp == NULLCHAR) {\r
6335     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6336   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6337     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6338     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6339   }\r
6340 }\r
6341 \r
6342 LRESULT CALLBACK\r
6343 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6344 {\r
6345   char buf[MSG_SIZ];\r
6346   HANDLE hwndCombo;\r
6347   char *p;\r
6348 \r
6349   switch (message) {\r
6350   case WM_INITDIALOG:\r
6351     /* Center the dialog */\r
6352     CenterWindow (hDlg, GetDesktopWindow());\r
6353     Translate(hDlg, DLG_Startup);\r
6354     /* Initialize the dialog items */\r
6355     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6356                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6357                   firstChessProgramNames);\r
6358     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6359                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6360                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6361     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6362     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6363       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6364     if (*appData.icsHelper != NULLCHAR) {\r
6365       char *q = QuoteForFilename(appData.icsHelper);\r
6366       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6367     }\r
6368     if (*appData.icsHost == NULLCHAR) {\r
6369       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6370       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6371     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6372       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6373       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6374     }\r
6375 \r
6376     if (appData.icsActive) {\r
6377       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6378     }\r
6379     else if (appData.noChessProgram) {\r
6380       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6381     }\r
6382     else {\r
6383       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6384     }\r
6385 \r
6386     SetStartupDialogEnables(hDlg);\r
6387     return TRUE;\r
6388 \r
6389   case WM_COMMAND:\r
6390     switch (LOWORD(wParam)) {\r
6391     case IDOK:\r
6392       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6393         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6394         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6395         p = buf;\r
6396         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6397         ParseArgs(StringGet, &p);\r
6398         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6399         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6400         p = buf;\r
6401         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6402         ParseArgs(StringGet, &p);\r
6403         SwapEngines(singleList); // ... and then make it 'second'\r
6404 \r
6405         appData.noChessProgram = FALSE;\r
6406         appData.icsActive = FALSE;\r
6407       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6408         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6409         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6410         p = buf;\r
6411         ParseArgs(StringGet, &p);\r
6412         if (appData.zippyPlay) {\r
6413           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6414           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6415           p = buf;\r
6416           ParseArgs(StringGet, &p);\r
6417         }\r
6418       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6419         appData.noChessProgram = TRUE;\r
6420         appData.icsActive = FALSE;\r
6421       } else {\r
6422         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6423                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6424         return TRUE;\r
6425       }\r
6426       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6427         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6428         p = buf;\r
6429         ParseArgs(StringGet, &p);\r
6430       }\r
6431       EndDialog(hDlg, TRUE);\r
6432       return TRUE;\r
6433 \r
6434     case IDCANCEL:\r
6435       ExitEvent(0);\r
6436       return TRUE;\r
6437 \r
6438     case IDM_HELPCONTENTS:\r
6439       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6440         MessageBox (GetFocus(),\r
6441                     _("Unable to activate help"),\r
6442                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6443       }\r
6444       break;\r
6445 \r
6446     default:\r
6447       SetStartupDialogEnables(hDlg);\r
6448       break;\r
6449     }\r
6450     break;\r
6451   }\r
6452   return FALSE;\r
6453 }\r
6454 \r
6455 /*---------------------------------------------------------------------------*\\r
6456  *\r
6457  * About box dialog functions\r
6458  *\r
6459 \*---------------------------------------------------------------------------*/\r
6460 \r
6461 /* Process messages for "About" dialog box */\r
6462 LRESULT CALLBACK\r
6463 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6464 {\r
6465   switch (message) {\r
6466   case WM_INITDIALOG: /* message: initialize dialog box */\r
6467     /* Center the dialog over the application window */\r
6468     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6469     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6470     Translate(hDlg, ABOUTBOX);\r
6471     JAWS_COPYRIGHT\r
6472     return (TRUE);\r
6473 \r
6474   case WM_COMMAND: /* message: received a command */\r
6475     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6476         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6477       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6478       return (TRUE);\r
6479     }\r
6480     break;\r
6481   }\r
6482   return (FALSE);\r
6483 }\r
6484 \r
6485 /*---------------------------------------------------------------------------*\\r
6486  *\r
6487  * Comment Dialog functions\r
6488  *\r
6489 \*---------------------------------------------------------------------------*/\r
6490 \r
6491 LRESULT CALLBACK\r
6492 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6493 {\r
6494   static HANDLE hwndText = NULL;\r
6495   int len, newSizeX, newSizeY;\r
6496   static int sizeX, sizeY;\r
6497   char *str;\r
6498   RECT rect;\r
6499   MINMAXINFO *mmi;\r
6500 \r
6501   switch (message) {\r
6502   case WM_INITDIALOG: /* message: initialize dialog box */\r
6503     /* Initialize the dialog items */\r
6504     Translate(hDlg, DLG_EditComment);\r
6505     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6506     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6507     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6508     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6509     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6510     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6511     SetWindowText(hDlg, commentTitle);\r
6512     if (editComment) {\r
6513       SetFocus(hwndText);\r
6514     } else {\r
6515       SetFocus(GetDlgItem(hDlg, IDOK));\r
6516     }\r
6517     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6518                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6519                 MAKELPARAM(FALSE, 0));\r
6520     /* Size and position the dialog */\r
6521     if (!commentDialog) {\r
6522       commentDialog = hDlg;\r
6523       GetClientRect(hDlg, &rect);\r
6524       sizeX = rect.right;\r
6525       sizeY = rect.bottom;\r
6526       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6527           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6528         WINDOWPLACEMENT wp;\r
6529         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6530         wp.length = sizeof(WINDOWPLACEMENT);\r
6531         wp.flags = 0;\r
6532         wp.showCmd = SW_SHOW;\r
6533         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6534         wp.rcNormalPosition.left = wpComment.x;\r
6535         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6536         wp.rcNormalPosition.top = wpComment.y;\r
6537         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6538         SetWindowPlacement(hDlg, &wp);\r
6539 \r
6540         GetClientRect(hDlg, &rect);\r
6541         newSizeX = rect.right;\r
6542         newSizeY = rect.bottom;\r
6543         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6544                               newSizeX, newSizeY);\r
6545         sizeX = newSizeX;\r
6546         sizeY = newSizeY;\r
6547       }\r
6548     }\r
6549     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6550     return FALSE;\r
6551 \r
6552   case WM_COMMAND: /* message: received a command */\r
6553     switch (LOWORD(wParam)) {\r
6554     case IDOK:\r
6555       if (editComment) {\r
6556         char *p, *q;\r
6557         /* Read changed options from the dialog box */\r
6558         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6559         len = GetWindowTextLength(hwndText);\r
6560         str = (char *) malloc(len + 1);\r
6561         GetWindowText(hwndText, str, len + 1);\r
6562         p = q = str;\r
6563         while (*q) {\r
6564           if (*q == '\r')\r
6565             q++;\r
6566           else\r
6567             *p++ = *q++;\r
6568         }\r
6569         *p = NULLCHAR;\r
6570         ReplaceComment(commentIndex, str);\r
6571         free(str);\r
6572       }\r
6573       CommentPopDown();\r
6574       return TRUE;\r
6575 \r
6576     case IDCANCEL:\r
6577     case OPT_CancelComment:\r
6578       CommentPopDown();\r
6579       return TRUE;\r
6580 \r
6581     case OPT_ClearComment:\r
6582       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6583       break;\r
6584 \r
6585     case OPT_EditComment:\r
6586       EditCommentEvent();\r
6587       return TRUE;\r
6588 \r
6589     default:\r
6590       break;\r
6591     }\r
6592     break;\r
6593 \r
6594   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6595         if( wParam == OPT_CommentText ) {\r
6596             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6597 \r
6598             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6599                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6600                 POINTL pt;\r
6601                 LRESULT index;\r
6602 \r
6603                 pt.x = LOWORD( lpMF->lParam );\r
6604                 pt.y = HIWORD( lpMF->lParam );\r
6605 \r
6606                 if(lpMF->msg == WM_CHAR) {\r
6607                         CHARRANGE sel;\r
6608                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6609                         index = sel.cpMin;\r
6610                 } else\r
6611                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6612 \r
6613                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6614                 len = GetWindowTextLength(hwndText);\r
6615                 str = (char *) malloc(len + 1);\r
6616                 GetWindowText(hwndText, str, len + 1);\r
6617                 ReplaceComment(commentIndex, str);\r
6618                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6619                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6620                 free(str);\r
6621 \r
6622                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6623                 lpMF->msg = WM_USER;\r
6624 \r
6625                 return TRUE;\r
6626             }\r
6627         }\r
6628         break;\r
6629 \r
6630   case WM_SIZE:\r
6631     newSizeX = LOWORD(lParam);\r
6632     newSizeY = HIWORD(lParam);\r
6633     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6634     sizeX = newSizeX;\r
6635     sizeY = newSizeY;\r
6636     break;\r
6637 \r
6638   case WM_GETMINMAXINFO:\r
6639     /* Prevent resizing window too small */\r
6640     mmi = (MINMAXINFO *) lParam;\r
6641     mmi->ptMinTrackSize.x = 100;\r
6642     mmi->ptMinTrackSize.y = 100;\r
6643     break;\r
6644   }\r
6645   return FALSE;\r
6646 }\r
6647 \r
6648 VOID\r
6649 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6650 {\r
6651   FARPROC lpProc;\r
6652   char *p, *q;\r
6653 \r
6654   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6655 \r
6656   if (str == NULL) str = "";\r
6657   p = (char *) malloc(2 * strlen(str) + 2);\r
6658   q = p;\r
6659   while (*str) {\r
6660     if (*str == '\n') *q++ = '\r';\r
6661     *q++ = *str++;\r
6662   }\r
6663   *q = NULLCHAR;\r
6664   if (commentText != NULL) free(commentText);\r
6665 \r
6666   commentIndex = index;\r
6667   commentTitle = title;\r
6668   commentText = p;\r
6669   editComment = edit;\r
6670 \r
6671   if (commentDialog) {\r
6672     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6673     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6674   } else {\r
6675     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6676     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6677                  hwndMain, (DLGPROC)lpProc);\r
6678     FreeProcInstance(lpProc);\r
6679   }\r
6680   commentUp = TRUE;\r
6681 }\r
6682 \r
6683 \r
6684 /*---------------------------------------------------------------------------*\\r
6685  *\r
6686  * Type-in move dialog functions\r
6687  * \r
6688 \*---------------------------------------------------------------------------*/\r
6689 \r
6690 LRESULT CALLBACK\r
6691 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6692 {\r
6693   char move[MSG_SIZ];\r
6694   HWND hInput;\r
6695 \r
6696   switch (message) {\r
6697   case WM_INITDIALOG:\r
6698     move[0] = (char) lParam;\r
6699     move[1] = NULLCHAR;\r
6700     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6701     Translate(hDlg, DLG_TypeInMove);\r
6702     hInput = GetDlgItem(hDlg, OPT_Move);\r
6703     SetWindowText(hInput, move);\r
6704     SetFocus(hInput);\r
6705     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6706     return FALSE;\r
6707 \r
6708   case WM_COMMAND:\r
6709     switch (LOWORD(wParam)) {\r
6710     case IDOK:\r
6711 \r
6712       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6713       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6714       TypeInDoneEvent(move);\r
6715       EndDialog(hDlg, TRUE);\r
6716       return TRUE;\r
6717     case IDCANCEL:\r
6718       EndDialog(hDlg, FALSE);\r
6719       return TRUE;\r
6720     default:\r
6721       break;\r
6722     }\r
6723     break;\r
6724   }\r
6725   return FALSE;\r
6726 }\r
6727 \r
6728 VOID\r
6729 PopUpMoveDialog(char firstchar)\r
6730 {\r
6731     FARPROC lpProc;\r
6732 \r
6733       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6734       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6735         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6736       FreeProcInstance(lpProc);\r
6737 }\r
6738 \r
6739 /*---------------------------------------------------------------------------*\\r
6740  *\r
6741  * Type-in name dialog functions\r
6742  * \r
6743 \*---------------------------------------------------------------------------*/\r
6744 \r
6745 LRESULT CALLBACK\r
6746 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6747 {\r
6748   char move[MSG_SIZ];\r
6749   HWND hInput;\r
6750 \r
6751   switch (message) {\r
6752   case WM_INITDIALOG:\r
6753     move[0] = (char) lParam;\r
6754     move[1] = NULLCHAR;\r
6755     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6756     Translate(hDlg, DLG_TypeInName);\r
6757     hInput = GetDlgItem(hDlg, OPT_Name);\r
6758     SetWindowText(hInput, move);\r
6759     SetFocus(hInput);\r
6760     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6761     return FALSE;\r
6762 \r
6763   case WM_COMMAND:\r
6764     switch (LOWORD(wParam)) {\r
6765     case IDOK:\r
6766       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6767       appData.userName = strdup(move);\r
6768       SetUserLogo(); DisplayLogos();\r
6769       SetGameInfo();\r
6770       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6771         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6772         DisplayTitle(move);\r
6773       }\r
6774 \r
6775 \r
6776       EndDialog(hDlg, TRUE);\r
6777       return TRUE;\r
6778     case IDCANCEL:\r
6779       EndDialog(hDlg, FALSE);\r
6780       return TRUE;\r
6781     default:\r
6782       break;\r
6783     }\r
6784     break;\r
6785   }\r
6786   return FALSE;\r
6787 }\r
6788 \r
6789 VOID\r
6790 PopUpNameDialog(char firstchar)\r
6791 {\r
6792     FARPROC lpProc;\r
6793     \r
6794       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6795       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6796         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6797       FreeProcInstance(lpProc);\r
6798 }\r
6799 \r
6800 /*---------------------------------------------------------------------------*\\r
6801  *\r
6802  *  Error dialogs\r
6803  * \r
6804 \*---------------------------------------------------------------------------*/\r
6805 \r
6806 /* Nonmodal error box */\r
6807 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6808                              WPARAM wParam, LPARAM lParam);\r
6809 \r
6810 VOID\r
6811 ErrorPopUp(char *title, char *content)\r
6812 {\r
6813   FARPROC lpProc;\r
6814   char *p, *q;\r
6815   BOOLEAN modal = hwndMain == NULL;\r
6816 \r
6817   p = content;\r
6818   q = errorMessage;\r
6819   while (*p) {\r
6820     if (*p == '\n') {\r
6821       if (modal) {\r
6822         *q++ = ' ';\r
6823         p++;\r
6824       } else {\r
6825         *q++ = '\r';\r
6826         *q++ = *p++;\r
6827       }\r
6828     } else {\r
6829       *q++ = *p++;\r
6830     }\r
6831   }\r
6832   *q = NULLCHAR;\r
6833   strncpy(errorTitle, title, sizeof(errorTitle));\r
6834   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6835   \r
6836   if (modal) {\r
6837     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6838   } else {\r
6839     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6840     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6841                  hwndMain, (DLGPROC)lpProc);\r
6842     FreeProcInstance(lpProc);\r
6843   }\r
6844 }\r
6845 \r
6846 VOID\r
6847 ErrorPopDown()\r
6848 {\r
6849   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6850   if (errorDialog == NULL) return;\r
6851   DestroyWindow(errorDialog);\r
6852   errorDialog = NULL;\r
6853   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6854 }\r
6855 \r
6856 LRESULT CALLBACK\r
6857 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6858 {\r
6859   RECT rChild;\r
6860 \r
6861   switch (message) {\r
6862   case WM_INITDIALOG:\r
6863     GetWindowRect(hDlg, &rChild);\r
6864 \r
6865     /*\r
6866     SetWindowPos(hDlg, NULL, rChild.left,\r
6867       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6868       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6869     */\r
6870 \r
6871     /* \r
6872         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6873         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6874         and it doesn't work when you resize the dialog.\r
6875         For now, just give it a default position.\r
6876     */\r
6877     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6878     Translate(hDlg, DLG_Error);\r
6879 \r
6880     errorDialog = hDlg;\r
6881     SetWindowText(hDlg, errorTitle);\r
6882     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6883     return FALSE;\r
6884 \r
6885   case WM_COMMAND:\r
6886     switch (LOWORD(wParam)) {\r
6887     case IDOK:\r
6888     case IDCANCEL:\r
6889       if (errorDialog == hDlg) errorDialog = NULL;\r
6890       DestroyWindow(hDlg);\r
6891       return TRUE;\r
6892 \r
6893     default:\r
6894       break;\r
6895     }\r
6896     break;\r
6897   }\r
6898   return FALSE;\r
6899 }\r
6900 \r
6901 #ifdef GOTHIC\r
6902 HWND gothicDialog = NULL;\r
6903 \r
6904 LRESULT CALLBACK\r
6905 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6906 {\r
6907   RECT rChild;\r
6908   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6909 \r
6910   switch (message) {\r
6911   case WM_INITDIALOG:\r
6912     GetWindowRect(hDlg, &rChild);\r
6913 \r
6914     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6915                                                              SWP_NOZORDER);\r
6916 \r
6917     /* \r
6918         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6919         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6920         and it doesn't work when you resize the dialog.\r
6921         For now, just give it a default position.\r
6922     */\r
6923     gothicDialog = hDlg;\r
6924     SetWindowText(hDlg, errorTitle);\r
6925     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6926     return FALSE;\r
6927 \r
6928   case WM_COMMAND:\r
6929     switch (LOWORD(wParam)) {\r
6930     case IDOK:\r
6931     case IDCANCEL:\r
6932       if (errorDialog == hDlg) errorDialog = NULL;\r
6933       DestroyWindow(hDlg);\r
6934       return TRUE;\r
6935 \r
6936     default:\r
6937       break;\r
6938     }\r
6939     break;\r
6940   }\r
6941   return FALSE;\r
6942 }\r
6943 \r
6944 VOID\r
6945 GothicPopUp(char *title, VariantClass variant)\r
6946 {\r
6947   FARPROC lpProc;\r
6948   static char *lastTitle;\r
6949 \r
6950   strncpy(errorTitle, title, sizeof(errorTitle));\r
6951   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6952 \r
6953   if(lastTitle != title && gothicDialog != NULL) {\r
6954     DestroyWindow(gothicDialog);\r
6955     gothicDialog = NULL;\r
6956   }\r
6957   if(variant != VariantNormal && gothicDialog == NULL) {\r
6958     title = lastTitle;\r
6959     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6960     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6961                  hwndMain, (DLGPROC)lpProc);\r
6962     FreeProcInstance(lpProc);\r
6963   }\r
6964 }\r
6965 #endif\r
6966 \r
6967 /*---------------------------------------------------------------------------*\\r
6968  *\r
6969  *  Ics Interaction console functions\r
6970  *\r
6971 \*---------------------------------------------------------------------------*/\r
6972 \r
6973 #define HISTORY_SIZE 64\r
6974 static char *history[HISTORY_SIZE];\r
6975 int histIn = 0, histP = 0;\r
6976 \r
6977 \r
6978 VOID\r
6979 SaveInHistory(char *cmd)\r
6980 {\r
6981   if (history[histIn] != NULL) {\r
6982     free(history[histIn]);\r
6983     history[histIn] = NULL;\r
6984   }\r
6985   if (*cmd == NULLCHAR) return;\r
6986   history[histIn] = StrSave(cmd);\r
6987   histIn = (histIn + 1) % HISTORY_SIZE;\r
6988   if (history[histIn] != NULL) {\r
6989     free(history[histIn]);\r
6990 \r
6991     history[histIn] = NULL;\r
6992   }\r
6993   histP = histIn;\r
6994 }\r
6995 \r
6996 char *\r
6997 PrevInHistory(char *cmd)\r
6998 {\r
6999   int newhp;\r
7000   if (histP == histIn) {\r
7001     if (history[histIn] != NULL) free(history[histIn]);\r
7002     history[histIn] = StrSave(cmd);\r
7003   }\r
7004   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7005   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7006   histP = newhp;\r
7007   return history[histP];\r
7008 }\r
7009 \r
7010 char *\r
7011 NextInHistory()\r
7012 {\r
7013   if (histP == histIn) return NULL;\r
7014   histP = (histP + 1) % HISTORY_SIZE;\r
7015   return history[histP];   \r
7016 }\r
7017 \r
7018 HMENU\r
7019 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7020 {\r
7021   HMENU hmenu, h;\r
7022   int i = 0;\r
7023   hmenu = LoadMenu(hInst, "TextMenu");\r
7024   h = GetSubMenu(hmenu, 0);\r
7025   while (e->item) {\r
7026     if (strcmp(e->item, "-") == 0) {\r
7027       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7028     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
7029       int flags = MF_STRING, j = 0;\r
7030       if (e->item[0] == '|') {\r
7031         flags |= MF_MENUBARBREAK;\r
7032         j++;\r
7033       }\r
7034       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
7035       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
7036     }\r
7037     e++;\r
7038     i++;\r
7039   } \r
7040   return hmenu;\r
7041 }\r
7042 \r
7043 WNDPROC consoleTextWindowProc;\r
7044 \r
7045 void\r
7046 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7047 {\r
7048   char buf[MSG_SIZ], name[MSG_SIZ];\r
7049   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7050   CHARRANGE sel;\r
7051 \r
7052   if (!getname) {\r
7053     SetWindowText(hInput, command);\r
7054     if (immediate) {\r
7055       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7056     } else {\r
7057       sel.cpMin = 999999;\r
7058       sel.cpMax = 999999;\r
7059       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7060       SetFocus(hInput);\r
7061     }\r
7062     return;\r
7063   }    \r
7064   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7065   if (sel.cpMin == sel.cpMax) {\r
7066     /* Expand to surrounding word */\r
7067     TEXTRANGE tr;\r
7068     do {\r
7069       tr.chrg.cpMax = sel.cpMin;\r
7070       tr.chrg.cpMin = --sel.cpMin;\r
7071       if (sel.cpMin < 0) break;\r
7072       tr.lpstrText = name;\r
7073       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7074     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7075     sel.cpMin++;\r
7076 \r
7077     do {\r
7078       tr.chrg.cpMin = sel.cpMax;\r
7079       tr.chrg.cpMax = ++sel.cpMax;\r
7080       tr.lpstrText = name;\r
7081       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7082     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7083     sel.cpMax--;\r
7084 \r
7085     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7086       MessageBeep(MB_ICONEXCLAMATION);\r
7087       return;\r
7088     }\r
7089     tr.chrg = sel;\r
7090     tr.lpstrText = name;\r
7091     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7092   } else {\r
7093     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7094       MessageBeep(MB_ICONEXCLAMATION);\r
7095       return;\r
7096     }\r
7097     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7098   }\r
7099   if (immediate) {\r
7100     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7101     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7102     SetWindowText(hInput, buf);\r
7103     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7104   } else {\r
7105     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7106       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7107     SetWindowText(hInput, buf);\r
7108     sel.cpMin = 999999;\r
7109     sel.cpMax = 999999;\r
7110     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7111     SetFocus(hInput);\r
7112   }\r
7113 }\r
7114 \r
7115 LRESULT CALLBACK \r
7116 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7117 {\r
7118   HWND hInput;\r
7119   CHARRANGE sel;\r
7120 \r
7121   switch (message) {\r
7122   case WM_KEYDOWN:\r
7123     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7124     if(wParam=='R') return 0;\r
7125     switch (wParam) {\r
7126     case VK_PRIOR:\r
7127       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7128       return 0;\r
7129     case VK_NEXT:\r
7130       sel.cpMin = 999999;\r
7131       sel.cpMax = 999999;\r
7132       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7133       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7134       return 0;\r
7135     }\r
7136     break;\r
7137   case WM_CHAR:\r
7138    if(wParam != '\022') {\r
7139     if (wParam == '\t') {\r
7140       if (GetKeyState(VK_SHIFT) < 0) {\r
7141         /* shifted */\r
7142         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7143         if (buttonDesc[0].hwnd) {\r
7144           SetFocus(buttonDesc[0].hwnd);\r
7145         } else {\r
7146           SetFocus(hwndMain);\r
7147         }\r
7148       } else {\r
7149         /* unshifted */\r
7150         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7151       }\r
7152     } else {\r
7153       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7154       JAWS_DELETE( SetFocus(hInput); )\r
7155       SendMessage(hInput, message, wParam, lParam);\r
7156     }\r
7157     return 0;\r
7158    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7159    lParam = -1;\r
7160   case WM_RBUTTONDOWN:\r
7161     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7162       /* Move selection here if it was empty */\r
7163       POINT pt;\r
7164       pt.x = LOWORD(lParam);\r
7165       pt.y = HIWORD(lParam);\r
7166       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7167       if (sel.cpMin == sel.cpMax) {\r
7168         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7169         sel.cpMax = sel.cpMin;\r
7170         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7171       }\r
7172       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7173 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7174       POINT pt;\r
7175       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7176       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7177       if (sel.cpMin == sel.cpMax) {\r
7178         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7179         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7180       }\r
7181       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7182         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7183       }\r
7184       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7185       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7186       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7187       MenuPopup(hwnd, pt, hmenu, -1);\r
7188 }\r
7189     }\r
7190     return 0;\r
7191   case WM_RBUTTONUP:\r
7192     if (GetKeyState(VK_SHIFT) & ~1) {\r
7193       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7194         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7195     }\r
7196     return 0;\r
7197   case WM_PASTE:\r
7198     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7199     SetFocus(hInput);\r
7200     return SendMessage(hInput, message, wParam, lParam);\r
7201   case WM_MBUTTONDOWN:\r
7202     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7203   case WM_COMMAND:\r
7204     switch (LOWORD(wParam)) {\r
7205     case IDM_QuickPaste:\r
7206       {\r
7207         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7208         if (sel.cpMin == sel.cpMax) {\r
7209           MessageBeep(MB_ICONEXCLAMATION);\r
7210           return 0;\r
7211         }\r
7212         SendMessage(hwnd, WM_COPY, 0, 0);\r
7213         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7214         SendMessage(hInput, WM_PASTE, 0, 0);\r
7215         SetFocus(hInput);\r
7216         return 0;\r
7217       }\r
7218     case IDM_Cut:\r
7219       SendMessage(hwnd, WM_CUT, 0, 0);\r
7220       return 0;\r
7221     case IDM_Paste:\r
7222       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7223       return 0;\r
7224     case IDM_Copy:\r
7225       SendMessage(hwnd, WM_COPY, 0, 0);\r
7226       return 0;\r
7227     default:\r
7228       {\r
7229         int i = LOWORD(wParam) - IDM_CommandX;\r
7230         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7231             icsTextMenuEntry[i].command != NULL) {\r
7232           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7233                    icsTextMenuEntry[i].getname,\r
7234                    icsTextMenuEntry[i].immediate);\r
7235           return 0;\r
7236         }\r
7237       }\r
7238       break;\r
7239     }\r
7240     break;\r
7241   }\r
7242   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7243 }\r
7244 \r
7245 WNDPROC consoleInputWindowProc;\r
7246 \r
7247 LRESULT CALLBACK\r
7248 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7249 {\r
7250   char buf[MSG_SIZ];\r
7251   char *p;\r
7252   static BOOL sendNextChar = FALSE;\r
7253   static BOOL quoteNextChar = FALSE;\r
7254   InputSource *is = consoleInputSource;\r
7255   CHARFORMAT cf;\r
7256   CHARRANGE sel;\r
7257 \r
7258   switch (message) {\r
7259   case WM_CHAR:\r
7260     if (!appData.localLineEditing || sendNextChar) {\r
7261       is->buf[0] = (CHAR) wParam;\r
7262       is->count = 1;\r
7263       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7264       sendNextChar = FALSE;\r
7265       return 0;\r
7266     }\r
7267     if (quoteNextChar) {\r
7268       buf[0] = (char) wParam;\r
7269       buf[1] = NULLCHAR;\r
7270       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7271       quoteNextChar = FALSE;\r
7272       return 0;\r
7273     }\r
7274     switch (wParam) {\r
7275     case '\r':   /* Enter key */\r
7276       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7277       if (consoleEcho) SaveInHistory(is->buf);\r
7278       is->buf[is->count++] = '\n';\r
7279       is->buf[is->count] = NULLCHAR;\r
7280       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7281       if (consoleEcho) {\r
7282         ConsoleOutput(is->buf, is->count, TRUE);\r
7283       } else if (appData.localLineEditing) {\r
7284         ConsoleOutput("\n", 1, TRUE);\r
7285       }\r
7286       /* fall thru */\r
7287     case '\033': /* Escape key */\r
7288       SetWindowText(hwnd, "");\r
7289       cf.cbSize = sizeof(CHARFORMAT);\r
7290       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7291       if (consoleEcho) {\r
7292         cf.crTextColor = textAttribs[ColorNormal].color;\r
7293       } else {\r
7294         cf.crTextColor = COLOR_ECHOOFF;\r
7295       }\r
7296       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7297       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7298       return 0;\r
7299     case '\t':   /* Tab key */\r
7300       if (GetKeyState(VK_SHIFT) < 0) {\r
7301         /* shifted */\r
7302         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7303       } else {\r
7304         /* unshifted */\r
7305         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7306         if (buttonDesc[0].hwnd) {\r
7307           SetFocus(buttonDesc[0].hwnd);\r
7308         } else {\r
7309           SetFocus(hwndMain);\r
7310         }\r
7311       }\r
7312       return 0;\r
7313     case '\023': /* Ctrl+S */\r
7314       sendNextChar = TRUE;\r
7315       return 0;\r
7316     case '\021': /* Ctrl+Q */\r
7317       quoteNextChar = TRUE;\r
7318       return 0;\r
7319     JAWS_REPLAY\r
7320     default:\r
7321       break;\r
7322     }\r
7323     break;\r
7324   case WM_KEYDOWN:\r
7325     switch (wParam) {\r
7326     case VK_UP:\r
7327       GetWindowText(hwnd, buf, MSG_SIZ);\r
7328       p = PrevInHistory(buf);\r
7329       if (p != NULL) {\r
7330         SetWindowText(hwnd, p);\r
7331         sel.cpMin = 999999;\r
7332         sel.cpMax = 999999;\r
7333         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7334         return 0;\r
7335       }\r
7336       break;\r
7337     case VK_DOWN:\r
7338       p = NextInHistory();\r
7339       if (p != NULL) {\r
7340         SetWindowText(hwnd, p);\r
7341         sel.cpMin = 999999;\r
7342         sel.cpMax = 999999;\r
7343         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7344         return 0;\r
7345       }\r
7346       break;\r
7347     case VK_HOME:\r
7348     case VK_END:\r
7349       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7350       /* fall thru */\r
7351     case VK_PRIOR:\r
7352     case VK_NEXT:\r
7353       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7354       return 0;\r
7355     }\r
7356     break;\r
7357   case WM_MBUTTONDOWN:\r
7358     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7359       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7360     break;\r
7361   case WM_RBUTTONUP:\r
7362     if (GetKeyState(VK_SHIFT) & ~1) {\r
7363       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7364         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7365     } else {\r
7366       POINT pt;\r
7367       HMENU hmenu;\r
7368       hmenu = LoadMenu(hInst, "InputMenu");\r
7369       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7370       if (sel.cpMin == sel.cpMax) {\r
7371         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7372         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7373       }\r
7374       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7375         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7376       }\r
7377       pt.x = LOWORD(lParam);\r
7378       pt.y = HIWORD(lParam);\r
7379       MenuPopup(hwnd, pt, hmenu, -1);\r
7380     }\r
7381     return 0;\r
7382   case WM_COMMAND:\r
7383     switch (LOWORD(wParam)) { \r
7384     case IDM_Undo:\r
7385       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7386       return 0;\r
7387     case IDM_SelectAll:\r
7388       sel.cpMin = 0;\r
7389       sel.cpMax = -1; /*999999?*/\r
7390       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7391       return 0;\r
7392     case IDM_Cut:\r
7393       SendMessage(hwnd, WM_CUT, 0, 0);\r
7394       return 0;\r
7395     case IDM_Paste:\r
7396       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7397       return 0;\r
7398     case IDM_Copy:\r
7399       SendMessage(hwnd, WM_COPY, 0, 0);\r
7400       return 0;\r
7401     }\r
7402     break;\r
7403   }\r
7404   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7405 }\r
7406 \r
7407 #define CO_MAX  100000\r
7408 #define CO_TRIM   1000\r
7409 \r
7410 LRESULT CALLBACK\r
7411 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7412 {\r
7413   static SnapData sd;\r
7414   HWND hText, hInput;\r
7415   RECT rect;\r
7416   static int sizeX, sizeY;\r
7417   int newSizeX, newSizeY;\r
7418   MINMAXINFO *mmi;\r
7419   WORD wMask;\r
7420 \r
7421   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7422   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7423 \r
7424   switch (message) {\r
7425   case WM_NOTIFY:\r
7426     if (((NMHDR*)lParam)->code == EN_LINK)\r
7427     {\r
7428       ENLINK *pLink = (ENLINK*)lParam;\r
7429       if (pLink->msg == WM_LBUTTONUP)\r
7430       {\r
7431         TEXTRANGE tr;\r
7432 \r
7433         tr.chrg = pLink->chrg;\r
7434         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7435         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7436         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7437         free(tr.lpstrText);\r
7438       }\r
7439     }\r
7440     break;\r
7441   case WM_INITDIALOG: /* message: initialize dialog box */\r
7442     hwndConsole = hDlg;\r
7443     SetFocus(hInput);\r
7444     consoleTextWindowProc = (WNDPROC)\r
7445       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7446     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7447     consoleInputWindowProc = (WNDPROC)\r
7448       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7449     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7450     Colorize(ColorNormal, TRUE);\r
7451     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7452     ChangedConsoleFont();\r
7453     GetClientRect(hDlg, &rect);\r
7454     sizeX = rect.right;\r
7455     sizeY = rect.bottom;\r
7456     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7457         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7458       WINDOWPLACEMENT wp;\r
7459       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7460       wp.length = sizeof(WINDOWPLACEMENT);\r
7461       wp.flags = 0;\r
7462       wp.showCmd = SW_SHOW;\r
7463       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7464       wp.rcNormalPosition.left = wpConsole.x;\r
7465       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7466       wp.rcNormalPosition.top = wpConsole.y;\r
7467       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7468       SetWindowPlacement(hDlg, &wp);\r
7469     }\r
7470 \r
7471    // [HGM] Chessknight's change 2004-07-13\r
7472    else { /* Determine Defaults */\r
7473        WINDOWPLACEMENT wp;\r
7474        wpConsole.x = wpMain.width + 1;\r
7475        wpConsole.y = wpMain.y;\r
7476        wpConsole.width = screenWidth -  wpMain.width;\r
7477        wpConsole.height = wpMain.height;\r
7478        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7479        wp.length = sizeof(WINDOWPLACEMENT);\r
7480        wp.flags = 0;\r
7481        wp.showCmd = SW_SHOW;\r
7482        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7483        wp.rcNormalPosition.left = wpConsole.x;\r
7484        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7485        wp.rcNormalPosition.top = wpConsole.y;\r
7486        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7487        SetWindowPlacement(hDlg, &wp);\r
7488     }\r
7489 \r
7490    // Allow hText to highlight URLs and send notifications on them\r
7491    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7492    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7493    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7494    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7495 \r
7496     return FALSE;\r
7497 \r
7498   case WM_SETFOCUS:\r
7499     SetFocus(hInput);\r
7500     return 0;\r
7501 \r
7502   case WM_CLOSE:\r
7503     ExitEvent(0);\r
7504     /* not reached */\r
7505     break;\r
7506 \r
7507   case WM_SIZE:\r
7508     if (IsIconic(hDlg)) break;\r
7509     newSizeX = LOWORD(lParam);\r
7510     newSizeY = HIWORD(lParam);\r
7511     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7512       RECT rectText, rectInput;\r
7513       POINT pt;\r
7514       int newTextHeight, newTextWidth;\r
7515       GetWindowRect(hText, &rectText);\r
7516       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7517       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7518       if (newTextHeight < 0) {\r
7519         newSizeY += -newTextHeight;\r
7520         newTextHeight = 0;\r
7521       }\r
7522       SetWindowPos(hText, NULL, 0, 0,\r
7523         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7524       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7525       pt.x = rectInput.left;\r
7526       pt.y = rectInput.top + newSizeY - sizeY;\r
7527       ScreenToClient(hDlg, &pt);\r
7528       SetWindowPos(hInput, NULL, \r
7529         pt.x, pt.y, /* needs client coords */   \r
7530         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7531         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7532     }\r
7533     sizeX = newSizeX;\r
7534     sizeY = newSizeY;\r
7535     break;\r
7536 \r
7537   case WM_GETMINMAXINFO:\r
7538     /* Prevent resizing window too small */\r
7539     mmi = (MINMAXINFO *) lParam;\r
7540     mmi->ptMinTrackSize.x = 100;\r
7541     mmi->ptMinTrackSize.y = 100;\r
7542     break;\r
7543 \r
7544   /* [AS] Snapping */\r
7545   case WM_ENTERSIZEMOVE:\r
7546     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7547 \r
7548   case WM_SIZING:\r
7549     return OnSizing( &sd, hDlg, wParam, lParam );\r
7550 \r
7551   case WM_MOVING:\r
7552     return OnMoving( &sd, hDlg, wParam, lParam );\r
7553 \r
7554   case WM_EXITSIZEMOVE:\r
7555         UpdateICSWidth(hText);\r
7556     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7557   }\r
7558 \r
7559   return DefWindowProc(hDlg, message, wParam, lParam);\r
7560 }\r
7561 \r
7562 \r
7563 VOID\r
7564 ConsoleCreate()\r
7565 {\r
7566   HWND hCons;\r
7567   if (hwndConsole) return;\r
7568   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7569   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7570 }\r
7571 \r
7572 \r
7573 VOID\r
7574 ConsoleOutput(char* data, int length, int forceVisible)\r
7575 {\r
7576   HWND hText;\r
7577   int trim, exlen;\r
7578   char *p, *q;\r
7579   char buf[CO_MAX+1];\r
7580   POINT pEnd;\r
7581   RECT rect;\r
7582   static int delayLF = 0;\r
7583   CHARRANGE savesel, sel;\r
7584 \r
7585   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7586   p = data;\r
7587   q = buf;\r
7588   if (delayLF) {\r
7589     *q++ = '\r';\r
7590     *q++ = '\n';\r
7591     delayLF = 0;\r
7592   }\r
7593   while (length--) {\r
7594     if (*p == '\n') {\r
7595       if (*++p) {\r
7596         *q++ = '\r';\r
7597         *q++ = '\n';\r
7598       } else {\r
7599         delayLF = 1;\r
7600       }\r
7601     } else if (*p == '\007') {\r
7602        MyPlaySound(&sounds[(int)SoundBell]);\r
7603        p++;\r
7604     } else {\r
7605       *q++ = *p++;\r
7606     }\r
7607   }\r
7608   *q = NULLCHAR;\r
7609   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7610   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7611   /* Save current selection */\r
7612   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7613   exlen = GetWindowTextLength(hText);\r
7614   /* Find out whether current end of text is visible */\r
7615   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7616   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7617   /* Trim existing text if it's too long */\r
7618   if (exlen + (q - buf) > CO_MAX) {\r
7619     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7620     sel.cpMin = 0;\r
7621     sel.cpMax = trim;\r
7622     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7623     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7624     exlen -= trim;\r
7625     savesel.cpMin -= trim;\r
7626     savesel.cpMax -= trim;\r
7627     if (exlen < 0) exlen = 0;\r
7628     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7629     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7630   }\r
7631   /* Append the new text */\r
7632   sel.cpMin = exlen;\r
7633   sel.cpMax = exlen;\r
7634   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7635   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7636   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7637   if (forceVisible || exlen == 0 ||\r
7638       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7639        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7640     /* Scroll to make new end of text visible if old end of text\r
7641        was visible or new text is an echo of user typein */\r
7642     sel.cpMin = 9999999;\r
7643     sel.cpMax = 9999999;\r
7644     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7645     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7646     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7647     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7648   }\r
7649   if (savesel.cpMax == exlen || forceVisible) {\r
7650     /* Move insert point to new end of text if it was at the old\r
7651        end of text or if the new text is an echo of user typein */\r
7652     sel.cpMin = 9999999;\r
7653     sel.cpMax = 9999999;\r
7654     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7655   } else {\r
7656     /* Restore previous selection */\r
7657     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7658   }\r
7659   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7660 }\r
7661 \r
7662 /*---------*/\r
7663 \r
7664 \r
7665 void\r
7666 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7667 {\r
7668   char buf[100];\r
7669   char *str;\r
7670   COLORREF oldFg, oldBg;\r
7671   HFONT oldFont;\r
7672   RECT rect;\r
7673 \r
7674   if(copyNumber > 1)\r
7675     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7676 \r
7677   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7678   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7679   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7680 \r
7681   rect.left = x;\r
7682   rect.right = x + squareSize;\r
7683   rect.top  = y;\r
7684   rect.bottom = y + squareSize;\r
7685   str = buf;\r
7686 \r
7687   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7688                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7689              y, ETO_CLIPPED|ETO_OPAQUE,\r
7690              &rect, str, strlen(str), NULL);\r
7691 \r
7692   (void) SetTextColor(hdc, oldFg);\r
7693   (void) SetBkColor(hdc, oldBg);\r
7694   (void) SelectObject(hdc, oldFont);\r
7695 }\r
7696 \r
7697 void\r
7698 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7699               RECT *rect, char *color, char *flagFell)\r
7700 {\r
7701   char buf[100];\r
7702   char *str;\r
7703   COLORREF oldFg, oldBg;\r
7704   HFONT oldFont;\r
7705 \r
7706   if (twoBoards && partnerUp) return;\r
7707   if (appData.clockMode) {\r
7708     if (tinyLayout == 2)\r
7709       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7710     else\r
7711       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7712     str = buf;\r
7713   } else {\r
7714     str = color;\r
7715   }\r
7716 \r
7717   if (highlight) {\r
7718     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7719     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7720   } else {\r
7721     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7722     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7723   }\r
7724 \r
7725   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7726 \r
7727   JAWS_SILENCE\r
7728 \r
7729   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7730              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7731              rect, str, strlen(str), NULL);\r
7732   if(logoHeight > 0 && appData.clockMode) {\r
7733       RECT r;\r
7734       str += strlen(color)+2;\r
7735       r.top = rect->top + logoHeight/2;\r
7736       r.left = rect->left;\r
7737       r.right = rect->right;\r
7738       r.bottom = rect->bottom;\r
7739       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7740                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7741                  &r, str, strlen(str), NULL);\r
7742   }\r
7743   (void) SetTextColor(hdc, oldFg);\r
7744   (void) SetBkColor(hdc, oldBg);\r
7745   (void) SelectObject(hdc, oldFont);\r
7746 }\r
7747 \r
7748 \r
7749 int\r
7750 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7751            OVERLAPPED *ovl)\r
7752 {\r
7753   int ok, err;\r
7754 \r
7755   /* [AS]  */\r
7756   if( count <= 0 ) {\r
7757     if (appData.debugMode) {\r
7758       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7759     }\r
7760 \r
7761     return ERROR_INVALID_USER_BUFFER;\r
7762   }\r
7763 \r
7764   ResetEvent(ovl->hEvent);\r
7765   ovl->Offset = ovl->OffsetHigh = 0;\r
7766   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7767   if (ok) {\r
7768     err = NO_ERROR;\r
7769   } else {\r
7770     err = GetLastError();\r
7771     if (err == ERROR_IO_PENDING) {\r
7772       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7773       if (ok)\r
7774         err = NO_ERROR;\r
7775       else\r
7776         err = GetLastError();\r
7777     }\r
7778   }\r
7779   return err;\r
7780 }\r
7781 \r
7782 int\r
7783 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7784             OVERLAPPED *ovl)\r
7785 {\r
7786   int ok, err;\r
7787 \r
7788   ResetEvent(ovl->hEvent);\r
7789   ovl->Offset = ovl->OffsetHigh = 0;\r
7790   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7791   if (ok) {\r
7792     err = NO_ERROR;\r
7793   } else {\r
7794     err = GetLastError();\r
7795     if (err == ERROR_IO_PENDING) {\r
7796       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7797       if (ok)\r
7798         err = NO_ERROR;\r
7799       else\r
7800         err = GetLastError();\r
7801     }\r
7802 \r
7803   }\r
7804   return err;\r
7805 }\r
7806 \r
7807 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7808 void CheckForInputBufferFull( InputSource * is )\r
7809 {\r
7810     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7811         /* Look for end of line */\r
7812         char * p = is->buf;\r
7813         \r
7814         while( p < is->next && *p != '\n' ) {\r
7815             p++;\r
7816         }\r
7817 \r
7818         if( p >= is->next ) {\r
7819             if (appData.debugMode) {\r
7820                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7821             }\r
7822 \r
7823             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7824             is->count = (DWORD) -1;\r
7825             is->next = is->buf;\r
7826         }\r
7827     }\r
7828 }\r
7829 \r
7830 DWORD\r
7831 InputThread(LPVOID arg)\r
7832 {\r
7833   InputSource *is;\r
7834   OVERLAPPED ovl;\r
7835 \r
7836   is = (InputSource *) arg;\r
7837   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7838   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7839   while (is->hThread != NULL) {\r
7840     is->error = DoReadFile(is->hFile, is->next,\r
7841                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7842                            &is->count, &ovl);\r
7843     if (is->error == NO_ERROR) {\r
7844       is->next += is->count;\r
7845     } else {\r
7846       if (is->error == ERROR_BROKEN_PIPE) {\r
7847         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7848         is->count = 0;\r
7849       } else {\r
7850         is->count = (DWORD) -1;\r
7851         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7852         break; \r
7853       }\r
7854     }\r
7855 \r
7856     CheckForInputBufferFull( is );\r
7857 \r
7858     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7859 \r
7860     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7861 \r
7862     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7863   }\r
7864 \r
7865   CloseHandle(ovl.hEvent);\r
7866   CloseHandle(is->hFile);\r
7867 \r
7868   if (appData.debugMode) {\r
7869     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7870   }\r
7871 \r
7872   return 0;\r
7873 }\r
7874 \r
7875 \r
7876 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7877 DWORD\r
7878 NonOvlInputThread(LPVOID arg)\r
7879 {\r
7880   InputSource *is;\r
7881   char *p, *q;\r
7882   int i;\r
7883   char prev;\r
7884 \r
7885   is = (InputSource *) arg;\r
7886   while (is->hThread != NULL) {\r
7887     is->error = ReadFile(is->hFile, is->next,\r
7888                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7889                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7890     if (is->error == NO_ERROR) {\r
7891       /* Change CRLF to LF */\r
7892       if (is->next > is->buf) {\r
7893         p = is->next - 1;\r
7894         i = is->count + 1;\r
7895       } else {\r
7896         p = is->next;\r
7897         i = is->count;\r
7898       }\r
7899       q = p;\r
7900       prev = NULLCHAR;\r
7901       while (i > 0) {\r
7902         if (prev == '\r' && *p == '\n') {\r
7903           *(q-1) = '\n';\r
7904           is->count--;\r
7905         } else { \r
7906           *q++ = *p;\r
7907         }\r
7908         prev = *p++;\r
7909         i--;\r
7910       }\r
7911       *q = NULLCHAR;\r
7912       is->next = q;\r
7913     } else {\r
7914       if (is->error == ERROR_BROKEN_PIPE) {\r
7915         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7916         is->count = 0; \r
7917       } else {\r
7918         is->count = (DWORD) -1;\r
7919       }\r
7920     }\r
7921 \r
7922     CheckForInputBufferFull( is );\r
7923 \r
7924     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7925 \r
7926     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7927 \r
7928     if (is->count < 0) break;  /* Quit on error */\r
7929   }\r
7930   CloseHandle(is->hFile);\r
7931   return 0;\r
7932 }\r
7933 \r
7934 DWORD\r
7935 SocketInputThread(LPVOID arg)\r
7936 {\r
7937   InputSource *is;\r
7938 \r
7939   is = (InputSource *) arg;\r
7940   while (is->hThread != NULL) {\r
7941     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7942     if ((int)is->count == SOCKET_ERROR) {\r
7943       is->count = (DWORD) -1;\r
7944       is->error = WSAGetLastError();\r
7945     } else {\r
7946       is->error = NO_ERROR;\r
7947       is->next += is->count;\r
7948       if (is->count == 0 && is->second == is) {\r
7949         /* End of file on stderr; quit with no message */\r
7950         break;\r
7951       }\r
7952     }\r
7953     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7954 \r
7955     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7956 \r
7957     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7958   }\r
7959   return 0;\r
7960 }\r
7961 \r
7962 VOID\r
7963 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7964 {\r
7965   InputSource *is;\r
7966 \r
7967   is = (InputSource *) lParam;\r
7968   if (is->lineByLine) {\r
7969     /* Feed in lines one by one */\r
7970     char *p = is->buf;\r
7971     char *q = p;\r
7972     while (q < is->next) {\r
7973       if (*q++ == '\n') {\r
7974         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7975         p = q;\r
7976       }\r
7977     }\r
7978     \r
7979     /* Move any partial line to the start of the buffer */\r
7980     q = is->buf;\r
7981     while (p < is->next) {\r
7982       *q++ = *p++;\r
7983     }\r
7984     is->next = q;\r
7985 \r
7986     if (is->error != NO_ERROR || is->count == 0) {\r
7987       /* Notify backend of the error.  Note: If there was a partial\r
7988          line at the end, it is not flushed through. */\r
7989       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7990     }\r
7991   } else {\r
7992     /* Feed in the whole chunk of input at once */\r
7993     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7994     is->next = is->buf;\r
7995   }\r
7996 }\r
7997 \r
7998 /*---------------------------------------------------------------------------*\\r
7999  *\r
8000  *  Menu enables. Used when setting various modes.\r
8001  *\r
8002 \*---------------------------------------------------------------------------*/\r
8003 \r
8004 typedef struct {\r
8005   int item;\r
8006   int flags;\r
8007 } Enables;\r
8008 \r
8009 VOID\r
8010 GreyRevert(Boolean grey)\r
8011 { // [HGM] vari: for retracting variations in local mode\r
8012   HMENU hmenu = GetMenu(hwndMain);\r
8013   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8014   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8015 }\r
8016 \r
8017 VOID\r
8018 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8019 {\r
8020   while (enab->item > 0) {\r
8021     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8022     enab++;\r
8023   }\r
8024 }\r
8025 \r
8026 Enables gnuEnables[] = {\r
8027   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8028   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8040 \r
8041 \r
8042   // Needed to switch from ncp to GNU mode on Engine Load\r
8043   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8044   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8045   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8046   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8047   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8048   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8049   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
8050   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8051   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
8052   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
8053   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8054   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8055   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8056   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8057   { -1, -1 }\r
8058 };\r
8059 \r
8060 Enables icsEnables[] = {\r
8061   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8065   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8066   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8068   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8069   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8071   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8072   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8073   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8074   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8075   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8076   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8077   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8078   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8079   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8080   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8081   { -1, -1 }\r
8082 };\r
8083 \r
8084 #if ZIPPY\r
8085 Enables zippyEnables[] = {\r
8086   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8087   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8088   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8089   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8090   { -1, -1 }\r
8091 };\r
8092 #endif\r
8093 \r
8094 Enables ncpEnables[] = {\r
8095   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8096   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8104   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8105   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8107   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8108   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8109   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8110   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8111   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8112   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8113   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8114   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8115   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8116   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8117   { -1, -1 }\r
8118 };\r
8119 \r
8120 Enables trainingOnEnables[] = {\r
8121   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8122   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8123   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8124   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8125   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8126   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8127   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8128   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8129   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8130   { -1, -1 }\r
8131 };\r
8132 \r
8133 Enables trainingOffEnables[] = {\r
8134   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8135   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8136   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8137   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8138   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8139   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8140   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8141   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8142   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8143   { -1, -1 }\r
8144 };\r
8145 \r
8146 /* These modify either ncpEnables or gnuEnables */\r
8147 Enables cmailEnables[] = {\r
8148   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8149   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8150   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8151   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8152   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8153   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8154   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8155   { -1, -1 }\r
8156 };\r
8157 \r
8158 Enables machineThinkingEnables[] = {\r
8159   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8160   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8161   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8162   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8163   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8164   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8165   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8166   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8167   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8168   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8169   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8170   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8171   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8172 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8173   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8174   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8175   { -1, -1 }\r
8176 };\r
8177 \r
8178 Enables userThinkingEnables[] = {\r
8179   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8180   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8181   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8182   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8183   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8184   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8185   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8186   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8187   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8188   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8189   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8190   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8191   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8192 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8193   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8194   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8195   { -1, -1 }\r
8196 };\r
8197 \r
8198 /*---------------------------------------------------------------------------*\\r
8199  *\r
8200  *  Front-end interface functions exported by XBoard.\r
8201  *  Functions appear in same order as prototypes in frontend.h.\r
8202  * \r
8203 \*---------------------------------------------------------------------------*/\r
8204 VOID\r
8205 CheckMark(UINT item, int state)\r
8206 {\r
8207     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8208 }\r
8209 \r
8210 VOID\r
8211 ModeHighlight()\r
8212 {\r
8213   static UINT prevChecked = 0;\r
8214   static int prevPausing = 0;\r
8215   UINT nowChecked;\r
8216 \r
8217   if (pausing != prevPausing) {\r
8218     prevPausing = pausing;\r
8219     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8220                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8221     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8222   }\r
8223 \r
8224   switch (gameMode) {\r
8225   case BeginningOfGame:\r
8226     if (appData.icsActive)\r
8227       nowChecked = IDM_IcsClient;\r
8228     else if (appData.noChessProgram)\r
8229       nowChecked = IDM_EditGame;\r
8230     else\r
8231       nowChecked = IDM_MachineBlack;\r
8232     break;\r
8233   case MachinePlaysBlack:\r
8234     nowChecked = IDM_MachineBlack;\r
8235     break;\r
8236   case MachinePlaysWhite:\r
8237     nowChecked = IDM_MachineWhite;\r
8238     break;\r
8239   case TwoMachinesPlay:\r
8240     nowChecked = IDM_TwoMachines;\r
8241     break;\r
8242   case AnalyzeMode:\r
8243     nowChecked = IDM_AnalysisMode;\r
8244     break;\r
8245   case AnalyzeFile:\r
8246     nowChecked = IDM_AnalyzeFile;\r
8247     break;\r
8248   case EditGame:\r
8249     nowChecked = IDM_EditGame;\r
8250     break;\r
8251   case PlayFromGameFile:\r
8252     nowChecked = IDM_LoadGame;\r
8253     break;\r
8254   case EditPosition:\r
8255     nowChecked = IDM_EditPosition;\r
8256     break;\r
8257   case Training:\r
8258     nowChecked = IDM_Training;\r
8259     break;\r
8260   case IcsPlayingWhite:\r
8261   case IcsPlayingBlack:\r
8262   case IcsObserving:\r
8263   case IcsIdle:\r
8264     nowChecked = IDM_IcsClient;\r
8265     break;\r
8266   default:\r
8267   case EndOfGame:\r
8268     nowChecked = 0;\r
8269     break;\r
8270   }\r
8271   if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
8272     EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
8273   CheckMark(prevChecked, MF_UNCHECKED);\r
8274   CheckMark(nowChecked, MF_CHECKED);\r
8275   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8276 \r
8277   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8278     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8279                           MF_BYCOMMAND|MF_ENABLED);\r
8280   } else {\r
8281     (void) EnableMenuItem(GetMenu(hwndMain), \r
8282                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8283   }\r
8284 \r
8285   prevChecked = nowChecked;\r
8286 \r
8287   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8288   if (appData.icsActive) {\r
8289        if (appData.icsEngineAnalyze) {\r
8290                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8291        } else {\r
8292                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8293        }\r
8294   }\r
8295   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8296 }\r
8297 \r
8298 VOID\r
8299 SetICSMode()\r
8300 {\r
8301   HMENU hmenu = GetMenu(hwndMain);\r
8302   SetMenuEnables(hmenu, icsEnables);\r
8303   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8304     MF_BYCOMMAND|MF_ENABLED);\r
8305 #if ZIPPY\r
8306   if (appData.zippyPlay) {\r
8307     SetMenuEnables(hmenu, zippyEnables);\r
8308     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8309          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8310           MF_BYCOMMAND|MF_ENABLED);\r
8311   }\r
8312 #endif\r
8313 }\r
8314 \r
8315 VOID\r
8316 SetGNUMode()\r
8317 {\r
8318   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8319 }\r
8320 \r
8321 VOID\r
8322 SetNCPMode()\r
8323 {\r
8324   HMENU hmenu = GetMenu(hwndMain);\r
8325   SetMenuEnables(hmenu, ncpEnables);\r
8326     DrawMenuBar(hwndMain);\r
8327 }\r
8328 \r
8329 VOID\r
8330 SetCmailMode()\r
8331 {\r
8332   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8333 }\r
8334 \r
8335 VOID \r
8336 SetTrainingModeOn()\r
8337 {\r
8338   int i;\r
8339   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8340   for (i = 0; i < N_BUTTONS; i++) {\r
8341     if (buttonDesc[i].hwnd != NULL)\r
8342       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8343   }\r
8344   CommentPopDown();\r
8345 }\r
8346 \r
8347 VOID SetTrainingModeOff()\r
8348 {\r
8349   int i;\r
8350   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8351   for (i = 0; i < N_BUTTONS; i++) {\r
8352     if (buttonDesc[i].hwnd != NULL)\r
8353       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8354   }\r
8355 }\r
8356 \r
8357 \r
8358 VOID\r
8359 SetUserThinkingEnables()\r
8360 {\r
8361   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8362 }\r
8363 \r
8364 VOID\r
8365 SetMachineThinkingEnables()\r
8366 {\r
8367   HMENU hMenu = GetMenu(hwndMain);\r
8368   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8369 \r
8370   SetMenuEnables(hMenu, machineThinkingEnables);\r
8371 \r
8372   if (gameMode == MachinePlaysBlack) {\r
8373     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8374   } else if (gameMode == MachinePlaysWhite) {\r
8375     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8376   } else if (gameMode == TwoMachinesPlay) {\r
8377     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8378   }\r
8379 }\r
8380 \r
8381 \r
8382 VOID\r
8383 DisplayTitle(char *str)\r
8384 {\r
8385   char title[MSG_SIZ], *host;\r
8386   if (str[0] != NULLCHAR) {\r
8387     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8388   } else if (appData.icsActive) {\r
8389     if (appData.icsCommPort[0] != NULLCHAR)\r
8390       host = "ICS";\r
8391     else \r
8392       host = appData.icsHost;\r
8393       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8394   } else if (appData.noChessProgram) {\r
8395     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8396   } else {\r
8397     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8398     strcat(title, ": ");\r
8399     strcat(title, first.tidy);\r
8400   }\r
8401   SetWindowText(hwndMain, title);\r
8402 }\r
8403 \r
8404 \r
8405 VOID\r
8406 DisplayMessage(char *str1, char *str2)\r
8407 {\r
8408   HDC hdc;\r
8409   HFONT oldFont;\r
8410   int remain = MESSAGE_TEXT_MAX - 1;\r
8411   int len;\r
8412 \r
8413   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8414   messageText[0] = NULLCHAR;\r
8415   if (*str1) {\r
8416     len = strlen(str1);\r
8417     if (len > remain) len = remain;\r
8418     strncpy(messageText, str1, len);\r
8419     messageText[len] = NULLCHAR;\r
8420     remain -= len;\r
8421   }\r
8422   if (*str2 && remain >= 2) {\r
8423     if (*str1) {\r
8424       strcat(messageText, "  ");\r
8425       remain -= 2;\r
8426     }\r
8427     len = strlen(str2);\r
8428     if (len > remain) len = remain;\r
8429     strncat(messageText, str2, len);\r
8430   }\r
8431   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8432   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8433 \r
8434   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8435 \r
8436   SAYMACHINEMOVE();\r
8437 \r
8438   hdc = GetDC(hwndMain);\r
8439   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8440   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8441              &messageRect, messageText, strlen(messageText), NULL);\r
8442   (void) SelectObject(hdc, oldFont);\r
8443   (void) ReleaseDC(hwndMain, hdc);\r
8444 }\r
8445 \r
8446 VOID\r
8447 DisplayError(char *str, int error)\r
8448 {\r
8449   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8450   int len;\r
8451 \r
8452   if (error == 0) {\r
8453     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8454   } else {\r
8455     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8456                         NULL, error, LANG_NEUTRAL,\r
8457                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8458     if (len > 0) {\r
8459       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8460     } else {\r
8461       ErrorMap *em = errmap;\r
8462       while (em->err != 0 && em->err != error) em++;\r
8463       if (em->err != 0) {\r
8464         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8465       } else {\r
8466         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8467       }\r
8468     }\r
8469   }\r
8470   \r
8471   ErrorPopUp(_("Error"), buf);\r
8472 }\r
8473 \r
8474 \r
8475 VOID\r
8476 DisplayMoveError(char *str)\r
8477 {\r
8478   fromX = fromY = -1;\r
8479   ClearHighlights();\r
8480   DrawPosition(FALSE, NULL);\r
8481   if (appData.popupMoveErrors) {\r
8482     ErrorPopUp(_("Error"), str);\r
8483   } else {\r
8484     DisplayMessage(str, "");\r
8485     moveErrorMessageUp = TRUE;\r
8486   }\r
8487 }\r
8488 \r
8489 VOID\r
8490 DisplayFatalError(char *str, int error, int exitStatus)\r
8491 {\r
8492   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8493   int len;\r
8494   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8495 \r
8496   if (error != 0) {\r
8497     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8498                         NULL, error, LANG_NEUTRAL,\r
8499                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8500     if (len > 0) {\r
8501       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8502     } else {\r
8503       ErrorMap *em = errmap;\r
8504       while (em->err != 0 && em->err != error) em++;\r
8505       if (em->err != 0) {\r
8506         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8507       } else {\r
8508         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8509       }\r
8510     }\r
8511     str = buf;\r
8512   }\r
8513   if (appData.debugMode) {\r
8514     fprintf(debugFP, "%s: %s\n", label, str);\r
8515   }\r
8516   if (appData.popupExitMessage) {\r
8517     if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!\r
8518     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8519                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8520   }\r
8521   ExitEvent(exitStatus);\r
8522 }\r
8523 \r
8524 \r
8525 VOID\r
8526 DisplayInformation(char *str)\r
8527 {\r
8528   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8529 }\r
8530 \r
8531 \r
8532 VOID\r
8533 DisplayNote(char *str)\r
8534 {\r
8535   ErrorPopUp(_("Note"), str);\r
8536 }\r
8537 \r
8538 \r
8539 typedef struct {\r
8540   char *title, *question, *replyPrefix;\r
8541   ProcRef pr;\r
8542 } QuestionParams;\r
8543 \r
8544 LRESULT CALLBACK\r
8545 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8546 {\r
8547   static QuestionParams *qp;\r
8548   char reply[MSG_SIZ];\r
8549   int len, err;\r
8550 \r
8551   switch (message) {\r
8552   case WM_INITDIALOG:\r
8553     qp = (QuestionParams *) lParam;\r
8554     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8555     Translate(hDlg, DLG_Question);\r
8556     SetWindowText(hDlg, qp->title);\r
8557     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8558     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8559     return FALSE;\r
8560 \r
8561   case WM_COMMAND:\r
8562     switch (LOWORD(wParam)) {\r
8563     case IDOK:\r
8564       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8565       if (*reply) strcat(reply, " ");\r
8566       len = strlen(reply);\r
8567       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8568       strcat(reply, "\n");\r
8569       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8570       EndDialog(hDlg, TRUE);\r
8571       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8572       return TRUE;\r
8573     case IDCANCEL:\r
8574       EndDialog(hDlg, FALSE);\r
8575       return TRUE;\r
8576     default:\r
8577       break;\r
8578     }\r
8579     break;\r
8580   }\r
8581   return FALSE;\r
8582 }\r
8583 \r
8584 VOID\r
8585 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8586 {\r
8587     QuestionParams qp;\r
8588     FARPROC lpProc;\r
8589     \r
8590     qp.title = title;\r
8591     qp.question = question;\r
8592     qp.replyPrefix = replyPrefix;\r
8593     qp.pr = pr;\r
8594     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8595     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8596       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8597     FreeProcInstance(lpProc);\r
8598 }\r
8599 \r
8600 /* [AS] Pick FRC position */\r
8601 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8602 {\r
8603     static int * lpIndexFRC;\r
8604     BOOL index_is_ok;\r
8605     char buf[16];\r
8606 \r
8607     switch( message )\r
8608     {\r
8609     case WM_INITDIALOG:\r
8610         lpIndexFRC = (int *) lParam;\r
8611 \r
8612         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8613         Translate(hDlg, DLG_NewGameFRC);\r
8614 \r
8615         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8616         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8617         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8618         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8619 \r
8620         break;\r
8621 \r
8622     case WM_COMMAND:\r
8623         switch( LOWORD(wParam) ) {\r
8624         case IDOK:\r
8625             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8626             EndDialog( hDlg, 0 );\r
8627             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8628             return TRUE;\r
8629         case IDCANCEL:\r
8630             EndDialog( hDlg, 1 );   \r
8631             return TRUE;\r
8632         case IDC_NFG_Edit:\r
8633             if( HIWORD(wParam) == EN_CHANGE ) {\r
8634                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8635 \r
8636                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8637             }\r
8638             return TRUE;\r
8639         case IDC_NFG_Random:\r
8640           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8641             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8642             return TRUE;\r
8643         }\r
8644 \r
8645         break;\r
8646     }\r
8647 \r
8648     return FALSE;\r
8649 }\r
8650 \r
8651 int NewGameFRC()\r
8652 {\r
8653     int result;\r
8654     int index = appData.defaultFrcPosition;\r
8655     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8656 \r
8657     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8658 \r
8659     if( result == 0 ) {\r
8660         appData.defaultFrcPosition = index;\r
8661     }\r
8662 \r
8663     return result;\r
8664 }\r
8665 \r
8666 /* [AS] Game list options. Refactored by HGM */\r
8667 \r
8668 HWND gameListOptionsDialog;\r
8669 \r
8670 // low-level front-end: clear text edit / list widget\r
8671 void\r
8672 \r
8673 GLT_ClearList()\r
8674 {\r
8675     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8676 }\r
8677 \r
8678 // low-level front-end: clear text edit / list widget\r
8679 void\r
8680 GLT_DeSelectList()\r
8681 {\r
8682     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8683 }\r
8684 \r
8685 // low-level front-end: append line to text edit / list widget\r
8686 void\r
8687 GLT_AddToList( char *name )\r
8688 {\r
8689     if( name != 0 ) {\r
8690             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8691     }\r
8692 }\r
8693 \r
8694 // low-level front-end: get line from text edit / list widget\r
8695 Boolean\r
8696 GLT_GetFromList( int index, char *name )\r
8697 {\r
8698     if( name != 0 ) {\r
8699             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8700                 return TRUE;\r
8701     }\r
8702     return FALSE;\r
8703 }\r
8704 \r
8705 void GLT_MoveSelection( HWND hDlg, int delta )\r
8706 {\r
8707     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8708     int idx2 = idx1 + delta;\r
8709     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8710 \r
8711     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8712         char buf[128];\r
8713 \r
8714         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8715         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8716         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8717         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8718     }\r
8719 }\r
8720 \r
8721 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8722 {\r
8723     switch( message )\r
8724     {\r
8725     case WM_INITDIALOG:\r
8726         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8727         \r
8728         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8729         Translate(hDlg, DLG_GameListOptions);\r
8730 \r
8731         /* Initialize list */\r
8732         GLT_TagsToList( lpUserGLT );\r
8733 \r
8734         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8735 \r
8736         break;\r
8737 \r
8738     case WM_COMMAND:\r
8739         switch( LOWORD(wParam) ) {\r
8740         case IDOK:\r
8741             GLT_ParseList();\r
8742             EndDialog( hDlg, 0 );\r
8743             return TRUE;\r
8744         case IDCANCEL:\r
8745             EndDialog( hDlg, 1 );\r
8746             return TRUE;\r
8747 \r
8748         case IDC_GLT_Default:\r
8749             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8750             return TRUE;\r
8751 \r
8752         case IDC_GLT_Restore:\r
8753             GLT_TagsToList( appData.gameListTags );\r
8754             return TRUE;\r
8755 \r
8756         case IDC_GLT_Up:\r
8757             GLT_MoveSelection( hDlg, -1 );\r
8758             return TRUE;\r
8759 \r
8760         case IDC_GLT_Down:\r
8761             GLT_MoveSelection( hDlg, +1 );\r
8762             return TRUE;\r
8763         }\r
8764 \r
8765         break;\r
8766     }\r
8767 \r
8768     return FALSE;\r
8769 }\r
8770 \r
8771 int GameListOptions()\r
8772 {\r
8773     int result;\r
8774     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8775 \r
8776       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8777 \r
8778     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8779 \r
8780     if( result == 0 ) {\r
8781         char *oldTags = appData.gameListTags;\r
8782         /* [AS] Memory leak here! */\r
8783         appData.gameListTags = strdup( lpUserGLT ); \r
8784         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8785             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8786     }\r
8787 \r
8788     return result;\r
8789 }\r
8790 \r
8791 VOID\r
8792 DisplayIcsInteractionTitle(char *str)\r
8793 {\r
8794   char consoleTitle[MSG_SIZ];\r
8795 \r
8796     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8797     SetWindowText(hwndConsole, consoleTitle);\r
8798 \r
8799     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8800       char buf[MSG_SIZ], *p = buf, *q;\r
8801         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8802       do {\r
8803         q = strchr(p, ';');\r
8804         if(q) *q++ = 0;\r
8805         if(*p) ChatPopUp(p);\r
8806       } while(p=q);\r
8807     }\r
8808 \r
8809     SetActiveWindow(hwndMain);\r
8810 }\r
8811 \r
8812 void\r
8813 DrawPosition(int fullRedraw, Board board)\r
8814 {\r
8815   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8816 }\r
8817 \r
8818 void NotifyFrontendLogin()\r
8819 {\r
8820         if (hwndConsole)\r
8821                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8822 }\r
8823 \r
8824 VOID\r
8825 ResetFrontEnd()\r
8826 {\r
8827   fromX = fromY = -1;\r
8828   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8829     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8830     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8831     dragInfo.lastpos = dragInfo.pos;\r
8832     dragInfo.start.x = dragInfo.start.y = -1;\r
8833     dragInfo.from = dragInfo.start;\r
8834     ReleaseCapture();\r
8835     DrawPosition(TRUE, NULL);\r
8836   }\r
8837   TagsPopDown();\r
8838 }\r
8839 \r
8840 \r
8841 VOID\r
8842 CommentPopUp(char *title, char *str)\r
8843 {\r
8844   HWND hwnd = GetActiveWindow();\r
8845   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8846   SAY(str);\r
8847   SetActiveWindow(hwnd);\r
8848 }\r
8849 \r
8850 VOID\r
8851 CommentPopDown(void)\r
8852 {\r
8853   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8854   if (commentDialog) {\r
8855     ShowWindow(commentDialog, SW_HIDE);\r
8856   }\r
8857   commentUp = FALSE;\r
8858 }\r
8859 \r
8860 VOID\r
8861 EditCommentPopUp(int index, char *title, char *str)\r
8862 {\r
8863   EitherCommentPopUp(index, title, str, TRUE);\r
8864 }\r
8865 \r
8866 \r
8867 int\r
8868 Roar()\r
8869 {\r
8870   MyPlaySound(&sounds[(int)SoundRoar]);\r
8871   return 1;\r
8872 }\r
8873 \r
8874 VOID\r
8875 RingBell()\r
8876 {\r
8877   MyPlaySound(&sounds[(int)SoundMove]);\r
8878 }\r
8879 \r
8880 VOID PlayIcsWinSound()\r
8881 {\r
8882   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8883 }\r
8884 \r
8885 VOID PlayIcsLossSound()\r
8886 {\r
8887   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8888 }\r
8889 \r
8890 VOID PlayIcsDrawSound()\r
8891 {\r
8892   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8893 }\r
8894 \r
8895 VOID PlayIcsUnfinishedSound()\r
8896 {\r
8897   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8898 }\r
8899 \r
8900 VOID\r
8901 PlayAlarmSound()\r
8902 {\r
8903   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8904 }\r
8905 \r
8906 VOID\r
8907 PlayTellSound()\r
8908 {\r
8909   MyPlaySound(&textAttribs[ColorTell].sound);\r
8910 }\r
8911 \r
8912 \r
8913 VOID\r
8914 EchoOn()\r
8915 {\r
8916   HWND hInput;\r
8917   consoleEcho = TRUE;\r
8918   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8919   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8920   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8921 }\r
8922 \r
8923 \r
8924 VOID\r
8925 EchoOff()\r
8926 {\r
8927   CHARFORMAT cf;\r
8928   HWND hInput;\r
8929   consoleEcho = FALSE;\r
8930   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8931   /* This works OK: set text and background both to the same color */\r
8932   cf = consoleCF;\r
8933   cf.crTextColor = COLOR_ECHOOFF;\r
8934   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8935   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8936 }\r
8937 \r
8938 /* No Raw()...? */\r
8939 \r
8940 void Colorize(ColorClass cc, int continuation)\r
8941 {\r
8942   currentColorClass = cc;\r
8943   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8944   consoleCF.crTextColor = textAttribs[cc].color;\r
8945   consoleCF.dwEffects = textAttribs[cc].effects;\r
8946   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8947 }\r
8948 \r
8949 char *\r
8950 UserName()\r
8951 {\r
8952   static char buf[MSG_SIZ];\r
8953   DWORD bufsiz = MSG_SIZ;\r
8954 \r
8955   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8956         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8957   }\r
8958   if (!GetUserName(buf, &bufsiz)) {\r
8959     /*DisplayError("Error getting user name", GetLastError());*/\r
8960     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8961   }\r
8962   return buf;\r
8963 }\r
8964 \r
8965 char *\r
8966 HostName()\r
8967 {\r
8968   static char buf[MSG_SIZ];\r
8969   DWORD bufsiz = MSG_SIZ;\r
8970 \r
8971   if (!GetComputerName(buf, &bufsiz)) {\r
8972     /*DisplayError("Error getting host name", GetLastError());*/\r
8973     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8974   }\r
8975   return buf;\r
8976 }\r
8977 \r
8978 \r
8979 int\r
8980 ClockTimerRunning()\r
8981 {\r
8982   return clockTimerEvent != 0;\r
8983 }\r
8984 \r
8985 int\r
8986 StopClockTimer()\r
8987 {\r
8988   if (clockTimerEvent == 0) return FALSE;\r
8989   KillTimer(hwndMain, clockTimerEvent);\r
8990   clockTimerEvent = 0;\r
8991   return TRUE;\r
8992 }\r
8993 \r
8994 void\r
8995 StartClockTimer(long millisec)\r
8996 {\r
8997   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8998                              (UINT) millisec, NULL);\r
8999 }\r
9000 \r
9001 void\r
9002 DisplayWhiteClock(long timeRemaining, int highlight)\r
9003 {\r
9004   HDC hdc;\r
9005   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9006 \r
9007   if(appData.noGUI) return;\r
9008   hdc = GetDC(hwndMain);\r
9009   if (!IsIconic(hwndMain)) {\r
9010     DisplayAClock(hdc, timeRemaining, highlight, \r
9011                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
9012   }\r
9013   if (highlight && iconCurrent == iconBlack) {\r
9014     iconCurrent = iconWhite;\r
9015     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9016     if (IsIconic(hwndMain)) {\r
9017       DrawIcon(hdc, 2, 2, iconCurrent);\r
9018     }\r
9019   }\r
9020   (void) ReleaseDC(hwndMain, hdc);\r
9021   if (hwndConsole)\r
9022     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9023 }\r
9024 \r
9025 void\r
9026 DisplayBlackClock(long timeRemaining, int highlight)\r
9027 {\r
9028   HDC hdc;\r
9029   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9030 \r
9031 \r
9032   if(appData.noGUI) return;\r
9033   hdc = GetDC(hwndMain);\r
9034   if (!IsIconic(hwndMain)) {\r
9035     DisplayAClock(hdc, timeRemaining, highlight, \r
9036                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
9037   }\r
9038   if (highlight && iconCurrent == iconWhite) {\r
9039     iconCurrent = iconBlack;\r
9040     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9041     if (IsIconic(hwndMain)) {\r
9042       DrawIcon(hdc, 2, 2, iconCurrent);\r
9043     }\r
9044   }\r
9045   (void) ReleaseDC(hwndMain, hdc);\r
9046   if (hwndConsole)\r
9047     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9048 }\r
9049 \r
9050 \r
9051 int\r
9052 LoadGameTimerRunning()\r
9053 {\r
9054   return loadGameTimerEvent != 0;\r
9055 }\r
9056 \r
9057 int\r
9058 StopLoadGameTimer()\r
9059 {\r
9060   if (loadGameTimerEvent == 0) return FALSE;\r
9061   KillTimer(hwndMain, loadGameTimerEvent);\r
9062   loadGameTimerEvent = 0;\r
9063   return TRUE;\r
9064 }\r
9065 \r
9066 void\r
9067 StartLoadGameTimer(long millisec)\r
9068 {\r
9069   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9070                                 (UINT) millisec, NULL);\r
9071 }\r
9072 \r
9073 void\r
9074 AutoSaveGame()\r
9075 {\r
9076   char *defName;\r
9077   FILE *f;\r
9078   char fileTitle[MSG_SIZ];\r
9079 \r
9080   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9081   f = OpenFileDialog(hwndMain, "a", defName,\r
9082                      appData.oldSaveStyle ? "gam" : "pgn",\r
9083                      GAME_FILT, \r
9084                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9085   if (f != NULL) {\r
9086     SaveGame(f, 0, "");\r
9087     fclose(f);\r
9088   }\r
9089 }\r
9090 \r
9091 \r
9092 void\r
9093 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9094 {\r
9095   if (delayedTimerEvent != 0) {\r
9096     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9097       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9098     }\r
9099     KillTimer(hwndMain, delayedTimerEvent);\r
9100     delayedTimerEvent = 0;\r
9101     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9102     delayedTimerCallback();\r
9103   }\r
9104   delayedTimerCallback = cb;\r
9105   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9106                                 (UINT) millisec, NULL);\r
9107 }\r
9108 \r
9109 DelayedEventCallback\r
9110 GetDelayedEvent()\r
9111 {\r
9112   if (delayedTimerEvent) {\r
9113     return delayedTimerCallback;\r
9114   } else {\r
9115     return NULL;\r
9116   }\r
9117 }\r
9118 \r
9119 void\r
9120 CancelDelayedEvent()\r
9121 {\r
9122   if (delayedTimerEvent) {\r
9123     KillTimer(hwndMain, delayedTimerEvent);\r
9124     delayedTimerEvent = 0;\r
9125   }\r
9126 }\r
9127 \r
9128 DWORD GetWin32Priority(int nice)\r
9129 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9130 /*\r
9131 REALTIME_PRIORITY_CLASS     0x00000100\r
9132 HIGH_PRIORITY_CLASS         0x00000080\r
9133 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9134 NORMAL_PRIORITY_CLASS       0x00000020\r
9135 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9136 IDLE_PRIORITY_CLASS         0x00000040\r
9137 */\r
9138         if (nice < -15) return 0x00000080;\r
9139         if (nice < 0)   return 0x00008000;\r
9140         if (nice == 0)  return 0x00000020;\r
9141         if (nice < 15)  return 0x00004000;\r
9142         return 0x00000040;\r
9143 }\r
9144 \r
9145 void RunCommand(char *cmdLine)\r
9146 {\r
9147   /* Now create the child process. */\r
9148   STARTUPINFO siStartInfo;\r
9149   PROCESS_INFORMATION piProcInfo;\r
9150 \r
9151   siStartInfo.cb = sizeof(STARTUPINFO);\r
9152   siStartInfo.lpReserved = NULL;\r
9153   siStartInfo.lpDesktop = NULL;\r
9154   siStartInfo.lpTitle = NULL;\r
9155   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9156   siStartInfo.cbReserved2 = 0;\r
9157   siStartInfo.lpReserved2 = NULL;\r
9158   siStartInfo.hStdInput = NULL;\r
9159   siStartInfo.hStdOutput = NULL;\r
9160   siStartInfo.hStdError = NULL;\r
9161 \r
9162   CreateProcess(NULL,\r
9163                 cmdLine,           /* command line */\r
9164                 NULL,      /* process security attributes */\r
9165                 NULL,      /* primary thread security attrs */\r
9166                 TRUE,      /* handles are inherited */\r
9167                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9168                 NULL,      /* use parent's environment */\r
9169                 NULL,\r
9170                 &siStartInfo, /* STARTUPINFO pointer */\r
9171                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9172 \r
9173   CloseHandle(piProcInfo.hThread);\r
9174 }\r
9175 \r
9176 /* Start a child process running the given program.\r
9177    The process's standard output can be read from "from", and its\r
9178    standard input can be written to "to".\r
9179    Exit with fatal error if anything goes wrong.\r
9180    Returns an opaque pointer that can be used to destroy the process\r
9181    later.\r
9182 */\r
9183 int\r
9184 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9185 {\r
9186 #define BUFSIZE 4096\r
9187 \r
9188   HANDLE hChildStdinRd, hChildStdinWr,\r
9189     hChildStdoutRd, hChildStdoutWr;\r
9190   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9191   SECURITY_ATTRIBUTES saAttr;\r
9192   BOOL fSuccess;\r
9193   PROCESS_INFORMATION piProcInfo;\r
9194   STARTUPINFO siStartInfo;\r
9195   ChildProc *cp;\r
9196   char buf[MSG_SIZ];\r
9197   DWORD err;\r
9198 \r
9199   if (appData.debugMode) {\r
9200     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9201   }\r
9202 \r
9203   *pr = NoProc;\r
9204 \r
9205   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9206   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9207   saAttr.bInheritHandle = TRUE;\r
9208   saAttr.lpSecurityDescriptor = NULL;\r
9209 \r
9210   /*\r
9211    * The steps for redirecting child's STDOUT:\r
9212    *     1. Create anonymous pipe to be STDOUT for child.\r
9213    *     2. Create a noninheritable duplicate of read handle,\r
9214    *         and close the inheritable read handle.\r
9215    */\r
9216 \r
9217   /* Create a pipe for the child's STDOUT. */\r
9218   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9219     return GetLastError();\r
9220   }\r
9221 \r
9222   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9223   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9224                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9225                              FALSE,     /* not inherited */\r
9226                              DUPLICATE_SAME_ACCESS);\r
9227   if (! fSuccess) {\r
9228     return GetLastError();\r
9229   }\r
9230   CloseHandle(hChildStdoutRd);\r
9231 \r
9232   /*\r
9233    * The steps for redirecting child's STDIN:\r
9234    *     1. Create anonymous pipe to be STDIN for child.\r
9235    *     2. Create a noninheritable duplicate of write handle,\r
9236    *         and close the inheritable write handle.\r
9237    */\r
9238 \r
9239   /* Create a pipe for the child's STDIN. */\r
9240   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9241     return GetLastError();\r
9242   }\r
9243 \r
9244   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9245   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9246                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9247                              FALSE,     /* not inherited */\r
9248                              DUPLICATE_SAME_ACCESS);\r
9249   if (! fSuccess) {\r
9250     return GetLastError();\r
9251   }\r
9252   CloseHandle(hChildStdinWr);\r
9253 \r
9254   /* Arrange to (1) look in dir for the child .exe file, and\r
9255    * (2) have dir be the child's working directory.  Interpret\r
9256    * dir relative to the directory WinBoard loaded from. */\r
9257   GetCurrentDirectory(MSG_SIZ, buf);\r
9258   SetCurrentDirectory(installDir);\r
9259   SetCurrentDirectory(dir);\r
9260 \r
9261   /* Now create the child process. */\r
9262 \r
9263   siStartInfo.cb = sizeof(STARTUPINFO);\r
9264   siStartInfo.lpReserved = NULL;\r
9265   siStartInfo.lpDesktop = NULL;\r
9266   siStartInfo.lpTitle = NULL;\r
9267   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9268   siStartInfo.cbReserved2 = 0;\r
9269   siStartInfo.lpReserved2 = NULL;\r
9270   siStartInfo.hStdInput = hChildStdinRd;\r
9271   siStartInfo.hStdOutput = hChildStdoutWr;\r
9272   siStartInfo.hStdError = hChildStdoutWr;\r
9273 \r
9274   fSuccess = CreateProcess(NULL,\r
9275                            cmdLine,        /* command line */\r
9276                            NULL,           /* process security attributes */\r
9277                            NULL,           /* primary thread security attrs */\r
9278                            TRUE,           /* handles are inherited */\r
9279                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9280                            NULL,           /* use parent's environment */\r
9281                            NULL,\r
9282                            &siStartInfo, /* STARTUPINFO pointer */\r
9283                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9284 \r
9285   err = GetLastError();\r
9286   SetCurrentDirectory(buf); /* return to prev directory */\r
9287   if (! fSuccess) {\r
9288     return err;\r
9289   }\r
9290 \r
9291   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9292     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9293     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9294   }\r
9295 \r
9296   /* Close the handles we don't need in the parent */\r
9297   CloseHandle(piProcInfo.hThread);\r
9298   CloseHandle(hChildStdinRd);\r
9299   CloseHandle(hChildStdoutWr);\r
9300 \r
9301   /* Prepare return value */\r
9302   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9303   cp->kind = CPReal;\r
9304   cp->hProcess = piProcInfo.hProcess;\r
9305   cp->pid = piProcInfo.dwProcessId;\r
9306   cp->hFrom = hChildStdoutRdDup;\r
9307   cp->hTo = hChildStdinWrDup;\r
9308 \r
9309   *pr = (void *) cp;\r
9310 \r
9311   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9312      2000 where engines sometimes don't see the initial command(s)\r
9313      from WinBoard and hang.  I don't understand how that can happen,\r
9314      but the Sleep is harmless, so I've put it in.  Others have also\r
9315      reported what may be the same problem, so hopefully this will fix\r
9316      it for them too.  */\r
9317   Sleep(500);\r
9318 \r
9319   return NO_ERROR;\r
9320 }\r
9321 \r
9322 \r
9323 void\r
9324 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9325 {\r
9326   ChildProc *cp; int result;\r
9327 \r
9328   cp = (ChildProc *) pr;\r
9329   if (cp == NULL) return;\r
9330 \r
9331   switch (cp->kind) {\r
9332   case CPReal:\r
9333     /* TerminateProcess is considered harmful, so... */\r
9334     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9335     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9336     /* The following doesn't work because the chess program\r
9337        doesn't "have the same console" as WinBoard.  Maybe\r
9338        we could arrange for this even though neither WinBoard\r
9339        nor the chess program uses a console for stdio? */\r
9340     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9341 \r
9342     /* [AS] Special termination modes for misbehaving programs... */\r
9343     if( signal & 8 ) { \r
9344         result = TerminateProcess( cp->hProcess, 0 );\r
9345 \r
9346         if ( appData.debugMode) {\r
9347             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9348         }\r
9349     }\r
9350     else if( signal & 4 ) {\r
9351         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9352 \r
9353         if( dw != WAIT_OBJECT_0 ) {\r
9354             result = TerminateProcess( cp->hProcess, 0 );\r
9355 \r
9356             if ( appData.debugMode) {\r
9357                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9358             }\r
9359 \r
9360         }\r
9361     }\r
9362 \r
9363     CloseHandle(cp->hProcess);\r
9364     break;\r
9365 \r
9366   case CPComm:\r
9367     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9368     break;\r
9369 \r
9370   case CPSock:\r
9371     closesocket(cp->sock);\r
9372     WSACleanup();\r
9373     break;\r
9374 \r
9375   case CPRcmd:\r
9376     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9377     closesocket(cp->sock);\r
9378     closesocket(cp->sock2);\r
9379     WSACleanup();\r
9380     break;\r
9381   }\r
9382   free(cp);\r
9383 }\r
9384 \r
9385 void\r
9386 InterruptChildProcess(ProcRef pr)\r
9387 {\r
9388   ChildProc *cp;\r
9389 \r
9390   cp = (ChildProc *) pr;\r
9391   if (cp == NULL) return;\r
9392   switch (cp->kind) {\r
9393   case CPReal:\r
9394     /* The following doesn't work because the chess program\r
9395        doesn't "have the same console" as WinBoard.  Maybe\r
9396        we could arrange for this even though neither WinBoard\r
9397        nor the chess program uses a console for stdio */\r
9398     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9399     break;\r
9400 \r
9401   case CPComm:\r
9402   case CPSock:\r
9403     /* Can't interrupt */\r
9404     break;\r
9405 \r
9406   case CPRcmd:\r
9407     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9408     break;\r
9409   }\r
9410 }\r
9411 \r
9412 \r
9413 int\r
9414 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9415 {\r
9416   char cmdLine[MSG_SIZ];\r
9417 \r
9418   if (port[0] == NULLCHAR) {\r
9419     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9420   } else {\r
9421     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9422   }\r
9423   return StartChildProcess(cmdLine, "", pr);\r
9424 }\r
9425 \r
9426 \r
9427 /* Code to open TCP sockets */\r
9428 \r
9429 int\r
9430 OpenTCP(char *host, char *port, ProcRef *pr)\r
9431 {\r
9432   ChildProc *cp;\r
9433   int err;\r
9434   SOCKET s;\r
9435 \r
9436   struct sockaddr_in sa, mysa;\r
9437   struct hostent FAR *hp;\r
9438   unsigned short uport;\r
9439   WORD wVersionRequested;\r
9440   WSADATA wsaData;\r
9441 \r
9442   /* Initialize socket DLL */\r
9443   wVersionRequested = MAKEWORD(1, 1);\r
9444   err = WSAStartup(wVersionRequested, &wsaData);\r
9445   if (err != 0) return err;\r
9446 \r
9447   /* Make socket */\r
9448   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9449     err = WSAGetLastError();\r
9450     WSACleanup();\r
9451     return err;\r
9452   }\r
9453 \r
9454   /* Bind local address using (mostly) don't-care values.\r
9455    */\r
9456   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9457   mysa.sin_family = AF_INET;\r
9458   mysa.sin_addr.s_addr = INADDR_ANY;\r
9459   uport = (unsigned short) 0;\r
9460   mysa.sin_port = htons(uport);\r
9461   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9462       == SOCKET_ERROR) {\r
9463     err = WSAGetLastError();\r
9464     WSACleanup();\r
9465     return err;\r
9466   }\r
9467 \r
9468   /* Resolve remote host name */\r
9469   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9470   if (!(hp = gethostbyname(host))) {\r
9471     unsigned int b0, b1, b2, b3;\r
9472 \r
9473     err = WSAGetLastError();\r
9474 \r
9475     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9476       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9477       hp->h_addrtype = AF_INET;\r
9478       hp->h_length = 4;\r
9479       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9480       hp->h_addr_list[0] = (char *) malloc(4);\r
9481       hp->h_addr_list[0][0] = (char) b0;\r
9482       hp->h_addr_list[0][1] = (char) b1;\r
9483       hp->h_addr_list[0][2] = (char) b2;\r
9484       hp->h_addr_list[0][3] = (char) b3;\r
9485     } else {\r
9486       WSACleanup();\r
9487       return err;\r
9488     }\r
9489   }\r
9490   sa.sin_family = hp->h_addrtype;\r
9491   uport = (unsigned short) atoi(port);\r
9492   sa.sin_port = htons(uport);\r
9493   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9494 \r
9495   /* Make connection */\r
9496   if (connect(s, (struct sockaddr *) &sa,\r
9497               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9498     err = WSAGetLastError();\r
9499     WSACleanup();\r
9500     return err;\r
9501   }\r
9502 \r
9503   /* Prepare return value */\r
9504   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9505   cp->kind = CPSock;\r
9506   cp->sock = s;\r
9507   *pr = (ProcRef *) cp;\r
9508 \r
9509   return NO_ERROR;\r
9510 }\r
9511 \r
9512 int\r
9513 OpenCommPort(char *name, ProcRef *pr)\r
9514 {\r
9515   HANDLE h;\r
9516   COMMTIMEOUTS ct;\r
9517   ChildProc *cp;\r
9518   char fullname[MSG_SIZ];\r
9519 \r
9520   if (*name != '\\')\r
9521     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9522   else\r
9523     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9524 \r
9525   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9526                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9527   if (h == (HANDLE) -1) {\r
9528     return GetLastError();\r
9529   }\r
9530   hCommPort = h;\r
9531 \r
9532   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9533 \r
9534   /* Accumulate characters until a 100ms pause, then parse */\r
9535   ct.ReadIntervalTimeout = 100;\r
9536   ct.ReadTotalTimeoutMultiplier = 0;\r
9537   ct.ReadTotalTimeoutConstant = 0;\r
9538   ct.WriteTotalTimeoutMultiplier = 0;\r
9539   ct.WriteTotalTimeoutConstant = 0;\r
9540   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9541 \r
9542   /* Prepare return value */\r
9543   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9544   cp->kind = CPComm;\r
9545   cp->hFrom = h;\r
9546   cp->hTo = h;\r
9547   *pr = (ProcRef *) cp;\r
9548 \r
9549   return NO_ERROR;\r
9550 }\r
9551 \r
9552 int\r
9553 OpenLoopback(ProcRef *pr)\r
9554 {\r
9555   DisplayFatalError(_("Not implemented"), 0, 1);\r
9556   return NO_ERROR;\r
9557 }\r
9558 \r
9559 \r
9560 int\r
9561 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9562 {\r
9563   ChildProc *cp;\r
9564   int err;\r
9565   SOCKET s, s2, s3;\r
9566   struct sockaddr_in sa, mysa;\r
9567   struct hostent FAR *hp;\r
9568   unsigned short uport;\r
9569   WORD wVersionRequested;\r
9570   WSADATA wsaData;\r
9571   int fromPort;\r
9572   char stderrPortStr[MSG_SIZ];\r
9573 \r
9574   /* Initialize socket DLL */\r
9575   wVersionRequested = MAKEWORD(1, 1);\r
9576   err = WSAStartup(wVersionRequested, &wsaData);\r
9577   if (err != 0) return err;\r
9578 \r
9579   /* Resolve remote host name */\r
9580   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9581   if (!(hp = gethostbyname(host))) {\r
9582     unsigned int b0, b1, b2, b3;\r
9583 \r
9584     err = WSAGetLastError();\r
9585 \r
9586     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9587       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9588       hp->h_addrtype = AF_INET;\r
9589       hp->h_length = 4;\r
9590       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9591       hp->h_addr_list[0] = (char *) malloc(4);\r
9592       hp->h_addr_list[0][0] = (char) b0;\r
9593       hp->h_addr_list[0][1] = (char) b1;\r
9594       hp->h_addr_list[0][2] = (char) b2;\r
9595       hp->h_addr_list[0][3] = (char) b3;\r
9596     } else {\r
9597       WSACleanup();\r
9598       return err;\r
9599     }\r
9600   }\r
9601   sa.sin_family = hp->h_addrtype;\r
9602   uport = (unsigned short) 514;\r
9603   sa.sin_port = htons(uport);\r
9604   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9605 \r
9606   /* Bind local socket to unused "privileged" port address\r
9607    */\r
9608   s = INVALID_SOCKET;\r
9609   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9610   mysa.sin_family = AF_INET;\r
9611   mysa.sin_addr.s_addr = INADDR_ANY;\r
9612   for (fromPort = 1023;; fromPort--) {\r
9613     if (fromPort < 0) {\r
9614       WSACleanup();\r
9615       return WSAEADDRINUSE;\r
9616     }\r
9617     if (s == INVALID_SOCKET) {\r
9618       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9619         err = WSAGetLastError();\r
9620         WSACleanup();\r
9621         return err;\r
9622       }\r
9623     }\r
9624     uport = (unsigned short) fromPort;\r
9625     mysa.sin_port = htons(uport);\r
9626     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9627         == SOCKET_ERROR) {\r
9628       err = WSAGetLastError();\r
9629       if (err == WSAEADDRINUSE) continue;\r
9630       WSACleanup();\r
9631       return err;\r
9632     }\r
9633     if (connect(s, (struct sockaddr *) &sa,\r
9634       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9635       err = WSAGetLastError();\r
9636       if (err == WSAEADDRINUSE) {\r
9637         closesocket(s);\r
9638         s = -1;\r
9639         continue;\r
9640       }\r
9641       WSACleanup();\r
9642       return err;\r
9643     }\r
9644     break;\r
9645   }\r
9646 \r
9647   /* Bind stderr local socket to unused "privileged" port address\r
9648    */\r
9649   s2 = INVALID_SOCKET;\r
9650   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9651   mysa.sin_family = AF_INET;\r
9652   mysa.sin_addr.s_addr = INADDR_ANY;\r
9653   for (fromPort = 1023;; fromPort--) {\r
9654     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9655     if (fromPort < 0) {\r
9656       (void) closesocket(s);\r
9657       WSACleanup();\r
9658       return WSAEADDRINUSE;\r
9659     }\r
9660     if (s2 == INVALID_SOCKET) {\r
9661       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9662         err = WSAGetLastError();\r
9663         closesocket(s);\r
9664         WSACleanup();\r
9665         return err;\r
9666       }\r
9667     }\r
9668     uport = (unsigned short) fromPort;\r
9669     mysa.sin_port = htons(uport);\r
9670     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9671         == SOCKET_ERROR) {\r
9672       err = WSAGetLastError();\r
9673       if (err == WSAEADDRINUSE) continue;\r
9674       (void) closesocket(s);\r
9675       WSACleanup();\r
9676       return err;\r
9677     }\r
9678     if (listen(s2, 1) == SOCKET_ERROR) {\r
9679       err = WSAGetLastError();\r
9680       if (err == WSAEADDRINUSE) {\r
9681         closesocket(s2);\r
9682         s2 = INVALID_SOCKET;\r
9683         continue;\r
9684       }\r
9685       (void) closesocket(s);\r
9686       (void) closesocket(s2);\r
9687       WSACleanup();\r
9688       return err;\r
9689     }\r
9690     break;\r
9691   }\r
9692   prevStderrPort = fromPort; // remember port used\r
9693   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9694 \r
9695   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9696     err = WSAGetLastError();\r
9697     (void) closesocket(s);\r
9698     (void) closesocket(s2);\r
9699     WSACleanup();\r
9700     return err;\r
9701   }\r
9702 \r
9703   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9704     err = WSAGetLastError();\r
9705     (void) closesocket(s);\r
9706     (void) closesocket(s2);\r
9707     WSACleanup();\r
9708     return err;\r
9709   }\r
9710   if (*user == NULLCHAR) user = UserName();\r
9711   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9712     err = WSAGetLastError();\r
9713     (void) closesocket(s);\r
9714     (void) closesocket(s2);\r
9715     WSACleanup();\r
9716     return err;\r
9717   }\r
9718   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9719     err = WSAGetLastError();\r
9720     (void) closesocket(s);\r
9721     (void) closesocket(s2);\r
9722     WSACleanup();\r
9723     return err;\r
9724   }\r
9725 \r
9726   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9727     err = WSAGetLastError();\r
9728     (void) closesocket(s);\r
9729     (void) closesocket(s2);\r
9730     WSACleanup();\r
9731     return err;\r
9732   }\r
9733   (void) closesocket(s2);  /* Stop listening */\r
9734 \r
9735   /* Prepare return value */\r
9736   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9737   cp->kind = CPRcmd;\r
9738   cp->sock = s;\r
9739   cp->sock2 = s3;\r
9740   *pr = (ProcRef *) cp;\r
9741 \r
9742   return NO_ERROR;\r
9743 }\r
9744 \r
9745 \r
9746 InputSourceRef\r
9747 AddInputSource(ProcRef pr, int lineByLine,\r
9748                InputCallback func, VOIDSTAR closure)\r
9749 {\r
9750   InputSource *is, *is2 = NULL;\r
9751   ChildProc *cp = (ChildProc *) pr;\r
9752 \r
9753   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9754   is->lineByLine = lineByLine;\r
9755   is->func = func;\r
9756   is->closure = closure;\r
9757   is->second = NULL;\r
9758   is->next = is->buf;\r
9759   if (pr == NoProc) {\r
9760     is->kind = CPReal;\r
9761     consoleInputSource = is;\r
9762   } else {\r
9763     is->kind = cp->kind;\r
9764     /* \r
9765         [AS] Try to avoid a race condition if the thread is given control too early:\r
9766         we create all threads suspended so that the is->hThread variable can be\r
9767         safely assigned, then let the threads start with ResumeThread.\r
9768     */\r
9769     switch (cp->kind) {\r
9770     case CPReal:\r
9771       is->hFile = cp->hFrom;\r
9772       cp->hFrom = NULL; /* now owned by InputThread */\r
9773       is->hThread =\r
9774         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9775                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9776       break;\r
9777 \r
9778     case CPComm:\r
9779       is->hFile = cp->hFrom;\r
9780       cp->hFrom = NULL; /* now owned by InputThread */\r
9781       is->hThread =\r
9782         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9783                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9784       break;\r
9785 \r
9786     case CPSock:\r
9787       is->sock = cp->sock;\r
9788       is->hThread =\r
9789         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9790                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9791       break;\r
9792 \r
9793     case CPRcmd:\r
9794       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9795       *is2 = *is;\r
9796       is->sock = cp->sock;\r
9797       is->second = is2;\r
9798       is2->sock = cp->sock2;\r
9799       is2->second = is2;\r
9800       is->hThread =\r
9801         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9802                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9803       is2->hThread =\r
9804         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9805                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9806       break;\r
9807     }\r
9808 \r
9809     if( is->hThread != NULL ) {\r
9810         ResumeThread( is->hThread );\r
9811     }\r
9812 \r
9813     if( is2 != NULL && is2->hThread != NULL ) {\r
9814         ResumeThread( is2->hThread );\r
9815     }\r
9816   }\r
9817 \r
9818   return (InputSourceRef) is;\r
9819 }\r
9820 \r
9821 void\r
9822 RemoveInputSource(InputSourceRef isr)\r
9823 {\r
9824   InputSource *is;\r
9825 \r
9826   is = (InputSource *) isr;\r
9827   is->hThread = NULL;  /* tell thread to stop */\r
9828   CloseHandle(is->hThread);\r
9829   if (is->second != NULL) {\r
9830     is->second->hThread = NULL;\r
9831     CloseHandle(is->second->hThread);\r
9832   }\r
9833 }\r
9834 \r
9835 int no_wrap(char *message, int count)\r
9836 {\r
9837     ConsoleOutput(message, count, FALSE);\r
9838     return count;\r
9839 }\r
9840 \r
9841 int\r
9842 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9843 {\r
9844   DWORD dOutCount;\r
9845   int outCount = SOCKET_ERROR;\r
9846   ChildProc *cp = (ChildProc *) pr;\r
9847   static OVERLAPPED ovl;\r
9848 \r
9849   static int line = 0;\r
9850 \r
9851   if (pr == NoProc)\r
9852   {\r
9853     if (appData.noJoin || !appData.useInternalWrap)\r
9854       return no_wrap(message, count);\r
9855     else\r
9856     {\r
9857       int width = get_term_width();\r
9858       int len = wrap(NULL, message, count, width, &line);\r
9859       char *msg = malloc(len);\r
9860       int dbgchk;\r
9861 \r
9862       if (!msg)\r
9863         return no_wrap(message, count);\r
9864       else\r
9865       {\r
9866         dbgchk = wrap(msg, message, count, width, &line);\r
9867         if (dbgchk != len && appData.debugMode)\r
9868             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9869         ConsoleOutput(msg, len, FALSE);\r
9870         free(msg);\r
9871         return len;\r
9872       }\r
9873     }\r
9874   }\r
9875 \r
9876   if (ovl.hEvent == NULL) {\r
9877     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9878   }\r
9879   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9880 \r
9881   switch (cp->kind) {\r
9882   case CPSock:\r
9883   case CPRcmd:\r
9884     outCount = send(cp->sock, message, count, 0);\r
9885     if (outCount == SOCKET_ERROR) {\r
9886       *outError = WSAGetLastError();\r
9887     } else {\r
9888       *outError = NO_ERROR;\r
9889     }\r
9890     break;\r
9891 \r
9892   case CPReal:\r
9893     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9894                   &dOutCount, NULL)) {\r
9895       *outError = NO_ERROR;\r
9896       outCount = (int) dOutCount;\r
9897     } else {\r
9898       *outError = GetLastError();\r
9899     }\r
9900     break;\r
9901 \r
9902   case CPComm:\r
9903     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9904                             &dOutCount, &ovl);\r
9905     if (*outError == NO_ERROR) {\r
9906       outCount = (int) dOutCount;\r
9907     }\r
9908     break;\r
9909   }\r
9910   return outCount;\r
9911 }\r
9912 \r
9913 void\r
9914 DoSleep(int n)\r
9915 {\r
9916     if(n != 0) Sleep(n);\r
9917 }\r
9918 \r
9919 int\r
9920 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9921                        long msdelay)\r
9922 {\r
9923   /* Ignore delay, not implemented for WinBoard */\r
9924   return OutputToProcess(pr, message, count, outError);\r
9925 }\r
9926 \r
9927 \r
9928 void\r
9929 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9930                         char *buf, int count, int error)\r
9931 {\r
9932   DisplayFatalError(_("Not implemented"), 0, 1);\r
9933 }\r
9934 \r
9935 /* see wgamelist.c for Game List functions */\r
9936 /* see wedittags.c for Edit Tags functions */\r
9937 \r
9938 \r
9939 int\r
9940 ICSInitScript()\r
9941 {\r
9942   FILE *f;\r
9943   char buf[MSG_SIZ];\r
9944   char *dummy;\r
9945 \r
9946   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9947     f = fopen(buf, "r");\r
9948     if (f != NULL) {\r
9949       ProcessICSInitScript(f);\r
9950       fclose(f);\r
9951       return TRUE;\r
9952     }\r
9953   }\r
9954   return FALSE;\r
9955 }\r
9956 \r
9957 \r
9958 VOID\r
9959 StartAnalysisClock()\r
9960 {\r
9961   if (analysisTimerEvent) return;\r
9962   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9963                                         (UINT) 2000, NULL);\r
9964 }\r
9965 \r
9966 VOID\r
9967 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9968 {\r
9969   highlightInfo.sq[0].x = fromX;\r
9970   highlightInfo.sq[0].y = fromY;\r
9971   highlightInfo.sq[1].x = toX;\r
9972   highlightInfo.sq[1].y = toY;\r
9973 }\r
9974 \r
9975 VOID\r
9976 ClearHighlights()\r
9977 {\r
9978   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9979     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9980 }\r
9981 \r
9982 VOID\r
9983 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9984 {\r
9985   premoveHighlightInfo.sq[0].x = fromX;\r
9986   premoveHighlightInfo.sq[0].y = fromY;\r
9987   premoveHighlightInfo.sq[1].x = toX;\r
9988   premoveHighlightInfo.sq[1].y = toY;\r
9989 }\r
9990 \r
9991 VOID\r
9992 ClearPremoveHighlights()\r
9993 {\r
9994   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9995     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9996 }\r
9997 \r
9998 VOID\r
9999 ShutDownFrontEnd()\r
10000 {\r
10001   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10002   DeleteClipboardTempFiles();\r
10003 }\r
10004 \r
10005 void\r
10006 BoardToTop()\r
10007 {\r
10008     if (IsIconic(hwndMain))\r
10009       ShowWindow(hwndMain, SW_RESTORE);\r
10010 \r
10011     SetActiveWindow(hwndMain);\r
10012 }\r
10013 \r
10014 /*\r
10015  * Prototypes for animation support routines\r
10016  */\r
10017 static void ScreenSquare(int column, int row, POINT * pt);\r
10018 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10019      POINT frames[], int * nFrames);\r
10020 \r
10021 \r
10022 #define kFactor 4\r
10023 \r
10024 void\r
10025 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
10026 {       // [HGM] atomic: animate blast wave\r
10027         int i;\r
10028 \r
10029         explodeInfo.fromX = fromX;\r
10030         explodeInfo.fromY = fromY;\r
10031         explodeInfo.toX = toX;\r
10032         explodeInfo.toY = toY;\r
10033         for(i=1; i<4*kFactor; i++) {\r
10034             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
10035             DrawPosition(FALSE, board);\r
10036             Sleep(appData.animSpeed);\r
10037         }\r
10038         explodeInfo.radius = 0;\r
10039         DrawPosition(TRUE, board);\r
10040 }\r
10041 \r
10042 void\r
10043 AnimateMove(board, fromX, fromY, toX, toY)\r
10044      Board board;\r
10045      int fromX;\r
10046      int fromY;\r
10047      int toX;\r
10048      int toY;\r
10049 {\r
10050   ChessSquare piece;\r
10051   int x = toX, y = toY;\r
10052   POINT start, finish, mid;\r
10053   POINT frames[kFactor * 2 + 1];\r
10054   int nFrames, n;\r
10055 \r
10056   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
10057 \r
10058   if (!appData.animate) return;\r
10059   if (doingSizing) return;\r
10060   if (fromY < 0 || fromX < 0) return;\r
10061   piece = board[fromY][fromX];\r
10062   if (piece >= EmptySquare) return;\r
10063 \r
10064   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
10065 \r
10066 again:\r
10067 \r
10068   ScreenSquare(fromX, fromY, &start);\r
10069   ScreenSquare(toX, toY, &finish);\r
10070 \r
10071   /* All moves except knight jumps move in straight line */\r
10072   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10073     mid.x = start.x + (finish.x - start.x) / 2;\r
10074     mid.y = start.y + (finish.y - start.y) / 2;\r
10075   } else {\r
10076     /* Knight: make straight movement then diagonal */\r
10077     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10078        mid.x = start.x + (finish.x - start.x) / 2;\r
10079        mid.y = start.y;\r
10080      } else {\r
10081        mid.x = start.x;\r
10082        mid.y = start.y + (finish.y - start.y) / 2;\r
10083      }\r
10084   }\r
10085   \r
10086   /* Don't use as many frames for very short moves */\r
10087   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10088     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10089   else\r
10090     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10091 \r
10092   animInfo.from.x = fromX;\r
10093   animInfo.from.y = fromY;\r
10094   animInfo.to.x = toX;\r
10095   animInfo.to.y = toY;\r
10096   animInfo.lastpos = start;\r
10097   animInfo.piece = piece;\r
10098   for (n = 0; n < nFrames; n++) {\r
10099     animInfo.pos = frames[n];\r
10100     DrawPosition(FALSE, NULL);\r
10101     animInfo.lastpos = animInfo.pos;\r
10102     Sleep(appData.animSpeed);\r
10103   }\r
10104   animInfo.pos = finish;\r
10105   DrawPosition(FALSE, NULL);\r
10106 \r
10107   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10108 \r
10109   animInfo.piece = EmptySquare;\r
10110   Explode(board, fromX, fromY, toX, toY);\r
10111 }\r
10112 \r
10113 /*      Convert board position to corner of screen rect and color       */\r
10114 \r
10115 static void\r
10116 ScreenSquare(column, row, pt)\r
10117      int column; int row; POINT * pt;\r
10118 {\r
10119   if (flipView) {\r
10120     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10121     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10122   } else {\r
10123     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10124     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10125   }\r
10126 }\r
10127 \r
10128 /*      Generate a series of frame coords from start->mid->finish.\r
10129         The movement rate doubles until the half way point is\r
10130         reached, then halves back down to the final destination,\r
10131         which gives a nice slow in/out effect. The algorithmn\r
10132         may seem to generate too many intermediates for short\r
10133         moves, but remember that the purpose is to attract the\r
10134         viewers attention to the piece about to be moved and\r
10135         then to where it ends up. Too few frames would be less\r
10136         noticeable.                                             */\r
10137 \r
10138 static void\r
10139 Tween(start, mid, finish, factor, frames, nFrames)\r
10140      POINT * start; POINT * mid;\r
10141      POINT * finish; int factor;\r
10142      POINT frames[]; int * nFrames;\r
10143 {\r
10144   int n, fraction = 1, count = 0;\r
10145 \r
10146   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10147   for (n = 0; n < factor; n++)\r
10148     fraction *= 2;\r
10149   for (n = 0; n < factor; n++) {\r
10150     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10151     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10152     count ++;\r
10153     fraction = fraction / 2;\r
10154   }\r
10155   \r
10156   /* Midpoint */\r
10157   frames[count] = *mid;\r
10158   count ++;\r
10159   \r
10160   /* Slow out, stepping 1/2, then 1/4, ... */\r
10161   fraction = 2;\r
10162   for (n = 0; n < factor; n++) {\r
10163     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10164     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10165     count ++;\r
10166     fraction = fraction * 2;\r
10167   }\r
10168   *nFrames = count;\r
10169 }\r
10170 \r
10171 void\r
10172 SettingsPopUp(ChessProgramState *cps)\r
10173 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10174       EngineOptionsPopup(savedHwnd, cps);\r
10175 }\r
10176 \r
10177 int flock(int fid, int code)\r
10178 {\r
10179     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10180     OVERLAPPED ov;\r
10181     ov.hEvent = NULL;\r
10182     ov.Offset = 0;\r
10183     ov.OffsetHigh = 0;\r
10184     switch(code) {\r
10185       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10186 \r
10187       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10188       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10189       default: return -1;\r
10190     }\r
10191     return 0;\r
10192 }\r
10193 \r
10194 char *\r
10195 Col2Text (int n)\r
10196 {\r
10197     static int i=0;\r
10198     static char col[8][20];\r
10199     COLORREF color = *(COLORREF *) colorVariable[n];\r
10200     i = i+1 & 7;\r
10201     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10202     return col[i];\r
10203 }\r
10204 \r
10205 void\r
10206 ActivateTheme (int new)\r
10207 {   // Redo initialization of features depending on options that can occur in themes\r
10208    InitTextures();\r
10209    if(new) InitDrawingColors();\r
10210    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10211    InitDrawingSizes(boardSize, 0);\r
10212    InvalidateRect(hwndMain, NULL, TRUE);\r
10213 }\r