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