updated copyright for 2016
[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, 1, 0, 0 },\r
539   { "teeny",    25, 1, 1, 1, 0, 0 },\r
540   { "dinky",    29, 1, 1, 1, 0, 0 },\r
541   { "petite",   33, 1, 1, 1, 0, 0 },\r
542   { "slim",     37, 2, 1, 0, 0, 0 },\r
543   { "small",    40, 2, 1, 0, 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 ? 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[2][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_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
608 };\r
609 \r
610 \r
611 MySound sounds[(int)NSoundClasses];\r
612 MyTextAttribs textAttribs[(int)NColorClasses];\r
613 \r
614 MyColorizeAttribs colorizeAttribs[] = {\r
615   { (COLORREF)0, 0, N_("Shout Text") },\r
616   { (COLORREF)0, 0, N_("SShout/CShout") },\r
617   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
618   { (COLORREF)0, 0, N_("Channel Text") },\r
619   { (COLORREF)0, 0, N_("Kibitz Text") },\r
620   { (COLORREF)0, 0, N_("Tell Text") },\r
621   { (COLORREF)0, 0, N_("Challenge Text") },\r
622   { (COLORREF)0, 0, N_("Request Text") },\r
623   { (COLORREF)0, 0, N_("Seek Text") },\r
624   { (COLORREF)0, 0, N_("Normal Text") },\r
625   { (COLORREF)0, 0, N_("None") }\r
626 };\r
627 \r
628 \r
629 \r
630 static char *commentTitle;\r
631 static char *commentText;\r
632 static int commentIndex;\r
633 static Boolean editComment = FALSE;\r
634 \r
635 \r
636 char errorTitle[MSG_SIZ];\r
637 char errorMessage[2*MSG_SIZ];\r
638 HWND errorDialog = NULL;\r
639 BOOLEAN moveErrorMessageUp = FALSE;\r
640 BOOLEAN consoleEcho = TRUE;\r
641 CHARFORMAT consoleCF;\r
642 COLORREF consoleBackgroundColor;\r
643 \r
644 char *programVersion;\r
645 \r
646 #define CPReal 1\r
647 #define CPComm 2\r
648 #define CPSock 3\r
649 #define CPRcmd 4\r
650 typedef int CPKind;\r
651 \r
652 typedef struct {\r
653   CPKind kind;\r
654   HANDLE hProcess;\r
655   DWORD pid;\r
656   HANDLE hTo;\r
657   HANDLE hFrom;\r
658   SOCKET sock;\r
659   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
660 } ChildProc;\r
661 \r
662 #define INPUT_SOURCE_BUF_SIZE 4096\r
663 \r
664 typedef struct _InputSource {\r
665   CPKind kind;\r
666   HANDLE hFile;\r
667   SOCKET sock;\r
668   int lineByLine;\r
669   HANDLE hThread;\r
670   DWORD id;\r
671   char buf[INPUT_SOURCE_BUF_SIZE];\r
672   char *next;\r
673   DWORD count;\r
674   int error;\r
675   InputCallback func;\r
676   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
677   VOIDSTAR closure;\r
678 } InputSource;\r
679 \r
680 InputSource *consoleInputSource;\r
681 \r
682 DCB dcb;\r
683 \r
684 /* forward */\r
685 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
686 VOID ConsoleCreate();\r
687 LRESULT CALLBACK\r
688   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
689 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
690 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
691 VOID ParseCommSettings(char *arg, DCB *dcb);\r
692 LRESULT CALLBACK\r
693   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
694 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
695 void ParseIcsTextMenu(char *icsTextMenuString);\r
696 VOID PopUpNameDialog(char firstchar);\r
697 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
698 \r
699 /* [AS] */\r
700 int NewGameFRC();\r
701 int GameListOptions();\r
702 \r
703 int dummy; // [HGM] for obsolete args\r
704 \r
705 HWND hwndMain = NULL;        /* root window*/\r
706 HWND hwndConsole = NULL;\r
707 HWND commentDialog = NULL;\r
708 HWND moveHistoryDialog = NULL;\r
709 HWND evalGraphDialog = NULL;\r
710 HWND engineOutputDialog = NULL;\r
711 HWND gameListDialog = NULL;\r
712 HWND editTagsDialog = NULL;\r
713 \r
714 int commentUp = FALSE;\r
715 \r
716 WindowPlacement wpMain;\r
717 WindowPlacement wpConsole;\r
718 WindowPlacement wpComment;\r
719 WindowPlacement wpMoveHistory;\r
720 WindowPlacement wpEvalGraph;\r
721 WindowPlacement wpEngineOutput;\r
722 WindowPlacement wpGameList;\r
723 WindowPlacement wpTags;\r
724 \r
725 VOID EngineOptionsPopup(); // [HGM] settings\r
726 \r
727 VOID GothicPopUp(char *title, VariantClass variant);\r
728 /*\r
729  * Setting "frozen" should disable all user input other than deleting\r
730  * the window.  We do this while engines are initializing themselves.\r
731  */\r
732 static int frozen = 0;\r
733 static int oldMenuItemState[MENU_BAR_ITEMS];\r
734 void FreezeUI()\r
735 {\r
736   HMENU hmenu;\r
737   int i;\r
738 \r
739   if (frozen) return;\r
740   frozen = 1;\r
741   hmenu = GetMenu(hwndMain);\r
742   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
743     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
744   }\r
745   DrawMenuBar(hwndMain);\r
746 }\r
747 \r
748 /* Undo a FreezeUI */\r
749 void ThawUI()\r
750 {\r
751   HMENU hmenu;\r
752   int i;\r
753 \r
754   if (!frozen) return;\r
755   frozen = 0;\r
756   hmenu = GetMenu(hwndMain);\r
757   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
758     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
759   }\r
760   DrawMenuBar(hwndMain);\r
761 }\r
762 \r
763 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
764 \r
765 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
766 #ifdef JAWS\r
767 #include "jaws.c"\r
768 #else\r
769 #define JAWS_INIT\r
770 #define JAWS_ARGS\r
771 #define JAWS_ALT_INTERCEPT\r
772 #define JAWS_KBUP_NAVIGATION\r
773 #define JAWS_KBDOWN_NAVIGATION\r
774 #define JAWS_MENU_ITEMS\r
775 #define JAWS_SILENCE\r
776 #define JAWS_REPLAY\r
777 #define JAWS_ACCEL\r
778 #define JAWS_COPYRIGHT\r
779 #define JAWS_DELETE(X) X\r
780 #define SAYMACHINEMOVE()\r
781 #define SAY(X)\r
782 #endif\r
783 \r
784 /*---------------------------------------------------------------------------*\\r
785  *\r
786  * WinMain\r
787  *\r
788 \*---------------------------------------------------------------------------*/\r
789 \r
790 static void HandleMessage P((MSG *message));\r
791 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
792 \r
793 int APIENTRY\r
794 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
795         LPSTR lpCmdLine, int nCmdShow)\r
796 {\r
797   MSG msg;\r
798 //  INITCOMMONCONTROLSEX ex;\r
799 \r
800   debugFP = stderr;\r
801 \r
802   LoadLibrary("RICHED32.DLL");\r
803   consoleCF.cbSize = sizeof(CHARFORMAT);\r
804 \r
805   if (!InitApplication(hInstance)) {\r
806     return (FALSE);\r
807   }\r
808   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
809     return (FALSE);\r
810   }\r
811 \r
812   JAWS_INIT\r
813   TranslateMenus(1);\r
814 \r
815 //  InitCommonControlsEx(&ex);\r
816   InitCommonControls();\r
817 \r
818   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
819   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
820   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
821 \r
822   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
823 \r
824   while (GetMessage(&msg, /* message structure */\r
825                     NULL, /* handle of window receiving the message */\r
826                     0,    /* lowest message to examine */\r
827                     0))   /* highest message to examine */\r
828     {\r
829         HandleMessage(&msg);\r
830     }\r
831 \r
832 \r
833   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
834 }\r
835 \r
836 static void\r
837 HandleMessage (MSG *message)\r
838 {\r
839     MSG msg = *message;\r
840 \r
841       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
842         // [HGM] navigate: switch between all windows with tab\r
843         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
844         int i, currentElement = 0;\r
845 \r
846         // first determine what element of the chain we come from (if any)\r
847         if(appData.icsActive) {\r
848             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
849             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
850         }\r
851         if(engineOutputDialog && EngineOutputIsUp()) {\r
852             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
853             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
854         }\r
855         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
856             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
857         }\r
858         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
859         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
860         if(msg.hwnd == e1)                 currentElement = 2; else\r
861         if(msg.hwnd == e2)                 currentElement = 3; else\r
862         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
863         if(msg.hwnd == mh)                currentElement = 4; else\r
864         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
865         if(msg.hwnd == hText)  currentElement = 5; else\r
866         if(msg.hwnd == hInput) currentElement = 6; else\r
867         for (i = 0; i < N_BUTTONS; i++) {\r
868             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
869         }\r
870 \r
871         // determine where to go to\r
872         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
873           do {\r
874             currentElement = (currentElement + direction) % 7;\r
875             switch(currentElement) {\r
876                 case 0:\r
877                   h = hwndMain; break; // passing this case always makes the loop exit\r
878                 case 1:\r
879                   h = buttonDesc[0].hwnd; break; // could be NULL\r
880                 case 2:\r
881                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
882                   h = e1; break;\r
883                 case 3:\r
884                   if(!EngineOutputIsUp()) continue;\r
885                   h = e2; break;\r
886                 case 4:\r
887                   if(!MoveHistoryIsUp()) continue;\r
888                   h = mh; break;\r
889 //              case 6: // input to eval graph does not seem to get here!\r
890 //                if(!EvalGraphIsUp()) continue;\r
891 //                h = evalGraphDialog; break;\r
892                 case 5:\r
893                   if(!appData.icsActive) continue;\r
894                   SAY("display");\r
895                   h = hText; break;\r
896                 case 6:\r
897                   if(!appData.icsActive) continue;\r
898                   SAY("input");\r
899                   h = hInput; break;\r
900             }\r
901           } while(h == 0);\r
902 \r
903           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
904           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
905           SetFocus(h);\r
906 \r
907           return; // this message now has been processed\r
908         }\r
909       }\r
910 \r
911       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
912           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
913           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
914           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
915           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
916           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
917           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
918           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
919           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
920           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
921         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
922         for(i=0; i<MAX_CHAT; i++) \r
923             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
924                 done = 1; break;\r
925         }\r
926         if(done) return; // [HGM] chat: end patch\r
927         TranslateMessage(&msg); /* Translates virtual key codes */\r
928         DispatchMessage(&msg);  /* Dispatches message to window */\r
929       }\r
930 }\r
931 \r
932 void\r
933 DoEvents ()\r
934 { /* Dispatch pending messages */\r
935   MSG msg;\r
936   while (PeekMessage(&msg, /* message structure */\r
937                      NULL, /* handle of window receiving the message */\r
938                      0,    /* lowest message to examine */\r
939                      0,    /* highest message to examine */\r
940                      PM_REMOVE))\r
941     {\r
942         HandleMessage(&msg);\r
943     }\r
944 }\r
945 \r
946 /*---------------------------------------------------------------------------*\\r
947  *\r
948  * Initialization functions\r
949  *\r
950 \*---------------------------------------------------------------------------*/\r
951 \r
952 void\r
953 SetUserLogo()\r
954 {   // update user logo if necessary\r
955     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
956 \r
957     if(appData.autoLogo) {\r
958           curName = UserName();\r
959           if(strcmp(curName, oldUserName)) {\r
960                 GetCurrentDirectory(MSG_SIZ, dir);\r
961                 SetCurrentDirectory(installDir);\r
962                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
963                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
964                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
965                 if(userLogo == NULL)\r
966                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
967                 SetCurrentDirectory(dir); /* return to prev directory */\r
968           }\r
969     }\r
970 }\r
971 \r
972 BOOL\r
973 InitApplication(HINSTANCE hInstance)\r
974 {\r
975   WNDCLASS wc;\r
976 \r
977   /* Fill in window class structure with parameters that describe the */\r
978   /* main window. */\r
979 \r
980   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
981   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
982   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
983   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
984   wc.hInstance     = hInstance;         /* Owner of this class */\r
985   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
986   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
987   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
988   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
989   wc.lpszClassName = szAppName;                 /* Name to register as */\r
990 \r
991   /* Register the window class and return success/failure code. */\r
992   if (!RegisterClass(&wc)) return FALSE;\r
993 \r
994   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
995   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
996   wc.cbClsExtra    = 0;\r
997   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
998   wc.hInstance     = hInstance;\r
999   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
1000   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1001   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1002   wc.lpszMenuName  = NULL;\r
1003   wc.lpszClassName = szConsoleName;\r
1004 \r
1005   if (!RegisterClass(&wc)) return FALSE;\r
1006   return TRUE;\r
1007 }\r
1008 \r
1009 \r
1010 /* Set by InitInstance, used by EnsureOnScreen */\r
1011 int screenHeight, screenWidth;\r
1012 RECT screenGeometry;\r
1013 \r
1014 void\r
1015 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1016 {\r
1017 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1018   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1019   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1020   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1021   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1022   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1023 }\r
1024 \r
1025 VOID\r
1026 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1027 {\r
1028   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1029   GetCurrentDirectory(MSG_SIZ, dir);\r
1030   SetCurrentDirectory(installDir);\r
1031   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1032       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1033 \r
1034       if (cps->programLogo == NULL && appData.debugMode) {\r
1035           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1036       }\r
1037   } else if(appData.autoLogo) {\r
1038       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1039         char *opponent = "";\r
1040         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1041         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1042         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1043         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1044             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1045             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1046         }\r
1047       } else\r
1048       if(appData.directory[n] && appData.directory[n][0]) {\r
1049         SetCurrentDirectory(appData.directory[n]);\r
1050         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1051       }\r
1052   }\r
1053   SetCurrentDirectory(dir); /* return to prev directory */\r
1054 }\r
1055 \r
1056 VOID\r
1057 InitTextures()\r
1058 {\r
1059   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1060   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1061   \r
1062   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1063       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1064       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1065       liteBackTextureMode = appData.liteBackTextureMode;\r
1066 \r
1067       if (liteBackTexture == NULL && appData.debugMode) {\r
1068           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1069       }\r
1070   }\r
1071   \r
1072   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1073       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1074       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1075       darkBackTextureMode = appData.darkBackTextureMode;\r
1076 \r
1077       if (darkBackTexture == NULL && appData.debugMode) {\r
1078           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1079       }\r
1080   }\r
1081 }\r
1082 \r
1083 #ifndef SM_CXVIRTUALSCREEN\r
1084 #define SM_CXVIRTUALSCREEN 78\r
1085 #endif\r
1086 #ifndef SM_CYVIRTUALSCREEN\r
1087 #define SM_CYVIRTUALSCREEN 79\r
1088 #endif\r
1089 #ifndef SM_XVIRTUALSCREEN \r
1090 #define SM_XVIRTUALSCREEN 76\r
1091 #endif\r
1092 #ifndef SM_YVIRTUALSCREEN \r
1093 #define SM_YVIRTUALSCREEN 77\r
1094 #endif\r
1095 \r
1096 VOID\r
1097 InitGeometry()\r
1098 {\r
1099   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1100   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1101   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1102   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1103   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1104   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1105   screenGeometry.right = screenGeometry.left + screenWidth;\r
1106   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1107 }\r
1108 \r
1109 BOOL\r
1110 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1111 {\r
1112   HWND hwnd; /* Main window handle. */\r
1113   int ibs;\r
1114   WINDOWPLACEMENT wp;\r
1115   char *filepart;\r
1116 \r
1117   hInst = hInstance;    /* Store instance handle in our global variable */\r
1118   programName = szAppName;\r
1119 \r
1120   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1121     *filepart = NULLCHAR;\r
1122     SetCurrentDirectory(installDir);\r
1123   } else {\r
1124     GetCurrentDirectory(MSG_SIZ, installDir);\r
1125   }\r
1126   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1127   InitGeometry();\r
1128   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1129   /* xboard, and older WinBoards, controlled the move sound with the\r
1130      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1131      always turn the option on (so that the backend will call us),\r
1132      then let the user turn the sound off by setting it to silence if\r
1133      desired.  To accommodate old winboard.ini files saved by old\r
1134      versions of WinBoard, we also turn off the sound if the option\r
1135      was initially set to false. [HGM] taken out of InitAppData */\r
1136   if (!appData.ringBellAfterMoves) {\r
1137     sounds[(int)SoundMove].name = strdup("");\r
1138     appData.ringBellAfterMoves = TRUE;\r
1139   }\r
1140   if (appData.debugMode) {\r
1141     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1142     setbuf(debugFP, NULL);\r
1143   }\r
1144 \r
1145   LoadLanguageFile(appData.language);\r
1146 \r
1147   InitBackEnd1();\r
1148 \r
1149 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1150 //  InitEngineUCI( installDir, &second );\r
1151 \r
1152   /* Create a main window for this application instance. */\r
1153   hwnd = CreateWindow(szAppName, szTitle,\r
1154                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1155                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1156                       NULL, NULL, hInstance, NULL);\r
1157   hwndMain = hwnd;\r
1158 \r
1159   /* If window could not be created, return "failure" */\r
1160   if (!hwnd) {\r
1161     return (FALSE);\r
1162   }\r
1163 \r
1164   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1165   LoadLogo(&first, 0, FALSE);\r
1166   LoadLogo(&second, 1, appData.icsActive);\r
1167 \r
1168   SetUserLogo();\r
1169 \r
1170   iconWhite = LoadIcon(hInstance, "icon_white");\r
1171   iconBlack = LoadIcon(hInstance, "icon_black");\r
1172   iconCurrent = iconWhite;\r
1173   InitDrawingColors();\r
1174 \r
1175   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1176   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1177     /* Compute window size for each board size, and use the largest\r
1178        size that fits on this screen as the default. */\r
1179     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1180     if (boardSize == (BoardSize)-1 &&\r
1181         winH <= screenHeight\r
1182            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1183         && winW <= screenWidth) {\r
1184       boardSize = (BoardSize)ibs;\r
1185     }\r
1186   }\r
1187 \r
1188   InitDrawingSizes(boardSize, 0);\r
1189   RecentEngineMenu(appData.recentEngineList);\r
1190   InitMenuChecks();\r
1191   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1192 \r
1193   /* [AS] Load textures if specified */\r
1194   InitTextures();\r
1195 \r
1196   mysrandom( (unsigned) time(NULL) );\r
1197 \r
1198   /* [AS] Restore layout */\r
1199   if( wpMoveHistory.visible ) {\r
1200       MoveHistoryPopUp();\r
1201   }\r
1202 \r
1203   if( wpEvalGraph.visible ) {\r
1204       EvalGraphPopUp();\r
1205   }\r
1206 \r
1207   if( wpEngineOutput.visible ) {\r
1208       EngineOutputPopUp();\r
1209   }\r
1210 \r
1211   /* Make the window visible; update its client area; and return "success" */\r
1212   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1213   wp.length = sizeof(WINDOWPLACEMENT);\r
1214   wp.flags = 0;\r
1215   wp.showCmd = nCmdShow;\r
1216   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1217   wp.rcNormalPosition.left = wpMain.x;\r
1218   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1219   wp.rcNormalPosition.top = wpMain.y;\r
1220   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1221   SetWindowPlacement(hwndMain, &wp);\r
1222 \r
1223   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1224 \r
1225   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1226                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1227 \r
1228   if (hwndConsole) {\r
1229 #if AOT_CONSOLE\r
1230     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1231                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1232 #endif\r
1233     ShowWindow(hwndConsole, nCmdShow);\r
1234     SetActiveWindow(hwndConsole);\r
1235   }\r
1236   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1237   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1238 \r
1239   return TRUE;\r
1240 \r
1241 }\r
1242 \r
1243 VOID\r
1244 InitMenuChecks()\r
1245 {\r
1246   HMENU hmenu = GetMenu(hwndMain);\r
1247 \r
1248   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1249                         MF_BYCOMMAND|((appData.icsActive &&\r
1250                                        *appData.icsCommPort != NULLCHAR) ?\r
1251                                       MF_ENABLED : MF_GRAYED));\r
1252   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1253                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1254                                      MF_CHECKED : MF_UNCHECKED));\r
1255   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1256 }\r
1257 \r
1258 //---------------------------------------------------------------------------------------------------------\r
1259 \r
1260 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1261 #define XBOARD FALSE\r
1262 \r
1263 #define OPTCHAR "/"\r
1264 #define SEPCHAR "="\r
1265 #define TOPLEVEL 0\r
1266 \r
1267 #include "args.h"\r
1268 \r
1269 // front-end part of option handling\r
1270 \r
1271 VOID\r
1272 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1273 {\r
1274   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1275   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1276   DeleteDC(hdc);\r
1277   lf->lfWidth = 0;\r
1278   lf->lfEscapement = 0;\r
1279   lf->lfOrientation = 0;\r
1280   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1281   lf->lfItalic = mfp->italic;\r
1282   lf->lfUnderline = mfp->underline;\r
1283   lf->lfStrikeOut = mfp->strikeout;\r
1284   lf->lfCharSet = mfp->charset;\r
1285   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1286 \r
1287   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1288   lf->lfQuality = DEFAULT_QUALITY;\r
1289   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1290     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1291 }\r
1292 \r
1293 void\r
1294 CreateFontInMF(MyFont *mf)\r
1295\r
1296   LFfromMFP(&mf->lf, &mf->mfp);\r
1297   if (mf->hf) DeleteObject(mf->hf);\r
1298   mf->hf = CreateFontIndirect(&mf->lf);\r
1299 }\r
1300 \r
1301 // [HGM] This platform-dependent table provides the location for storing the color info\r
1302 void *\r
1303 colorVariable[] = {\r
1304   &whitePieceColor, \r
1305   &blackPieceColor, \r
1306   &lightSquareColor,\r
1307   &darkSquareColor, \r
1308   &highlightSquareColor,\r
1309   &premoveHighlightColor,\r
1310   NULL,\r
1311   &consoleBackgroundColor,\r
1312   &appData.fontForeColorWhite,\r
1313   &appData.fontBackColorWhite,\r
1314   &appData.fontForeColorBlack,\r
1315   &appData.fontBackColorBlack,\r
1316   &appData.evalHistColorWhite,\r
1317   &appData.evalHistColorBlack,\r
1318   &appData.highlightArrowColor,\r
1319 };\r
1320 \r
1321 /* Command line font name parser.  NULL name means do nothing.\r
1322    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1323    For backward compatibility, syntax without the colon is also\r
1324    accepted, but font names with digits in them won't work in that case.\r
1325 */\r
1326 VOID\r
1327 ParseFontName(char *name, MyFontParams *mfp)\r
1328 {\r
1329   char *p, *q;\r
1330   if (name == NULL) return;\r
1331   p = name;\r
1332   q = strchr(p, ':');\r
1333   if (q) {\r
1334     if (q - p >= sizeof(mfp->faceName))\r
1335       ExitArgError(_("Font name too long:"), name, TRUE);\r
1336     memcpy(mfp->faceName, p, q - p);\r
1337     mfp->faceName[q - p] = NULLCHAR;\r
1338     p = q + 1;\r
1339   } else {\r
1340     q = mfp->faceName;\r
1341 \r
1342     while (*p && !isdigit(*p)) {\r
1343       *q++ = *p++;\r
1344       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1345         ExitArgError(_("Font name too long:"), name, TRUE);\r
1346     }\r
1347     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1348     *q = NULLCHAR;\r
1349   }\r
1350   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1351   mfp->pointSize = (float) atof(p);\r
1352   mfp->bold = (strchr(p, 'b') != NULL);\r
1353   mfp->italic = (strchr(p, 'i') != NULL);\r
1354   mfp->underline = (strchr(p, 'u') != NULL);\r
1355   mfp->strikeout = (strchr(p, 's') != NULL);\r
1356   mfp->charset = DEFAULT_CHARSET;\r
1357   q = strchr(p, 'c');\r
1358   if (q)\r
1359     mfp->charset = (BYTE) atoi(q+1);\r
1360 }\r
1361 \r
1362 void\r
1363 ParseFont(char *name, int number)\r
1364 { // wrapper to shield back-end from 'font'\r
1365   ParseFontName(name, &font[boardSize][number]->mfp);\r
1366 }\r
1367 \r
1368 void\r
1369 SetFontDefaults()\r
1370 { // in WB  we have a 2D array of fonts; this initializes their description\r
1371   int i, j;\r
1372   /* Point font array elements to structures and\r
1373      parse default font names */\r
1374   for (i=0; i<NUM_FONTS; i++) {\r
1375     for (j=0; j<NUM_SIZES; j++) {\r
1376       font[j][i] = &fontRec[j][i];\r
1377       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1378     }\r
1379   }\r
1380 }\r
1381 \r
1382 void\r
1383 CreateFonts()\r
1384 { // here we create the actual fonts from the selected descriptions\r
1385   int i, j;\r
1386   for (i=0; i<NUM_FONTS; i++) {\r
1387     for (j=0; j<NUM_SIZES; j++) {\r
1388       CreateFontInMF(font[j][i]);\r
1389     }\r
1390   }\r
1391 }\r
1392 /* Color name parser.\r
1393    X version accepts X color names, but this one\r
1394    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1395 COLORREF\r
1396 ParseColorName(char *name)\r
1397 {\r
1398   int red, green, blue, count;\r
1399   char buf[MSG_SIZ];\r
1400 \r
1401   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1402   if (count != 3) {\r
1403     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1404       &red, &green, &blue);\r
1405   }\r
1406   if (count != 3) {\r
1407     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1408     DisplayError(buf, 0);\r
1409     return RGB(0, 0, 0);\r
1410   }\r
1411   return PALETTERGB(red, green, blue);\r
1412 }\r
1413 \r
1414 void\r
1415 ParseColor(int n, char *name)\r
1416 { // for WinBoard the color is an int, which needs to be derived from the string\r
1417   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1418 }\r
1419 \r
1420 void\r
1421 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1422 {\r
1423   char *e = argValue;\r
1424   int eff = 0;\r
1425 \r
1426   while (*e) {\r
1427     if (*e == 'b')      eff |= CFE_BOLD;\r
1428     else if (*e == 'i') eff |= CFE_ITALIC;\r
1429     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1430     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1431     else if (*e == '#' || isdigit(*e)) break;\r
1432     e++;\r
1433   }\r
1434   *effects = eff;\r
1435   *color   = ParseColorName(e);\r
1436 }\r
1437 \r
1438 void\r
1439 ParseTextAttribs(ColorClass cc, char *s)\r
1440 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1441     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1442     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1443 }\r
1444 \r
1445 void\r
1446 ParseBoardSize(void *addr, char *name)\r
1447 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1448   BoardSize bs = SizeTiny;\r
1449   while (sizeInfo[bs].name != NULL) {\r
1450     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1451         *(BoardSize *)addr = bs;\r
1452         return;\r
1453     }\r
1454     bs++;\r
1455   }\r
1456   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1457 }\r
1458 \r
1459 void\r
1460 LoadAllSounds()\r
1461 { // [HGM] import name from appData first\r
1462   ColorClass cc;\r
1463   SoundClass sc;\r
1464   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1465     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1466     textAttribs[cc].sound.data = NULL;\r
1467     MyLoadSound(&textAttribs[cc].sound);\r
1468   }\r
1469   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1470     textAttribs[cc].sound.name = strdup("");\r
1471     textAttribs[cc].sound.data = NULL;\r
1472   }\r
1473   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1474     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1475     sounds[sc].data = NULL;\r
1476     MyLoadSound(&sounds[sc]);\r
1477   }\r
1478 }\r
1479 \r
1480 void\r
1481 SetCommPortDefaults()\r
1482 {\r
1483    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1484   dcb.DCBlength = sizeof(DCB);\r
1485   dcb.BaudRate = 9600;\r
1486   dcb.fBinary = TRUE;\r
1487   dcb.fParity = FALSE;\r
1488   dcb.fOutxCtsFlow = FALSE;\r
1489   dcb.fOutxDsrFlow = FALSE;\r
1490   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1491   dcb.fDsrSensitivity = FALSE;\r
1492   dcb.fTXContinueOnXoff = TRUE;\r
1493   dcb.fOutX = FALSE;\r
1494   dcb.fInX = FALSE;\r
1495   dcb.fNull = FALSE;\r
1496   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1497   dcb.fAbortOnError = FALSE;\r
1498   dcb.ByteSize = 7;\r
1499   dcb.Parity = SPACEPARITY;\r
1500   dcb.StopBits = ONESTOPBIT;\r
1501 }\r
1502 \r
1503 // [HGM] args: these three cases taken out to stay in front-end\r
1504 void\r
1505 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1506 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1507         // while the curent board size determines the element. This system should be ported to XBoard.\r
1508         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1509         int bs;\r
1510         for (bs=0; bs<NUM_SIZES; bs++) {\r
1511           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1512           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1513           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1514             ad->argName, mfp->faceName, mfp->pointSize,\r
1515             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1516             mfp->bold ? "b" : "",\r
1517             mfp->italic ? "i" : "",\r
1518             mfp->underline ? "u" : "",\r
1519             mfp->strikeout ? "s" : "",\r
1520             (int)mfp->charset);\r
1521         }\r
1522       }\r
1523 \r
1524 void\r
1525 ExportSounds()\r
1526 { // [HGM] copy the names from the internal WB variables to appData\r
1527   ColorClass cc;\r
1528   SoundClass sc;\r
1529   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1530     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1531   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1532     (&appData.soundMove)[sc] = sounds[sc].name;\r
1533 }\r
1534 \r
1535 void\r
1536 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1537 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1538         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1539         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1540           (ta->effects & CFE_BOLD) ? "b" : "",\r
1541           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1542           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1543           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1544           (ta->effects) ? " " : "",\r
1545           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1546       }\r
1547 \r
1548 void\r
1549 SaveColor(FILE *f, ArgDescriptor *ad)\r
1550 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1551         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1552         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1553           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1554 }\r
1555 \r
1556 void\r
1557 SaveBoardSize(FILE *f, char *name, void *addr)\r
1558 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1559   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1560 }\r
1561 \r
1562 void\r
1563 ParseCommPortSettings(char *s)\r
1564 { // wrapper to keep dcb from back-end\r
1565   ParseCommSettings(s, &dcb);\r
1566 }\r
1567 \r
1568 void\r
1569 GetWindowCoords()\r
1570 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1571   GetActualPlacement(hwndMain, &wpMain);\r
1572   GetActualPlacement(hwndConsole, &wpConsole);\r
1573   GetActualPlacement(commentDialog, &wpComment);\r
1574   GetActualPlacement(editTagsDialog, &wpTags);\r
1575   GetActualPlacement(gameListDialog, &wpGameList);\r
1576   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1577   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1578   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1579 }\r
1580 \r
1581 void\r
1582 PrintCommPortSettings(FILE *f, char *name)\r
1583 { // wrapper to shield back-end from DCB\r
1584       PrintCommSettings(f, name, &dcb);\r
1585 }\r
1586 \r
1587 int\r
1588 MySearchPath(char *installDir, char *name, char *fullname)\r
1589 {\r
1590   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1591   if(name[0]== '%') {\r
1592     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1593     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1594       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1595       *strchr(buf, '%') = 0;\r
1596       strcat(fullname, getenv(buf));\r
1597       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1598     }\r
1599     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1600     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1601     return (int) strlen(fullname);\r
1602   }\r
1603   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1604 }\r
1605 \r
1606 int\r
1607 MyGetFullPathName(char *name, char *fullname)\r
1608 {\r
1609   char *dummy;\r
1610   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1611 }\r
1612 \r
1613 int\r
1614 MainWindowUp()\r
1615 { // [HGM] args: allows testing if main window is realized from back-end\r
1616   return hwndMain != NULL;\r
1617 }\r
1618 \r
1619 void\r
1620 PopUpStartupDialog()\r
1621 {\r
1622     FARPROC lpProc;\r
1623     \r
1624     LoadLanguageFile(appData.language);\r
1625     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1626     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1627     FreeProcInstance(lpProc);\r
1628 }\r
1629 \r
1630 /*---------------------------------------------------------------------------*\\r
1631  *\r
1632  * GDI board drawing routines\r
1633  *\r
1634 \*---------------------------------------------------------------------------*/\r
1635 \r
1636 /* [AS] Draw square using background texture */\r
1637 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1638 {\r
1639     XFORM   x;\r
1640 \r
1641     if( mode == 0 ) {\r
1642         return; /* Should never happen! */\r
1643     }\r
1644 \r
1645     SetGraphicsMode( dst, GM_ADVANCED );\r
1646 \r
1647     switch( mode ) {\r
1648     case 1:\r
1649         /* Identity */\r
1650         break;\r
1651     case 2:\r
1652         /* X reflection */\r
1653         x.eM11 = -1.0;\r
1654         x.eM12 = 0;\r
1655         x.eM21 = 0;\r
1656         x.eM22 = 1.0;\r
1657         x.eDx = (FLOAT) dw + dx - 1;\r
1658         x.eDy = 0;\r
1659         dx = 0;\r
1660         SetWorldTransform( dst, &x );\r
1661         break;\r
1662     case 3:\r
1663         /* Y reflection */\r
1664         x.eM11 = 1.0;\r
1665         x.eM12 = 0;\r
1666         x.eM21 = 0;\r
1667         x.eM22 = -1.0;\r
1668         x.eDx = 0;\r
1669         x.eDy = (FLOAT) dh + dy - 1;\r
1670         dy = 0;\r
1671         SetWorldTransform( dst, &x );\r
1672         break;\r
1673     case 4:\r
1674         /* X/Y flip */\r
1675         x.eM11 = 0;\r
1676         x.eM12 = 1.0;\r
1677         x.eM21 = 1.0;\r
1678         x.eM22 = 0;\r
1679         x.eDx = (FLOAT) dx;\r
1680         x.eDy = (FLOAT) dy;\r
1681         dx = 0;\r
1682         dy = 0;\r
1683         SetWorldTransform( dst, &x );\r
1684         break;\r
1685     }\r
1686 \r
1687     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1688 \r
1689     x.eM11 = 1.0;\r
1690     x.eM12 = 0;\r
1691     x.eM21 = 0;\r
1692     x.eM22 = 1.0;\r
1693     x.eDx = 0;\r
1694     x.eDy = 0;\r
1695     SetWorldTransform( dst, &x );\r
1696 \r
1697     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1698 }\r
1699 \r
1700 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1701 enum {\r
1702     PM_WP = (int) WhitePawn, \r
1703     PM_WN = (int) WhiteKnight, \r
1704     PM_WB = (int) WhiteBishop, \r
1705     PM_WR = (int) WhiteRook, \r
1706     PM_WQ = (int) WhiteQueen, \r
1707     PM_WF = (int) WhiteFerz, \r
1708     PM_WW = (int) WhiteWazir, \r
1709     PM_WE = (int) WhiteAlfil, \r
1710     PM_WM = (int) WhiteMan, \r
1711     PM_WO = (int) WhiteCannon, \r
1712     PM_WU = (int) WhiteUnicorn, \r
1713     PM_WH = (int) WhiteNightrider, \r
1714     PM_WA = (int) WhiteAngel, \r
1715     PM_WC = (int) WhiteMarshall, \r
1716     PM_WAB = (int) WhiteCardinal, \r
1717     PM_WD = (int) WhiteDragon, \r
1718     PM_WL = (int) WhiteLance, \r
1719     PM_WS = (int) WhiteCobra, \r
1720     PM_WV = (int) WhiteFalcon, \r
1721     PM_WSG = (int) WhiteSilver, \r
1722     PM_WG = (int) WhiteGrasshopper, \r
1723     PM_WK = (int) WhiteKing,\r
1724     PM_BP = (int) BlackPawn, \r
1725     PM_BN = (int) BlackKnight, \r
1726     PM_BB = (int) BlackBishop, \r
1727     PM_BR = (int) BlackRook, \r
1728     PM_BQ = (int) BlackQueen, \r
1729     PM_BF = (int) BlackFerz, \r
1730     PM_BW = (int) BlackWazir, \r
1731     PM_BE = (int) BlackAlfil, \r
1732     PM_BM = (int) BlackMan,\r
1733     PM_BO = (int) BlackCannon, \r
1734     PM_BU = (int) BlackUnicorn, \r
1735     PM_BH = (int) BlackNightrider, \r
1736     PM_BA = (int) BlackAngel, \r
1737     PM_BC = (int) BlackMarshall, \r
1738     PM_BG = (int) BlackGrasshopper, \r
1739     PM_BAB = (int) BlackCardinal,\r
1740     PM_BD = (int) BlackDragon,\r
1741     PM_BL = (int) BlackLance,\r
1742     PM_BS = (int) BlackCobra,\r
1743     PM_BV = (int) BlackFalcon,\r
1744     PM_BSG = (int) BlackSilver,\r
1745     PM_BK = (int) BlackKing\r
1746 };\r
1747 \r
1748 static HFONT hPieceFont = NULL;\r
1749 static HBITMAP hPieceMask[(int) EmptySquare];\r
1750 static HBITMAP hPieceFace[(int) EmptySquare];\r
1751 static int fontBitmapSquareSize = 0;\r
1752 static char pieceToFontChar[(int) EmptySquare] =\r
1753                               { 'p', 'n', 'b', 'r', 'q', \r
1754                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1755                       'k', 'o', 'm', 'v', 't', 'w', \r
1756                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1757                                                               'l' };\r
1758 \r
1759 extern BOOL SetCharTable( char *table, const char * map );\r
1760 /* [HGM] moved to backend.c */\r
1761 \r
1762 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1763 {\r
1764     HBRUSH hbrush;\r
1765     BYTE r1 = GetRValue( color );\r
1766     BYTE g1 = GetGValue( color );\r
1767     BYTE b1 = GetBValue( color );\r
1768     BYTE r2 = r1 / 2;\r
1769     BYTE g2 = g1 / 2;\r
1770     BYTE b2 = b1 / 2;\r
1771     RECT rc;\r
1772 \r
1773     /* Create a uniform background first */\r
1774     hbrush = CreateSolidBrush( color );\r
1775     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1776     FillRect( hdc, &rc, hbrush );\r
1777     DeleteObject( hbrush );\r
1778     \r
1779     if( mode == 1 ) {\r
1780         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1781         int steps = squareSize / 2;\r
1782         int i;\r
1783 \r
1784         for( i=0; i<steps; i++ ) {\r
1785             BYTE r = r1 - (r1-r2) * i / steps;\r
1786             BYTE g = g1 - (g1-g2) * i / steps;\r
1787             BYTE b = b1 - (b1-b2) * i / steps;\r
1788 \r
1789             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1790             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1791             FillRect( hdc, &rc, hbrush );\r
1792             DeleteObject(hbrush);\r
1793         }\r
1794     }\r
1795     else if( mode == 2 ) {\r
1796         /* Diagonal gradient, good more or less for every piece */\r
1797         POINT triangle[3];\r
1798         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1799         HBRUSH hbrush_old;\r
1800         int steps = squareSize;\r
1801         int i;\r
1802 \r
1803         triangle[0].x = squareSize - steps;\r
1804         triangle[0].y = squareSize;\r
1805         triangle[1].x = squareSize;\r
1806         triangle[1].y = squareSize;\r
1807         triangle[2].x = squareSize;\r
1808         triangle[2].y = squareSize - steps;\r
1809 \r
1810         for( i=0; i<steps; i++ ) {\r
1811             BYTE r = r1 - (r1-r2) * i / steps;\r
1812             BYTE g = g1 - (g1-g2) * i / steps;\r
1813             BYTE b = b1 - (b1-b2) * i / steps;\r
1814 \r
1815             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1816             hbrush_old = SelectObject( hdc, hbrush );\r
1817             Polygon( hdc, triangle, 3 );\r
1818             SelectObject( hdc, hbrush_old );\r
1819             DeleteObject(hbrush);\r
1820             triangle[0].x++;\r
1821             triangle[2].y++;\r
1822         }\r
1823 \r
1824         SelectObject( hdc, hpen );\r
1825     }\r
1826 }\r
1827 \r
1828 /*\r
1829     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1830     seems to work ok. The main problem here is to find the "inside" of a chess\r
1831     piece: follow the steps as explained below.\r
1832 */\r
1833 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1834 {\r
1835     HBITMAP hbm;\r
1836     HBITMAP hbm_old;\r
1837     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1838     RECT rc;\r
1839     SIZE sz;\r
1840 \r
1841 \r
1842     POINT pt;\r
1843     int backColor = whitePieceColor; \r
1844     int foreColor = blackPieceColor;\r
1845     \r
1846     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1847         backColor = appData.fontBackColorWhite;\r
1848         foreColor = appData.fontForeColorWhite;\r
1849     }\r
1850     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1851         backColor = appData.fontBackColorBlack;\r
1852         foreColor = appData.fontForeColorBlack;\r
1853     }\r
1854 \r
1855     /* Mask */\r
1856     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1857 \r
1858     hbm_old = SelectObject( hdc, hbm );\r
1859 \r
1860     rc.left = 0;\r
1861     rc.top = 0;\r
1862     rc.right = squareSize;\r
1863     rc.bottom = squareSize;\r
1864 \r
1865     /* Step 1: background is now black */\r
1866     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1867 \r
1868     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1869 \r
1870     pt.x = (squareSize - sz.cx) / 2;\r
1871     pt.y = (squareSize - sz.cy) / 2;\r
1872 \r
1873     SetBkMode( hdc, TRANSPARENT );\r
1874     SetTextColor( hdc, chroma );\r
1875     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1876     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1877 \r
1878     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1879     /* Step 3: the area outside the piece is filled with white */\r
1880 //    FloodFill( hdc, 0, 0, chroma );\r
1881     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1882     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1883     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1884     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1885     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1886     /* \r
1887         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1888         but if the start point is not inside the piece we're lost!\r
1889         There should be a better way to do this... if we could create a region or path\r
1890         from the fill operation we would be fine for example.\r
1891     */\r
1892 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1893     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1894 \r
1895     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1896         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1897         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1898 \r
1899         SelectObject( dc2, bm2 );\r
1900         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1901         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1902         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1903         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1904         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1905 \r
1906         DeleteDC( dc2 );\r
1907         DeleteObject( bm2 );\r
1908     }\r
1909 \r
1910     SetTextColor( hdc, 0 );\r
1911     /* \r
1912         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1913         draw the piece again in black for safety.\r
1914     */\r
1915     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1916 \r
1917     SelectObject( hdc, hbm_old );\r
1918 \r
1919     if( hPieceMask[index] != NULL ) {\r
1920         DeleteObject( hPieceMask[index] );\r
1921     }\r
1922 \r
1923     hPieceMask[index] = hbm;\r
1924 \r
1925     /* Face */\r
1926     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1927 \r
1928     SelectObject( hdc, hbm );\r
1929 \r
1930     {\r
1931         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1932         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1933         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1934 \r
1935         SelectObject( dc1, hPieceMask[index] );\r
1936         SelectObject( dc2, bm2 );\r
1937         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1938         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1939         \r
1940         /* \r
1941             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1942             the piece background and deletes (makes transparent) the rest.\r
1943             Thanks to that mask, we are free to paint the background with the greates\r
1944             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1945             We use this, to make gradients and give the pieces a "roundish" look.\r
1946         */\r
1947         SetPieceBackground( hdc, backColor, 2 );\r
1948         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1949 \r
1950         DeleteDC( dc2 );\r
1951         DeleteDC( dc1 );\r
1952         DeleteObject( bm2 );\r
1953     }\r
1954 \r
1955     SetTextColor( hdc, foreColor );\r
1956     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1957 \r
1958     SelectObject( hdc, hbm_old );\r
1959 \r
1960     if( hPieceFace[index] != NULL ) {\r
1961         DeleteObject( hPieceFace[index] );\r
1962     }\r
1963 \r
1964     hPieceFace[index] = hbm;\r
1965 }\r
1966 \r
1967 static int TranslatePieceToFontPiece( int piece )\r
1968 {\r
1969     switch( piece ) {\r
1970     case BlackPawn:\r
1971         return PM_BP;\r
1972     case BlackKnight:\r
1973         return PM_BN;\r
1974     case BlackBishop:\r
1975         return PM_BB;\r
1976     case BlackRook:\r
1977         return PM_BR;\r
1978     case BlackQueen:\r
1979         return PM_BQ;\r
1980     case BlackKing:\r
1981         return PM_BK;\r
1982     case WhitePawn:\r
1983         return PM_WP;\r
1984     case WhiteKnight:\r
1985         return PM_WN;\r
1986     case WhiteBishop:\r
1987         return PM_WB;\r
1988     case WhiteRook:\r
1989         return PM_WR;\r
1990     case WhiteQueen:\r
1991         return PM_WQ;\r
1992     case WhiteKing:\r
1993         return PM_WK;\r
1994 \r
1995     case BlackAngel:\r
1996         return PM_BA;\r
1997     case BlackMarshall:\r
1998         return PM_BC;\r
1999     case BlackFerz:\r
2000         return PM_BF;\r
2001     case BlackNightrider:\r
2002         return PM_BH;\r
2003     case BlackAlfil:\r
2004         return PM_BE;\r
2005     case BlackWazir:\r
2006         return PM_BW;\r
2007     case BlackUnicorn:\r
2008         return PM_BU;\r
2009     case BlackCannon:\r
2010         return PM_BO;\r
2011     case BlackGrasshopper:\r
2012         return PM_BG;\r
2013     case BlackMan:\r
2014         return PM_BM;\r
2015     case BlackSilver:\r
2016         return PM_BSG;\r
2017     case BlackLance:\r
2018         return PM_BL;\r
2019     case BlackFalcon:\r
2020         return PM_BV;\r
2021     case BlackCobra:\r
2022         return PM_BS;\r
2023     case BlackCardinal:\r
2024         return PM_BAB;\r
2025     case BlackDragon:\r
2026         return PM_BD;\r
2027 \r
2028     case WhiteAngel:\r
2029         return PM_WA;\r
2030     case WhiteMarshall:\r
2031         return PM_WC;\r
2032     case WhiteFerz:\r
2033         return PM_WF;\r
2034     case WhiteNightrider:\r
2035         return PM_WH;\r
2036     case WhiteAlfil:\r
2037         return PM_WE;\r
2038     case WhiteWazir:\r
2039         return PM_WW;\r
2040     case WhiteUnicorn:\r
2041         return PM_WU;\r
2042     case WhiteCannon:\r
2043         return PM_WO;\r
2044     case WhiteGrasshopper:\r
2045         return PM_WG;\r
2046     case WhiteMan:\r
2047         return PM_WM;\r
2048     case WhiteSilver:\r
2049         return PM_WSG;\r
2050     case WhiteLance:\r
2051         return PM_WL;\r
2052     case WhiteFalcon:\r
2053         return PM_WV;\r
2054     case WhiteCobra:\r
2055         return PM_WS;\r
2056     case WhiteCardinal:\r
2057         return PM_WAB;\r
2058     case WhiteDragon:\r
2059         return PM_WD;\r
2060     }\r
2061 \r
2062     return 0;\r
2063 }\r
2064 \r
2065 void CreatePiecesFromFont()\r
2066 {\r
2067     LOGFONT lf;\r
2068     HDC hdc_window = NULL;\r
2069     HDC hdc = NULL;\r
2070     HFONT hfont_old;\r
2071     int fontHeight;\r
2072     int i;\r
2073 \r
2074     if( fontBitmapSquareSize < 0 ) {\r
2075         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2076         return;\r
2077     }\r
2078 \r
2079     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2080             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2081         fontBitmapSquareSize = -1;\r
2082         return;\r
2083     }\r
2084 \r
2085     if( fontBitmapSquareSize != squareSize ) {\r
2086         hdc_window = GetDC( hwndMain );\r
2087         hdc = CreateCompatibleDC( hdc_window );\r
2088 \r
2089         if( hPieceFont != NULL ) {\r
2090             DeleteObject( hPieceFont );\r
2091         }\r
2092         else {\r
2093             for( i=0; i<=(int)BlackKing; i++ ) {\r
2094                 hPieceMask[i] = NULL;\r
2095                 hPieceFace[i] = NULL;\r
2096             }\r
2097         }\r
2098 \r
2099         fontHeight = 75;\r
2100 \r
2101         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2102             fontHeight = appData.fontPieceSize;\r
2103         }\r
2104 \r
2105         fontHeight = (fontHeight * squareSize) / 100;\r
2106 \r
2107         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2108         lf.lfWidth = 0;\r
2109         lf.lfEscapement = 0;\r
2110         lf.lfOrientation = 0;\r
2111         lf.lfWeight = FW_NORMAL;\r
2112         lf.lfItalic = 0;\r
2113         lf.lfUnderline = 0;\r
2114         lf.lfStrikeOut = 0;\r
2115         lf.lfCharSet = DEFAULT_CHARSET;\r
2116         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2117         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2118         lf.lfQuality = PROOF_QUALITY;\r
2119         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2120         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2121         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2122 \r
2123         hPieceFont = CreateFontIndirect( &lf );\r
2124 \r
2125         if( hPieceFont == NULL ) {\r
2126             fontBitmapSquareSize = -2;\r
2127         }\r
2128         else {\r
2129             /* Setup font-to-piece character table */\r
2130             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2131                 /* No (or wrong) global settings, try to detect the font */\r
2132                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2133                     /* Alpha */\r
2134                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2135                 }\r
2136                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2137                     /* DiagramTT* family */\r
2138                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2139                 }\r
2140                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2141                     /* Fairy symbols */\r
2142                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2143                 }\r
2144                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2145                     /* Good Companion (Some characters get warped as literal :-( */\r
2146                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2147                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2148                     SetCharTable(pieceToFontChar, s);\r
2149                 }\r
2150                 else {\r
2151                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2152                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2153                 }\r
2154             }\r
2155 \r
2156             /* Create bitmaps */\r
2157             hfont_old = SelectObject( hdc, hPieceFont );\r
2158             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2159                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2160                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2161 \r
2162             SelectObject( hdc, hfont_old );\r
2163 \r
2164             fontBitmapSquareSize = squareSize;\r
2165         }\r
2166     }\r
2167 \r
2168     if( hdc != NULL ) {\r
2169         DeleteDC( hdc );\r
2170     }\r
2171 \r
2172     if( hdc_window != NULL ) {\r
2173         ReleaseDC( hwndMain, hdc_window );\r
2174     }\r
2175 }\r
2176 \r
2177 HBITMAP\r
2178 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2179 {\r
2180   char name[128], buf[MSG_SIZ];\r
2181 \r
2182     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2183   if(appData.pieceDirectory[0]) {\r
2184     HBITMAP res;\r
2185     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2186     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2187     if(res) return res;\r
2188   }\r
2189   if (gameInfo.event &&\r
2190       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2191       strcmp(name, "k80s") == 0) {\r
2192     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2193   }\r
2194   return LoadBitmap(hinst, name);\r
2195 }\r
2196 \r
2197 \r
2198 /* Insert a color into the program's logical palette\r
2199    structure.  This code assumes the given color is\r
2200    the result of the RGB or PALETTERGB macro, and it\r
2201    knows how those macros work (which is documented).\r
2202 */\r
2203 VOID\r
2204 InsertInPalette(COLORREF color)\r
2205 {\r
2206   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2207 \r
2208   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2209     DisplayFatalError(_("Too many colors"), 0, 1);\r
2210     pLogPal->palNumEntries--;\r
2211     return;\r
2212   }\r
2213 \r
2214   pe->peFlags = (char) 0;\r
2215   pe->peRed = (char) (0xFF & color);\r
2216   pe->peGreen = (char) (0xFF & (color >> 8));\r
2217   pe->peBlue = (char) (0xFF & (color >> 16));\r
2218   return;\r
2219 }\r
2220 \r
2221 \r
2222 VOID\r
2223 InitDrawingColors()\r
2224 {\r
2225   int i;\r
2226   if (pLogPal == NULL) {\r
2227     /* Allocate enough memory for a logical palette with\r
2228      * PALETTESIZE entries and set the size and version fields\r
2229      * of the logical palette structure.\r
2230      */\r
2231     pLogPal = (NPLOGPALETTE)\r
2232       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2233                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2234     pLogPal->palVersion    = 0x300;\r
2235   }\r
2236   pLogPal->palNumEntries = 0;\r
2237 \r
2238   InsertInPalette(lightSquareColor);\r
2239   InsertInPalette(darkSquareColor);\r
2240   InsertInPalette(whitePieceColor);\r
2241   InsertInPalette(blackPieceColor);\r
2242   InsertInPalette(highlightSquareColor);\r
2243   InsertInPalette(premoveHighlightColor);\r
2244 \r
2245   /*  create a logical color palette according the information\r
2246    *  in the LOGPALETTE structure.\r
2247    */\r
2248   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2249 \r
2250   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2251   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2252   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2253   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2254   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2255   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2256   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2257     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2258 \r
2259    /* [AS] Force rendering of the font-based pieces */\r
2260   if( fontBitmapSquareSize > 0 ) {\r
2261     fontBitmapSquareSize = 0;\r
2262   }\r
2263 }\r
2264 \r
2265 \r
2266 int\r
2267 BoardWidth(int boardSize, int n)\r
2268 { /* [HGM] argument n added to allow different width and height */\r
2269   int lineGap = sizeInfo[boardSize].lineGap;\r
2270 \r
2271   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2272       lineGap = appData.overrideLineGap;\r
2273   }\r
2274 \r
2275   return (n + 1) * lineGap +\r
2276           n * sizeInfo[boardSize].squareSize;\r
2277 }\r
2278 \r
2279 /* Respond to board resize by dragging edge */\r
2280 VOID\r
2281 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2282 {\r
2283   BoardSize newSize = NUM_SIZES - 1;\r
2284   static int recurse = 0;\r
2285   if (IsIconic(hwndMain)) return;\r
2286   if (recurse > 0) return;\r
2287   recurse++;\r
2288   while (newSize > 0) {\r
2289         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2290         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2291            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2292     newSize--;\r
2293   } \r
2294   boardSize = newSize;\r
2295   InitDrawingSizes(boardSize, flags);\r
2296   recurse--;\r
2297 }\r
2298 \r
2299 \r
2300 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2301 \r
2302 VOID\r
2303 InitDrawingSizes(BoardSize boardSize, int flags)\r
2304 {\r
2305   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2306   ChessSquare piece;\r
2307   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2308   HDC hdc;\r
2309   SIZE clockSize, messageSize;\r
2310   HFONT oldFont;\r
2311   char buf[MSG_SIZ];\r
2312   char *str;\r
2313   HMENU hmenu = GetMenu(hwndMain);\r
2314   RECT crect, wrect, oldRect;\r
2315   int offby;\r
2316   LOGBRUSH logbrush;\r
2317   VariantClass v = gameInfo.variant;\r
2318 \r
2319   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2320   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2321 \r
2322   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2323   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2324   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2325   oldBoardSize = boardSize;\r
2326 \r
2327   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2328   { // correct board size to one where built-in pieces exist\r
2329     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2330        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2331 \r
2332       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2333       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2334       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2335       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2336       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2337                                    boardSize = SizeMiddling;\r
2338     }\r
2339   }\r
2340   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2341 \r
2342   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2343   oldRect.top = wpMain.y;\r
2344   oldRect.right = wpMain.x + wpMain.width;\r
2345   oldRect.bottom = wpMain.y + wpMain.height;\r
2346 \r
2347   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2348   smallLayout = sizeInfo[boardSize].smallLayout;\r
2349   squareSize = sizeInfo[boardSize].squareSize;\r
2350   lineGap = sizeInfo[boardSize].lineGap;\r
2351   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2352   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2353 \r
2354   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2355       lineGap = appData.overrideLineGap;\r
2356   }\r
2357 \r
2358   if (tinyLayout != oldTinyLayout) {\r
2359     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2360     if (tinyLayout) {\r
2361       style &= ~WS_SYSMENU;\r
2362       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2363                  "&Minimize\tCtrl+F4");\r
2364     } else {\r
2365       style |= WS_SYSMENU;\r
2366       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2367     }\r
2368     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2369 \r
2370     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2371       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2372         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2373     }\r
2374     DrawMenuBar(hwndMain);\r
2375   }\r
2376 \r
2377   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2378   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2379 \r
2380   /* Get text area sizes */\r
2381   hdc = GetDC(hwndMain);\r
2382   if (appData.clockMode) {\r
2383     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2384   } else {\r
2385     snprintf(buf, MSG_SIZ, _("White"));\r
2386   }\r
2387   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2388   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2389   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2390   str = _("We only care about the height here");\r
2391   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2392   SelectObject(hdc, oldFont);\r
2393   ReleaseDC(hwndMain, hdc);\r
2394 \r
2395   /* Compute where everything goes */\r
2396   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2397         /* [HGM] logo: if either logo is on, reserve space for it */\r
2398         logoHeight =  2*clockSize.cy;\r
2399         leftLogoRect.left   = OUTER_MARGIN;\r
2400         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2401         leftLogoRect.top    = OUTER_MARGIN;\r
2402         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2403 \r
2404         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2405         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2406         rightLogoRect.top    = OUTER_MARGIN;\r
2407         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2408 \r
2409 \r
2410     whiteRect.left = leftLogoRect.right;\r
2411     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2412     whiteRect.top = OUTER_MARGIN;\r
2413     whiteRect.bottom = whiteRect.top + logoHeight;\r
2414 \r
2415     blackRect.right = rightLogoRect.left;\r
2416     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2417     blackRect.top = whiteRect.top;\r
2418     blackRect.bottom = whiteRect.bottom;\r
2419   } else {\r
2420     whiteRect.left = OUTER_MARGIN;\r
2421     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2422     whiteRect.top = OUTER_MARGIN;\r
2423     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2424 \r
2425     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2426     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2427     blackRect.top = whiteRect.top;\r
2428     blackRect.bottom = whiteRect.bottom;\r
2429 \r
2430     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2431   }\r
2432 \r
2433   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2434   if (appData.showButtonBar) {\r
2435     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2436       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2437   } else {\r
2438     messageRect.right = OUTER_MARGIN + boardWidth;\r
2439   }\r
2440   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2441   messageRect.bottom = messageRect.top + messageSize.cy;\r
2442 \r
2443   boardRect.left = OUTER_MARGIN;\r
2444   boardRect.right = boardRect.left + boardWidth;\r
2445   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2446   boardRect.bottom = boardRect.top + boardHeight;\r
2447 \r
2448   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2449   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2450   oldTinyLayout = tinyLayout;\r
2451   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2452   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2453     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2454   winW *= 1 + twoBoards;\r
2455   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2456   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2457   wpMain.height = winH; //       without disturbing window attachments\r
2458   GetWindowRect(hwndMain, &wrect);\r
2459   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2460                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2461 \r
2462   // [HGM] placement: let attached windows follow size change.\r
2463   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2464   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2465   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2466   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2467   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2468 \r
2469   /* compensate if menu bar wrapped */\r
2470   GetClientRect(hwndMain, &crect);\r
2471   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2472   wpMain.height += offby;\r
2473   switch (flags) {\r
2474   case WMSZ_TOPLEFT:\r
2475     SetWindowPos(hwndMain, NULL, \r
2476                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2477                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2478     break;\r
2479 \r
2480   case WMSZ_TOPRIGHT:\r
2481   case WMSZ_TOP:\r
2482     SetWindowPos(hwndMain, NULL, \r
2483                  wrect.left, wrect.bottom - wpMain.height, \r
2484                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2485     break;\r
2486 \r
2487   case WMSZ_BOTTOMLEFT:\r
2488   case WMSZ_LEFT:\r
2489     SetWindowPos(hwndMain, NULL, \r
2490                  wrect.right - wpMain.width, wrect.top, \r
2491                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2492     break;\r
2493 \r
2494   case WMSZ_BOTTOMRIGHT:\r
2495   case WMSZ_BOTTOM:\r
2496   case WMSZ_RIGHT:\r
2497   default:\r
2498     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2499                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2500     break;\r
2501   }\r
2502 \r
2503   hwndPause = NULL;\r
2504   for (i = 0; i < N_BUTTONS; i++) {\r
2505     if (buttonDesc[i].hwnd != NULL) {\r
2506       DestroyWindow(buttonDesc[i].hwnd);\r
2507       buttonDesc[i].hwnd = NULL;\r
2508     }\r
2509     if (appData.showButtonBar) {\r
2510       buttonDesc[i].hwnd =\r
2511         CreateWindow("BUTTON", buttonDesc[i].label,\r
2512                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2513                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2514                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2515                      (HMENU) buttonDesc[i].id,\r
2516                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2517       if (tinyLayout) {\r
2518         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2519                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2520                     MAKELPARAM(FALSE, 0));\r
2521       }\r
2522       if (buttonDesc[i].id == IDM_Pause)\r
2523         hwndPause = buttonDesc[i].hwnd;\r
2524       buttonDesc[i].wndproc = (WNDPROC)\r
2525         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2526     }\r
2527   }\r
2528   if (gridPen != NULL) DeleteObject(gridPen);\r
2529   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2530   if (premovePen != NULL) DeleteObject(premovePen);\r
2531   if (lineGap != 0) {\r
2532     logbrush.lbStyle = BS_SOLID;\r
2533     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2534     gridPen =\r
2535       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2536                    lineGap, &logbrush, 0, NULL);\r
2537     logbrush.lbColor = highlightSquareColor;\r
2538     highlightPen =\r
2539       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2540                    lineGap, &logbrush, 0, NULL);\r
2541 \r
2542     logbrush.lbColor = premoveHighlightColor; \r
2543     premovePen =\r
2544       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2545                    lineGap, &logbrush, 0, NULL);\r
2546 \r
2547     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2548     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2549       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2550       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2551         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2552       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2553         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2554       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2555     }\r
2556     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2557       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2558       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2559         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2560         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2561       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2562         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2563       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2564     }\r
2565   }\r
2566 \r
2567   /* [HGM] Licensing requirement */\r
2568 #ifdef GOTHIC\r
2569   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2570 #endif\r
2571 #ifdef FALCON\r
2572   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2573 #endif\r
2574   GothicPopUp( "", VariantNormal);\r
2575 \r
2576 \r
2577 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2578 \r
2579   /* Load piece bitmaps for this board size */\r
2580   for (i=0; i<=2; i++) {\r
2581     for (piece = WhitePawn;\r
2582          (int) piece < (int) BlackPawn;\r
2583          piece = (ChessSquare) ((int) piece + 1)) {\r
2584       if (pieceBitmap[i][piece] != NULL)\r
2585         DeleteObject(pieceBitmap[i][piece]);\r
2586     }\r
2587   }\r
2588 \r
2589   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2590   // Orthodox Chess pieces\r
2591   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2592   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2593   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2594   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2595   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2596   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2597   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2598   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2599   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2600   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2601   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2602   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2603   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2604   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2605   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2606   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2607     // in Shogi, Hijack the unused Queen for Lance\r
2608     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2609     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2610     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2611   } else {\r
2612     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2613     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2614     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2615   }\r
2616 \r
2617   if(squareSize <= 72 && squareSize >= 33) { \r
2618     /* A & C are available in most sizes now */\r
2619     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2620       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2621       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2622       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2623       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2624       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2625       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2626       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2627       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2628       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2629       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2630       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2631       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2632     } else { // Smirf-like\r
2633       if(gameInfo.variant == VariantSChess) {\r
2634         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2635         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2636         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2637       } else {\r
2638         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2639         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2640         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2641       }\r
2642     }\r
2643     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2644       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2645       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2646       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2647     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2648       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2649       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2650       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2651     } else { // WinBoard standard\r
2652       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2653       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2654       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2655     }\r
2656   }\r
2657 \r
2658 \r
2659   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2660     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2661     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2662     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2663     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2664     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2665     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2666     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2667     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2668     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2669     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2670     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2671     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2672     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2673     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2674     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2675     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2676     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2677     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2678     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2679     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2680     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2681     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2682     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2683     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2684     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2685     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2686     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2687     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2688     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2689     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2690     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2691     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2692     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2693 \r
2694     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
2695       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2696       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2697       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2698       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2699       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2700       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2701       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2702       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2703       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2704       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2705       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2706       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2707     } else {\r
2708       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2709       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2710       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2711       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2712       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2713       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2714       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2715       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2716       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2717       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2718       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2719       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2720     }\r
2721 \r
2722   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2723     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2724     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2725     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2726     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2727     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2728     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2729     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2730     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2731     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2732     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2733     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2734     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2735     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2736     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2737   }\r
2738 \r
2739 \r
2740   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2741   /* special Shogi support in this size */\r
2742   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2743       for (piece = WhitePawn;\r
2744            (int) piece < (int) BlackPawn;\r
2745            piece = (ChessSquare) ((int) piece + 1)) {\r
2746         if (pieceBitmap[i][piece] != NULL)\r
2747           DeleteObject(pieceBitmap[i][piece]);\r
2748       }\r
2749     }\r
2750   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2751   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2752   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2753   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2754   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2755   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2756   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2757   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2758   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2759   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2760   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2761   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2762   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2763   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2764   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2765   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2766   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2767   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2768   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2769   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2770   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2771   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2772   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2773   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2774   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2775   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2776   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2777   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2778   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2779   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2780   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2781   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2782   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2783   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2784   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2785   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2786   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2787   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2788   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2789   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2790   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2791   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2792   minorSize = 0;\r
2793   }\r
2794 }\r
2795 \r
2796 HBITMAP\r
2797 PieceBitmap(ChessSquare p, int kind)\r
2798 {\r
2799   if ((int) p >= (int) BlackPawn)\r
2800     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2801 \r
2802   return pieceBitmap[kind][(int) p];\r
2803 }\r
2804 \r
2805 /***************************************************************/\r
2806 \r
2807 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2808 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2809 /*\r
2810 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2811 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2812 */\r
2813 \r
2814 VOID\r
2815 SquareToPos(int row, int column, int * x, int * y)\r
2816 {\r
2817   if (flipView) {\r
2818     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2819     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2820   } else {\r
2821     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2822     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2823   }\r
2824 }\r
2825 \r
2826 VOID\r
2827 DrawCoordsOnDC(HDC hdc)\r
2828 {\r
2829   static char files[] = "0123456789012345678901221098765432109876543210";\r
2830   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2831   char str[2] = { NULLCHAR, NULLCHAR };\r
2832   int oldMode, oldAlign, x, y, start, i;\r
2833   HFONT oldFont;\r
2834   HBRUSH oldBrush;\r
2835 \r
2836   if (!appData.showCoords)\r
2837     return;\r
2838 \r
2839   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2840 \r
2841   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2842   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2843   oldAlign = GetTextAlign(hdc);\r
2844   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2845 \r
2846   y = boardRect.top + lineGap;\r
2847   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2848 \r
2849   if(border) {\r
2850     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2851     x += border - lineGap - 4; y += squareSize - 6;\r
2852   } else\r
2853   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2854   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2855     str[0] = files[start + i];\r
2856     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2857     y += squareSize + lineGap;\r
2858   }\r
2859 \r
2860   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2861 \r
2862   if(border) {\r
2863     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2864     x += -border + 4; y += border - squareSize + 6;\r
2865   } else\r
2866   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2867   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2868     str[0] = ranks[start + i];\r
2869     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2870     x += squareSize + lineGap;\r
2871   }    \r
2872 \r
2873   SelectObject(hdc, oldBrush);\r
2874   SetBkMode(hdc, oldMode);\r
2875   SetTextAlign(hdc, oldAlign);\r
2876   SelectObject(hdc, oldFont);\r
2877 }\r
2878 \r
2879 VOID\r
2880 DrawGridOnDC(HDC hdc)\r
2881 {\r
2882   HPEN oldPen;\r
2883  \r
2884   if (lineGap != 0) {\r
2885     oldPen = SelectObject(hdc, gridPen);\r
2886     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2887     SelectObject(hdc, oldPen);\r
2888   }\r
2889 }\r
2890 \r
2891 #define HIGHLIGHT_PEN 0\r
2892 #define PREMOVE_PEN   1\r
2893 \r
2894 VOID\r
2895 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2896 {\r
2897   int x1, y1;\r
2898   HPEN oldPen, hPen;\r
2899   if (lineGap == 0) return;\r
2900   if (flipView) {\r
2901     x1 = boardRect.left +\r
2902       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2903     y1 = boardRect.top +\r
2904       lineGap/2 + y * (squareSize + lineGap) + border;\r
2905   } else {\r
2906     x1 = boardRect.left +\r
2907       lineGap/2 + x * (squareSize + lineGap) + border;\r
2908     y1 = boardRect.top +\r
2909       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2910   }\r
2911   hPen = pen ? premovePen : highlightPen;\r
2912   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2913   MoveToEx(hdc, x1, y1, NULL);\r
2914   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2915   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2916   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2917   LineTo(hdc, x1, y1);\r
2918   SelectObject(hdc, oldPen);\r
2919 }\r
2920 \r
2921 VOID\r
2922 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2923 {\r
2924   int i;\r
2925   for (i=0; i<2; i++) {\r
2926     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2927       DrawHighlightOnDC(hdc, TRUE,\r
2928                         h->sq[i].x, h->sq[i].y,\r
2929                         pen);\r
2930   }\r
2931 }\r
2932 \r
2933 /* Note: sqcolor is used only in monoMode */\r
2934 /* Note that this code is largely duplicated in woptions.c,\r
2935    function DrawSampleSquare, so that needs to be updated too */\r
2936 VOID\r
2937 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2938 {\r
2939   HBITMAP oldBitmap;\r
2940   HBRUSH oldBrush;\r
2941   int tmpSize;\r
2942 \r
2943   if (appData.blindfold) return;\r
2944 \r
2945   /* [AS] Use font-based pieces if needed */\r
2946   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2947     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2948     CreatePiecesFromFont();\r
2949 \r
2950     if( fontBitmapSquareSize == squareSize ) {\r
2951         int index = TranslatePieceToFontPiece(piece);\r
2952 \r
2953         SelectObject( tmphdc, hPieceMask[ index ] );\r
2954 \r
2955       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2956         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2957       else\r
2958         BitBlt( hdc,\r
2959             x, y,\r
2960             squareSize, squareSize,\r
2961             tmphdc,\r
2962             0, 0,\r
2963             SRCAND );\r
2964 \r
2965         SelectObject( tmphdc, hPieceFace[ index ] );\r
2966 \r
2967       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2968         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2969       else\r
2970         BitBlt( hdc,\r
2971             x, y,\r
2972             squareSize, squareSize,\r
2973             tmphdc,\r
2974             0, 0,\r
2975             SRCPAINT );\r
2976 \r
2977         return;\r
2978     }\r
2979   }\r
2980 \r
2981   if (appData.monoMode) {\r
2982     SelectObject(tmphdc, PieceBitmap(piece, \r
2983       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2984     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2985            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2986   } else {\r
2987     HBRUSH xBrush = whitePieceBrush;\r
2988     tmpSize = squareSize;\r
2989     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2990     if(minorSize &&\r
2991         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2992          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2993       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2994       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2995       x += (squareSize - minorSize)>>1;\r
2996       y += squareSize - minorSize - 2;\r
2997       tmpSize = minorSize;\r
2998     }\r
2999     if (color || appData.allWhite ) {\r
3000       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3001       if( color )\r
3002               oldBrush = SelectObject(hdc, xBrush);\r
3003       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3004       if(appData.upsideDown && color==flipView)\r
3005         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3006       else\r
3007         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3008       /* Use black for outline of white pieces */\r
3009       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3010       if(appData.upsideDown && color==flipView)\r
3011         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3012       else\r
3013         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3014     } else if(appData.pieceDirectory[0]) {\r
3015       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3016       oldBrush = SelectObject(hdc, xBrush);\r
3017       if(appData.upsideDown && color==flipView)\r
3018         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3019       else\r
3020         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3021       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3022       if(appData.upsideDown && color==flipView)\r
3023         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3024       else\r
3025         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3026     } else {\r
3027       /* Use square color for details of black pieces */\r
3028       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3029       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3030       if(appData.upsideDown && !flipView)\r
3031         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3032       else\r
3033         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3034     }\r
3035     SelectObject(hdc, oldBrush);\r
3036     SelectObject(tmphdc, oldBitmap);\r
3037   }\r
3038 }\r
3039 \r
3040 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3041 int GetBackTextureMode( int algo )\r
3042 {\r
3043     int result = BACK_TEXTURE_MODE_DISABLED;\r
3044 \r
3045     switch( algo ) \r
3046     {\r
3047         case BACK_TEXTURE_MODE_PLAIN:\r
3048             result = 1; /* Always use identity map */\r
3049             break;\r
3050         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3051             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3052             break;\r
3053     }\r
3054 \r
3055     return result;\r
3056 }\r
3057 \r
3058 /* \r
3059     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3060     to handle redraws cleanly (as random numbers would always be different).\r
3061 */\r
3062 VOID RebuildTextureSquareInfo()\r
3063 {\r
3064     BITMAP bi;\r
3065     int lite_w = 0;\r
3066     int lite_h = 0;\r
3067     int dark_w = 0;\r
3068     int dark_h = 0;\r
3069     int row;\r
3070     int col;\r
3071 \r
3072     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3073 \r
3074     if( liteBackTexture != NULL ) {\r
3075         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3076             lite_w = bi.bmWidth;\r
3077             lite_h = bi.bmHeight;\r
3078         }\r
3079     }\r
3080 \r
3081     if( darkBackTexture != NULL ) {\r
3082         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3083             dark_w = bi.bmWidth;\r
3084             dark_h = bi.bmHeight;\r
3085         }\r
3086     }\r
3087 \r
3088     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3089         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3090             if( (col + row) & 1 ) {\r
3091                 /* Lite square */\r
3092                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3093                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3094                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3095                   else\r
3096                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3097                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3098                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3099                   else\r
3100                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3101                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3102                 }\r
3103             }\r
3104             else {\r
3105                 /* Dark square */\r
3106                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3107                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3108                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3109                   else\r
3110                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3111                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3112                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3113                   else\r
3114                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3115                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3116                 }\r
3117             }\r
3118         }\r
3119     }\r
3120 }\r
3121 \r
3122 /* [AS] Arrow highlighting support */\r
3123 \r
3124 static double A_WIDTH = 5; /* Width of arrow body */\r
3125 \r
3126 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3127 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3128 \r
3129 static double Sqr( double x )\r
3130 {\r
3131     return x*x;\r
3132 }\r
3133 \r
3134 static int Round( double x )\r
3135 {\r
3136     return (int) (x + 0.5);\r
3137 }\r
3138 \r
3139 /* Draw an arrow between two points using current settings */\r
3140 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3141 {\r
3142     POINT arrow[7];\r
3143     double dx, dy, j, k, x, y;\r
3144 \r
3145     if( d_x == s_x ) {\r
3146         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3147 \r
3148         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3149         arrow[0].y = s_y;\r
3150 \r
3151         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3152         arrow[1].y = d_y - h;\r
3153 \r
3154         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3155         arrow[2].y = d_y - h;\r
3156 \r
3157         arrow[3].x = d_x;\r
3158         arrow[3].y = d_y;\r
3159 \r
3160         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3161         arrow[5].y = d_y - h;\r
3162 \r
3163         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3164         arrow[4].y = d_y - h;\r
3165 \r
3166         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3167         arrow[6].y = s_y;\r
3168     }\r
3169     else if( d_y == s_y ) {\r
3170         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3171 \r
3172         arrow[0].x = s_x;\r
3173         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3174 \r
3175         arrow[1].x = d_x - w;\r
3176         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3177 \r
3178         arrow[2].x = d_x - w;\r
3179         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3180 \r
3181         arrow[3].x = d_x;\r
3182         arrow[3].y = d_y;\r
3183 \r
3184         arrow[5].x = d_x - w;\r
3185         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3186 \r
3187         arrow[4].x = d_x - w;\r
3188         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3189 \r
3190         arrow[6].x = s_x;\r
3191         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3192     }\r
3193     else {\r
3194         /* [AS] Needed a lot of paper for this! :-) */\r
3195         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3196         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3197   \r
3198         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3199 \r
3200         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3201 \r
3202         x = s_x;\r
3203         y = s_y;\r
3204 \r
3205         arrow[0].x = Round(x - j);\r
3206         arrow[0].y = Round(y + j*dx);\r
3207 \r
3208         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3209         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3210 \r
3211         if( d_x > s_x ) {\r
3212             x = (double) d_x - k;\r
3213             y = (double) d_y - k*dy;\r
3214         }\r
3215         else {\r
3216             x = (double) d_x + k;\r
3217             y = (double) d_y + k*dy;\r
3218         }\r
3219 \r
3220         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3221 \r
3222         arrow[6].x = Round(x - j);\r
3223         arrow[6].y = Round(y + j*dx);\r
3224 \r
3225         arrow[2].x = Round(arrow[6].x + 2*j);\r
3226         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3227 \r
3228         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3229         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3230 \r
3231         arrow[4].x = d_x;\r
3232         arrow[4].y = d_y;\r
3233 \r
3234         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3235         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3236     }\r
3237 \r
3238     Polygon( hdc, arrow, 7 );\r
3239 }\r
3240 \r
3241 /* [AS] Draw an arrow between two squares */\r
3242 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3243 {\r
3244     int s_x, s_y, d_x, d_y;\r
3245     HPEN hpen;\r
3246     HPEN holdpen;\r
3247     HBRUSH hbrush;\r
3248     HBRUSH holdbrush;\r
3249     LOGBRUSH stLB;\r
3250 \r
3251     if( s_col == d_col && s_row == d_row ) {\r
3252         return;\r
3253     }\r
3254 \r
3255     /* Get source and destination points */\r
3256     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3257     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3258 \r
3259     if( d_y > s_y ) {\r
3260         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3261     }\r
3262     else if( d_y < s_y ) {\r
3263         d_y += squareSize / 2 + squareSize / 4;\r
3264     }\r
3265     else {\r
3266         d_y += squareSize / 2;\r
3267     }\r
3268 \r
3269     if( d_x > s_x ) {\r
3270         d_x += squareSize / 2 - squareSize / 4;\r
3271     }\r
3272     else if( d_x < s_x ) {\r
3273         d_x += squareSize / 2 + squareSize / 4;\r
3274     }\r
3275     else {\r
3276         d_x += squareSize / 2;\r
3277     }\r
3278 \r
3279     s_x += squareSize / 2;\r
3280     s_y += squareSize / 2;\r
3281 \r
3282     /* Adjust width */\r
3283     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3284 \r
3285     /* Draw */\r
3286     stLB.lbStyle = BS_SOLID;\r
3287     stLB.lbColor = appData.highlightArrowColor;\r
3288     stLB.lbHatch = 0;\r
3289 \r
3290     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3291     holdpen = SelectObject( hdc, hpen );\r
3292     hbrush = CreateBrushIndirect( &stLB );\r
3293     holdbrush = SelectObject( hdc, hbrush );\r
3294 \r
3295     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3296 \r
3297     SelectObject( hdc, holdpen );\r
3298     SelectObject( hdc, holdbrush );\r
3299     DeleteObject( hpen );\r
3300     DeleteObject( hbrush );\r
3301 }\r
3302 \r
3303 BOOL HasHighlightInfo()\r
3304 {\r
3305     BOOL result = FALSE;\r
3306 \r
3307     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3308         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3309     {\r
3310         result = TRUE;\r
3311     }\r
3312 \r
3313     return result;\r
3314 \r
3315 \r
3316 \r
3317 }\r
3318 \r
3319 BOOL IsDrawArrowEnabled()\r
3320 {\r
3321     BOOL result = FALSE;\r
3322 \r
3323     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3324         result = TRUE;\r
3325     }\r
3326 \r
3327     return result;\r
3328 }\r
3329 \r
3330 VOID DrawArrowHighlight( HDC hdc )\r
3331 {\r
3332     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3333         DrawArrowBetweenSquares( hdc,\r
3334             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3335             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3336     }\r
3337 }\r
3338 \r
3339 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3340 {\r
3341     HRGN result = NULL;\r
3342 \r
3343     if( HasHighlightInfo() ) {\r
3344         int x1, y1, x2, y2;\r
3345         int sx, sy, dx, dy;\r
3346 \r
3347         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3348         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3349 \r
3350         sx = MIN( x1, x2 );\r
3351         sy = MIN( y1, y2 );\r
3352         dx = MAX( x1, x2 ) + squareSize;\r
3353         dy = MAX( y1, y2 ) + squareSize;\r
3354 \r
3355         result = CreateRectRgn( sx, sy, dx, dy );\r
3356     }\r
3357 \r
3358     return result;\r
3359 }\r
3360 \r
3361 /*\r
3362     Warning: this function modifies the behavior of several other functions. \r
3363     \r
3364     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3365     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3366     repaint is scattered all over the place, which is not good for features such as\r
3367     "arrow highlighting" that require a full repaint of the board.\r
3368 \r
3369     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3370     user interaction, when speed is not so important) but especially to avoid errors\r
3371     in the displayed graphics.\r
3372 \r
3373     In such patched places, I always try refer to this function so there is a single\r
3374     place to maintain knowledge.\r
3375     \r
3376     To restore the original behavior, just return FALSE unconditionally.\r
3377 */\r
3378 BOOL IsFullRepaintPreferrable()\r
3379 {\r
3380     BOOL result = FALSE;\r
3381 \r
3382     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3383         /* Arrow may appear on the board */\r
3384         result = TRUE;\r
3385     }\r
3386 \r
3387     return result;\r
3388 }\r
3389 \r
3390 /* \r
3391     This function is called by DrawPosition to know whether a full repaint must\r
3392     be forced or not.\r
3393 \r
3394     Only DrawPosition may directly call this function, which makes use of \r
3395     some state information. Other function should call DrawPosition specifying \r
3396     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3397 */\r
3398 BOOL DrawPositionNeedsFullRepaint()\r
3399 {\r
3400     BOOL result = FALSE;\r
3401 \r
3402     /* \r
3403         Probably a slightly better policy would be to trigger a full repaint\r
3404         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3405         but animation is fast enough that it's difficult to notice.\r
3406     */\r
3407     if( animInfo.piece == EmptySquare ) {\r
3408         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3409             result = TRUE;\r
3410         }\r
3411     }\r
3412 \r
3413     return result;\r
3414 }\r
3415 \r
3416 static HBITMAP borderBitmap;\r
3417 \r
3418 VOID\r
3419 DrawBackgroundOnDC(HDC hdc)\r
3420 {\r
3421   \r
3422   BITMAP bi;\r
3423   HDC tmphdc;\r
3424   HBITMAP hbm;\r
3425   static char oldBorder[MSG_SIZ];\r
3426   int w = 600, h = 600, mode;\r
3427 \r
3428   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3429     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3430     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3431   }\r
3432   if(borderBitmap == NULL) { // loading failed, use white\r
3433     FillRect( hdc, &boardRect, whitePieceBrush );\r
3434     return;\r
3435   }\r
3436   tmphdc = CreateCompatibleDC(hdc);\r
3437   hbm = SelectObject(tmphdc, borderBitmap);\r
3438   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3439             w = bi.bmWidth;\r
3440             h = bi.bmHeight;\r
3441   }\r
3442   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3443   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3444                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3445   SetStretchBltMode(hdc, mode);\r
3446   SelectObject(tmphdc, hbm);\r
3447   DeleteDC(tmphdc);\r
3448 }\r
3449 \r
3450 VOID\r
3451 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3452 {\r
3453   int row, column, x, y, square_color, piece_color;\r
3454   ChessSquare piece;\r
3455   HBRUSH oldBrush;\r
3456   HDC texture_hdc = NULL;\r
3457 \r
3458   /* [AS] Initialize background textures if needed */\r
3459   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3460       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3461       if( backTextureSquareSize != squareSize \r
3462        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3463           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3464           backTextureSquareSize = squareSize;\r
3465           RebuildTextureSquareInfo();\r
3466       }\r
3467 \r
3468       texture_hdc = CreateCompatibleDC( hdc );\r
3469   }\r
3470 \r
3471   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3472     for (column = 0; column < BOARD_WIDTH; column++) {\r
3473   \r
3474       SquareToPos(row, column, &x, &y);\r
3475 \r
3476       piece = board[row][column];\r
3477 \r
3478       square_color = ((column + row) % 2) == 1;\r
3479       if( gameInfo.variant == VariantXiangqi ) {\r
3480           square_color = !InPalace(row, column);\r
3481           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3482           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3483       }\r
3484       piece_color = (int) piece < (int) BlackPawn;\r
3485 \r
3486 \r
3487       /* [HGM] holdings file: light square or black */\r
3488       if(column == BOARD_LEFT-2) {\r
3489             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3490                 square_color = 1;\r
3491             else {\r
3492                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3493                 continue;\r
3494             }\r
3495       } else\r
3496       if(column == BOARD_RGHT + 1 ) {\r
3497             if( row < gameInfo.holdingsSize )\r
3498                 square_color = 1;\r
3499             else {\r
3500                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3501                 continue;\r
3502             }\r
3503       }\r
3504       if(column == BOARD_LEFT-1 ) /* left align */\r
3505             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3506       else if( column == BOARD_RGHT) /* right align */\r
3507             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3508       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3509       else\r
3510       if (appData.monoMode) {\r
3511         if (piece == EmptySquare) {\r
3512           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3513                  square_color ? WHITENESS : BLACKNESS);\r
3514         } else {\r
3515           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3516         }\r
3517       } \r
3518       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3519           /* [AS] Draw the square using a texture bitmap */\r
3520           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3521           int r = row, c = column; // [HGM] do not flip board in flipView\r
3522           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3523 \r
3524           DrawTile( x, y, \r
3525               squareSize, squareSize, \r
3526               hdc, \r
3527               texture_hdc,\r
3528               backTextureSquareInfo[r][c].mode,\r
3529               backTextureSquareInfo[r][c].x,\r
3530               backTextureSquareInfo[r][c].y );\r
3531 \r
3532           SelectObject( texture_hdc, hbm );\r
3533 \r
3534           if (piece != EmptySquare) {\r
3535               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3536           }\r
3537       }\r
3538       else {\r
3539         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3540 \r
3541         oldBrush = SelectObject(hdc, brush );\r
3542         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3543         SelectObject(hdc, oldBrush);\r
3544         if (piece != EmptySquare)\r
3545           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3546       }\r
3547     }\r
3548   }\r
3549 \r
3550   if( texture_hdc != NULL ) {\r
3551     DeleteDC( texture_hdc );\r
3552   }\r
3553 }\r
3554 \r
3555 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3556 void fputDW(FILE *f, int x)\r
3557 {\r
3558         fputc(x     & 255, f);\r
3559         fputc(x>>8  & 255, f);\r
3560         fputc(x>>16 & 255, f);\r
3561         fputc(x>>24 & 255, f);\r
3562 }\r
3563 \r
3564 #define MAX_CLIPS 200   /* more than enough */\r
3565 \r
3566 VOID\r
3567 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3568 {\r
3569 //  HBITMAP bufferBitmap;\r
3570   BITMAP bi;\r
3571 //  RECT Rect;\r
3572   HDC tmphdc;\r
3573   HBITMAP hbm;\r
3574   int w = 100, h = 50;\r
3575 \r
3576   if(logo == NULL) {\r
3577     if(!logoHeight) return;\r
3578     FillRect( hdc, &logoRect, whitePieceBrush );\r
3579   }\r
3580 //  GetClientRect(hwndMain, &Rect);\r
3581 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3582 //                                      Rect.bottom-Rect.top+1);\r
3583   tmphdc = CreateCompatibleDC(hdc);\r
3584   hbm = SelectObject(tmphdc, logo);\r
3585   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3586             w = bi.bmWidth;\r
3587             h = bi.bmHeight;\r
3588   }\r
3589   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3590                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3591   SelectObject(tmphdc, hbm);\r
3592   DeleteDC(tmphdc);\r
3593 }\r
3594 \r
3595 VOID\r
3596 DisplayLogos()\r
3597 {\r
3598   if(logoHeight) {\r
3599         HDC hdc = GetDC(hwndMain);\r
3600         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3601         if(appData.autoLogo) {\r
3602           \r
3603           switch(gameMode) { // pick logos based on game mode\r
3604             case IcsObserving:\r
3605                 whiteLogo = second.programLogo; // ICS logo\r
3606                 blackLogo = second.programLogo;\r
3607             default:\r
3608                 break;\r
3609             case IcsPlayingWhite:\r
3610                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3611                 blackLogo = second.programLogo; // ICS logo\r
3612                 break;\r
3613             case IcsPlayingBlack:\r
3614                 whiteLogo = second.programLogo; // ICS logo\r
3615                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3616                 break;\r
3617             case TwoMachinesPlay:\r
3618                 if(first.twoMachinesColor[0] == 'b') {\r
3619                     whiteLogo = second.programLogo;\r
3620                     blackLogo = first.programLogo;\r
3621                 }\r
3622                 break;\r
3623             case MachinePlaysWhite:\r
3624                 blackLogo = userLogo;\r
3625                 break;\r
3626             case MachinePlaysBlack:\r
3627                 whiteLogo = userLogo;\r
3628                 blackLogo = first.programLogo;\r
3629           }\r
3630         }\r
3631         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3632         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3633         ReleaseDC(hwndMain, hdc);\r
3634   }\r
3635 }\r
3636 \r
3637 void\r
3638 UpdateLogos(int display)\r
3639 { // called after loading new engine(s), in tourney or from menu\r
3640   LoadLogo(&first, 0, FALSE);\r
3641   LoadLogo(&second, 1, appData.icsActive);\r
3642   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3643   if(display) DisplayLogos();\r
3644 }\r
3645 \r
3646 static HDC hdcSeek;\r
3647 \r
3648 // [HGM] seekgraph\r
3649 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3650 {\r
3651     POINT stPt;\r
3652     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3653     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3654     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3655     SelectObject( hdcSeek, hp );\r
3656 }\r
3657 \r
3658 // front-end wrapper for drawing functions to do rectangles\r
3659 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3660 {\r
3661     HPEN hp;\r
3662     RECT rc;\r
3663 \r
3664     if (hdcSeek == NULL) {\r
3665     hdcSeek = GetDC(hwndMain);\r
3666       if (!appData.monoMode) {\r
3667         SelectPalette(hdcSeek, hPal, FALSE);\r
3668         RealizePalette(hdcSeek);\r
3669       }\r
3670     }\r
3671     hp = SelectObject( hdcSeek, gridPen );\r
3672     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3673     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3674     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3675     SelectObject( hdcSeek, hp );\r
3676 }\r
3677 \r
3678 // front-end wrapper for putting text in graph\r
3679 void DrawSeekText(char *buf, int x, int y)\r
3680 {\r
3681         SIZE stSize;\r
3682         SetBkMode( hdcSeek, TRANSPARENT );\r
3683         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3684         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3685 }\r
3686 \r
3687 void DrawSeekDot(int x, int y, int color)\r
3688 {\r
3689         int square = color & 0x80;\r
3690         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3691                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3692         color &= 0x7F;\r
3693         if(square)\r
3694             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3695                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3696         else\r
3697             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3698                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3699             SelectObject(hdcSeek, oldBrush);\r
3700 }\r
3701 \r
3702 void DrawSeekOpen()\r
3703 {\r
3704 }\r
3705 \r
3706 void DrawSeekClose()\r
3707 {\r
3708 }\r
3709 \r
3710 VOID\r
3711 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3712 {\r
3713   static Board lastReq[2], lastDrawn[2];\r
3714   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3715   static int lastDrawnFlipView = 0;\r
3716   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3717   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3718   HDC tmphdc;\r
3719   HDC hdcmem;\r
3720   HBITMAP bufferBitmap;\r
3721   HBITMAP oldBitmap;\r
3722   RECT Rect;\r
3723   HRGN clips[MAX_CLIPS];\r
3724   ChessSquare dragged_piece = EmptySquare;\r
3725   int nr = twoBoards*partnerUp;\r
3726 \r
3727   /* I'm undecided on this - this function figures out whether a full\r
3728    * repaint is necessary on its own, so there's no real reason to have the\r
3729    * caller tell it that.  I think this can safely be set to FALSE - but\r
3730    * if we trust the callers not to request full repaints unnessesarily, then\r
3731    * we could skip some clipping work.  In other words, only request a full\r
3732    * redraw when the majority of pieces have changed positions (ie. flip, \r
3733    * gamestart and similar)  --Hawk\r
3734    */\r
3735   Boolean fullrepaint = repaint;\r
3736 \r
3737   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3738 \r
3739   if( DrawPositionNeedsFullRepaint() ) {\r
3740       fullrepaint = TRUE;\r
3741   }\r
3742 \r
3743   if (board == NULL) {\r
3744     if (!lastReqValid[nr]) {\r
3745       return;\r
3746     }\r
3747     board = lastReq[nr];\r
3748   } else {\r
3749     CopyBoard(lastReq[nr], board);\r
3750     lastReqValid[nr] = 1;\r
3751   }\r
3752 \r
3753   if (doingSizing) {\r
3754     return;\r
3755   }\r
3756 \r
3757   if (IsIconic(hwndMain)) {\r
3758     return;\r
3759   }\r
3760 \r
3761   if (hdc == NULL) {\r
3762     hdc = GetDC(hwndMain);\r
3763     if (!appData.monoMode) {\r
3764       SelectPalette(hdc, hPal, FALSE);\r
3765       RealizePalette(hdc);\r
3766     }\r
3767     releaseDC = TRUE;\r
3768   } else {\r
3769     releaseDC = FALSE;\r
3770   }\r
3771 \r
3772   /* Create some work-DCs */\r
3773   hdcmem = CreateCompatibleDC(hdc);\r
3774   tmphdc = CreateCompatibleDC(hdc);\r
3775 \r
3776   /* If dragging is in progress, we temporarely remove the piece */\r
3777   /* [HGM] or temporarily decrease count if stacked              */\r
3778   /*       !! Moved to before board compare !!                   */\r
3779   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3780     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3781     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3782             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3783         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3784     } else \r
3785     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3786             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3787         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3788     } else \r
3789         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3790   }\r
3791 \r
3792   /* Figure out which squares need updating by comparing the \r
3793    * newest board with the last drawn board and checking if\r
3794    * flipping has changed.\r
3795    */\r
3796   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3797     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3798       for (column = 0; column < BOARD_WIDTH; column++) {\r
3799         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3800           SquareToPos(row, column, &x, &y);\r
3801           clips[num_clips++] =\r
3802             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3803         }\r
3804       }\r
3805     }\r
3806    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3807     for (i=0; i<2; i++) {\r
3808       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3809           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3810         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3811             lastDrawnHighlight.sq[i].y >= 0) {\r
3812           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3813                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3814           clips[num_clips++] =\r
3815             CreateRectRgn(x - lineGap, y - lineGap, \r
3816                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3817         }\r
3818         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3819           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3820           clips[num_clips++] =\r
3821             CreateRectRgn(x - lineGap, y - lineGap, \r
3822                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3823         }\r
3824       }\r
3825     }\r
3826     for (i=0; i<2; i++) {\r
3827       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3828           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3829         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3830             lastDrawnPremove.sq[i].y >= 0) {\r
3831           SquareToPos(lastDrawnPremove.sq[i].y,\r
3832                       lastDrawnPremove.sq[i].x, &x, &y);\r
3833           clips[num_clips++] =\r
3834             CreateRectRgn(x - lineGap, y - lineGap, \r
3835                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3836         }\r
3837         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3838             premoveHighlightInfo.sq[i].y >= 0) {\r
3839           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3840                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3841           clips[num_clips++] =\r
3842             CreateRectRgn(x - lineGap, y - lineGap, \r
3843                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3844         }\r
3845       }\r
3846     }\r
3847    } else { // nr == 1\r
3848         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3849         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3850         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3851         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3852       for (i=0; i<2; i++) {\r
3853         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3854             partnerHighlightInfo.sq[i].y >= 0) {\r
3855           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3856                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3857           clips[num_clips++] =\r
3858             CreateRectRgn(x - lineGap, y - lineGap, \r
3859                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3860         }\r
3861         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3862             oldPartnerHighlight.sq[i].y >= 0) {\r
3863           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3864                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3865           clips[num_clips++] =\r
3866             CreateRectRgn(x - lineGap, y - lineGap, \r
3867                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3868         }\r
3869       }\r
3870    }\r
3871   } else {\r
3872     fullrepaint = TRUE;\r
3873   }\r
3874 \r
3875   /* Create a buffer bitmap - this is the actual bitmap\r
3876    * being written to.  When all the work is done, we can\r
3877    * copy it to the real DC (the screen).  This avoids\r
3878    * the problems with flickering.\r
3879    */\r
3880   GetClientRect(hwndMain, &Rect);\r
3881   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3882                                         Rect.bottom-Rect.top+1);\r
3883   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3884   if (!appData.monoMode) {\r
3885     SelectPalette(hdcmem, hPal, FALSE);\r
3886   }\r
3887 \r
3888   /* Create clips for dragging */\r
3889   if (!fullrepaint) {\r
3890     if (dragInfo.from.x >= 0) {\r
3891       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3892       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3893     }\r
3894     if (dragInfo.start.x >= 0) {\r
3895       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3896       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3897     }\r
3898     if (dragInfo.pos.x >= 0) {\r
3899       x = dragInfo.pos.x - squareSize / 2;\r
3900       y = dragInfo.pos.y - squareSize / 2;\r
3901       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3902     }\r
3903     if (dragInfo.lastpos.x >= 0) {\r
3904       x = dragInfo.lastpos.x - squareSize / 2;\r
3905       y = dragInfo.lastpos.y - squareSize / 2;\r
3906       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3907     }\r
3908   }\r
3909 \r
3910   /* Are we animating a move?  \r
3911    * If so, \r
3912    *   - remove the piece from the board (temporarely)\r
3913    *   - calculate the clipping region\r
3914    */\r
3915   if (!fullrepaint) {\r
3916     if (animInfo.piece != EmptySquare) {\r
3917       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3918       x = boardRect.left + animInfo.lastpos.x;\r
3919       y = boardRect.top + animInfo.lastpos.y;\r
3920       x2 = boardRect.left + animInfo.pos.x;\r
3921       y2 = boardRect.top + animInfo.pos.y;\r
3922       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3923       /* Slight kludge.  The real problem is that after AnimateMove is\r
3924          done, the position on the screen does not match lastDrawn.\r
3925          This currently causes trouble only on e.p. captures in\r
3926          atomic, where the piece moves to an empty square and then\r
3927          explodes.  The old and new positions both had an empty square\r
3928          at the destination, but animation has drawn a piece there and\r
3929          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3930       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3931     }\r
3932   }\r
3933 \r
3934   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3935   if (num_clips == 0)\r
3936     fullrepaint = TRUE;\r
3937 \r
3938   /* Set clipping on the memory DC */\r
3939   if (!fullrepaint) {\r
3940     SelectClipRgn(hdcmem, clips[0]);\r
3941     for (x = 1; x < num_clips; x++) {\r
3942       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3943         abort();  // this should never ever happen!\r
3944     }\r
3945   }\r
3946 \r
3947   /* Do all the drawing to the memory DC */\r
3948   if(explodeInfo.radius) { // [HGM] atomic\r
3949         HBRUSH oldBrush;\r
3950         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3951         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3952         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3953         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3954         x += squareSize/2;\r
3955         y += squareSize/2;\r
3956         if(!fullrepaint) {\r
3957           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3958           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3959         }\r
3960         DrawGridOnDC(hdcmem);\r
3961         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3962         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3963         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3964         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3965         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3966         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3967         SelectObject(hdcmem, oldBrush);\r
3968   } else {\r
3969     if(border) DrawBackgroundOnDC(hdcmem);\r
3970     DrawGridOnDC(hdcmem);\r
3971     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3972         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3973         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3974     } else {\r
3975         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3976         oldPartnerHighlight = partnerHighlightInfo;\r
3977     }\r
3978     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3979   }\r
3980   if(nr == 0) // [HGM] dual: markers only on left board\r
3981   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3982     for (column = 0; column < BOARD_WIDTH; column++) {\r
3983         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3984             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3985             SquareToPos(row, column, &x, &y);\r
3986             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3987                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3988             SelectObject(hdcmem, oldBrush);\r
3989         }\r
3990     }\r
3991   }\r
3992 \r
3993   if( appData.highlightMoveWithArrow ) {\r
3994     DrawArrowHighlight(hdcmem);\r
3995   }\r
3996 \r
3997   DrawCoordsOnDC(hdcmem);\r
3998 \r
3999   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
4000                  /* to make sure lastDrawn contains what is actually drawn */\r
4001 \r
4002   /* Put the dragged piece back into place and draw it (out of place!) */\r
4003     if (dragged_piece != EmptySquare) {\r
4004     /* [HGM] or restack */\r
4005     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4006                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4007     else\r
4008     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4009                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4010 \r
4011     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4012     x = dragInfo.pos.x - squareSize / 2;\r
4013     y = dragInfo.pos.y - squareSize / 2;\r
4014     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4015                   ((int) dragInfo.piece < (int) BlackPawn), \r
4016                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4017   }   \r
4018   \r
4019   /* Put the animated piece back into place and draw it */\r
4020   if (animInfo.piece != EmptySquare) {\r
4021     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4022     x = boardRect.left + animInfo.pos.x;\r
4023     y = boardRect.top + animInfo.pos.y;\r
4024     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4025                   ((int) animInfo.piece < (int) BlackPawn),\r
4026                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4027   }\r
4028 \r
4029   /* Release the bufferBitmap by selecting in the old bitmap \r
4030    * and delete the memory DC\r
4031    */\r
4032   SelectObject(hdcmem, oldBitmap);\r
4033   DeleteDC(hdcmem);\r
4034 \r
4035   /* Set clipping on the target DC */\r
4036   if (!fullrepaint) {\r
4037     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4038         RECT rect;\r
4039         GetRgnBox(clips[x], &rect);\r
4040         DeleteObject(clips[x]);\r
4041         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4042                           rect.right + wpMain.width/2, rect.bottom);\r
4043     }\r
4044     SelectClipRgn(hdc, clips[0]);\r
4045     for (x = 1; x < num_clips; x++) {\r
4046       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4047         abort();   // this should never ever happen!\r
4048     } \r
4049   }\r
4050 \r
4051   /* Copy the new bitmap onto the screen in one go.\r
4052    * This way we avoid any flickering\r
4053    */\r
4054   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4055   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4056          boardRect.right - boardRect.left,\r
4057          boardRect.bottom - boardRect.top,\r
4058          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4059   if(saveDiagFlag) { \r
4060     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4061     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4062 \r
4063     GetObject(bufferBitmap, sizeof(b), &b);\r
4064     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4065         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4066         bih.biWidth = b.bmWidth;\r
4067         bih.biHeight = b.bmHeight;\r
4068         bih.biPlanes = 1;\r
4069         bih.biBitCount = b.bmBitsPixel;\r
4070         bih.biCompression = 0;\r
4071         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4072         bih.biXPelsPerMeter = 0;\r
4073         bih.biYPelsPerMeter = 0;\r
4074         bih.biClrUsed = 0;\r
4075         bih.biClrImportant = 0;\r
4076 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4077 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4078         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4079 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4080 \r
4081         wb = b.bmWidthBytes;\r
4082         // count colors\r
4083         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4084                 int k = ((int*) pData)[i];\r
4085                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4086                 if(j >= 16) break;\r
4087                 color[j] = k;\r
4088                 if(j >= nrColors) nrColors = j+1;\r
4089         }\r
4090         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4091                 INT p = 0;\r
4092                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4093                     for(w=0; w<(wb>>2); w+=2) {\r
4094                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4095                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4096                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4097                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4098                         pData[p++] = m | j<<4;\r
4099                     }\r
4100                     while(p&3) pData[p++] = 0;\r
4101                 }\r
4102                 fac = 3;\r
4103                 wb = ((wb+31)>>5)<<2;\r
4104         }\r
4105         // write BITMAPFILEHEADER\r
4106         fprintf(diagFile, "BM");\r
4107         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4108         fputDW(diagFile, 0);\r
4109         fputDW(diagFile, 0x36 + (fac?64:0));\r
4110         // write BITMAPINFOHEADER\r
4111         fputDW(diagFile, 40);\r
4112         fputDW(diagFile, b.bmWidth);\r
4113         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4114         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4115         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4116         fputDW(diagFile, 0);\r
4117         fputDW(diagFile, 0);\r
4118         fputDW(diagFile, 0);\r
4119         fputDW(diagFile, 0);\r
4120         fputDW(diagFile, 0);\r
4121         fputDW(diagFile, 0);\r
4122         // write color table\r
4123         if(fac)\r
4124         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4125         // write bitmap data\r
4126         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4127                 fputc(pData[i], diagFile);\r
4128         free(pData);\r
4129      }\r
4130   }\r
4131 \r
4132   SelectObject(tmphdc, oldBitmap);\r
4133 \r
4134   /* Massive cleanup */\r
4135   for (x = 0; x < num_clips; x++)\r
4136     DeleteObject(clips[x]);\r
4137 \r
4138   DeleteDC(tmphdc);\r
4139   DeleteObject(bufferBitmap);\r
4140 \r
4141   if (releaseDC) \r
4142     ReleaseDC(hwndMain, hdc);\r
4143   \r
4144   if (lastDrawnFlipView != flipView && nr == 0) {\r
4145     if (flipView)\r
4146       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4147     else\r
4148       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4149   }\r
4150 \r
4151 /*  CopyBoard(lastDrawn, board);*/\r
4152   lastDrawnHighlight = highlightInfo;\r
4153   lastDrawnPremove   = premoveHighlightInfo;\r
4154   lastDrawnFlipView = flipView;\r
4155   lastDrawnValid[nr] = 1;\r
4156 }\r
4157 \r
4158 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4159 int\r
4160 SaveDiagram(f)\r
4161      FILE *f;\r
4162 {\r
4163     saveDiagFlag = 1; diagFile = f;\r
4164     HDCDrawPosition(NULL, TRUE, NULL);\r
4165     saveDiagFlag = 0;\r
4166 \r
4167     fclose(f);\r
4168     return TRUE;\r
4169 }\r
4170 \r
4171 \r
4172 /*---------------------------------------------------------------------------*\\r
4173 | CLIENT PAINT PROCEDURE\r
4174 |   This is the main event-handler for the WM_PAINT message.\r
4175 |\r
4176 \*---------------------------------------------------------------------------*/\r
4177 VOID\r
4178 PaintProc(HWND hwnd)\r
4179 {\r
4180   HDC         hdc;\r
4181   PAINTSTRUCT ps;\r
4182   HFONT       oldFont;\r
4183 \r
4184   if((hdc = BeginPaint(hwnd, &ps))) {\r
4185     if (IsIconic(hwnd)) {\r
4186       DrawIcon(hdc, 2, 2, iconCurrent);\r
4187     } else {\r
4188       if (!appData.monoMode) {\r
4189         SelectPalette(hdc, hPal, FALSE);\r
4190         RealizePalette(hdc);\r
4191       }\r
4192       HDCDrawPosition(hdc, 1, NULL);\r
4193       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4194         flipView = !flipView; partnerUp = !partnerUp;\r
4195         HDCDrawPosition(hdc, 1, NULL);\r
4196         flipView = !flipView; partnerUp = !partnerUp;\r
4197       }\r
4198       oldFont =\r
4199         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4200       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4201                  ETO_CLIPPED|ETO_OPAQUE,\r
4202                  &messageRect, messageText, strlen(messageText), NULL);\r
4203       SelectObject(hdc, oldFont);\r
4204       DisplayBothClocks();\r
4205       DisplayLogos();\r
4206     }\r
4207     EndPaint(hwnd,&ps);\r
4208   }\r
4209 \r
4210   return;\r
4211 }\r
4212 \r
4213 \r
4214 /*\r
4215  * If the user selects on a border boundary, return -1; if off the board,\r
4216  *   return -2.  Otherwise map the event coordinate to the square.\r
4217  * The offset boardRect.left or boardRect.top must already have been\r
4218  *   subtracted from x.\r
4219  */\r
4220 int EventToSquare(x, limit)\r
4221      int x, limit;\r
4222 {\r
4223   if (x <= border)\r
4224     return -2;\r
4225   if (x < lineGap + border)\r
4226     return -1;\r
4227   x -= lineGap + border;\r
4228   if ((x % (squareSize + lineGap)) >= squareSize)\r
4229     return -1;\r
4230   x /= (squareSize + lineGap);\r
4231     if (x >= limit)\r
4232     return -2;\r
4233   return x;\r
4234 }\r
4235 \r
4236 typedef struct {\r
4237   char piece;\r
4238   int command;\r
4239   char* name;\r
4240 } DropEnable;\r
4241 \r
4242 DropEnable dropEnables[] = {\r
4243   { 'P', DP_Pawn, N_("Pawn") },\r
4244   { 'N', DP_Knight, N_("Knight") },\r
4245   { 'B', DP_Bishop, N_("Bishop") },\r
4246   { 'R', DP_Rook, N_("Rook") },\r
4247   { 'Q', DP_Queen, N_("Queen") },\r
4248 };\r
4249 \r
4250 VOID\r
4251 SetupDropMenu(HMENU hmenu)\r
4252 {\r
4253   int i, count, enable;\r
4254   char *p;\r
4255   extern char white_holding[], black_holding[];\r
4256   char item[MSG_SIZ];\r
4257 \r
4258   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4259     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4260                dropEnables[i].piece);\r
4261     count = 0;\r
4262     while (p && *p++ == dropEnables[i].piece) count++;\r
4263       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4264     enable = count > 0 || !appData.testLegality\r
4265       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4266                       && !appData.icsActive);\r
4267     ModifyMenu(hmenu, dropEnables[i].command,\r
4268                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4269                dropEnables[i].command, item);\r
4270   }\r
4271 }\r
4272 \r
4273 void DragPieceBegin(int x, int y, Boolean instantly)\r
4274 {\r
4275       dragInfo.lastpos.x = boardRect.left + x;\r
4276       dragInfo.lastpos.y = boardRect.top + y;\r
4277       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4278       dragInfo.from.x = fromX;\r
4279       dragInfo.from.y = fromY;\r
4280       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4281       dragInfo.start = dragInfo.from;\r
4282       SetCapture(hwndMain);\r
4283 }\r
4284 \r
4285 void DragPieceEnd(int x, int y)\r
4286 {\r
4287     ReleaseCapture();\r
4288     dragInfo.start.x = dragInfo.start.y = -1;\r
4289     dragInfo.from = dragInfo.start;\r
4290     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4291 }\r
4292 \r
4293 void ChangeDragPiece(ChessSquare piece)\r
4294 {\r
4295     dragInfo.piece = piece;\r
4296 }\r
4297 \r
4298 /* Event handler for mouse messages */\r
4299 VOID\r
4300 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4301 {\r
4302   int x, y, menuNr;\r
4303   POINT pt;\r
4304   static int recursive = 0;\r
4305   HMENU hmenu;\r
4306   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4307 \r
4308   if (recursive) {\r
4309     if (message == WM_MBUTTONUP) {\r
4310       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4311          to the middle button: we simulate pressing the left button too!\r
4312          */\r
4313       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4314       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4315     }\r
4316     return;\r
4317   }\r
4318   recursive++;\r
4319   \r
4320   pt.x = LOWORD(lParam);\r
4321   pt.y = HIWORD(lParam);\r
4322   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4323   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4324   if (!flipView && y >= 0) {\r
4325     y = BOARD_HEIGHT - 1 - y;\r
4326   }\r
4327   if (flipView && x >= 0) {\r
4328     x = BOARD_WIDTH - 1 - x;\r
4329   }\r
4330 \r
4331   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4332   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4333 \r
4334   switch (message) {\r
4335   case WM_LBUTTONDOWN:\r
4336       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4337         ClockClick(flipClock); break;\r
4338       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4339         ClockClick(!flipClock); break;\r
4340       }\r
4341     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4342       dragInfo.start.x = dragInfo.start.y = -1;\r
4343       dragInfo.from = dragInfo.start;\r
4344     }\r
4345     if(fromX == -1 && frozen) { // not sure where this is for\r
4346                 fromX = fromY = -1; \r
4347       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4348       break;\r
4349     }\r
4350       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4351       DrawPosition(TRUE, NULL);\r
4352     break;\r
4353 \r
4354   case WM_LBUTTONUP:\r
4355       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4356       DrawPosition(TRUE, NULL);\r
4357     break;\r
4358 \r
4359   case WM_MOUSEMOVE:\r
4360     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4361     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4362     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4363     if ((appData.animateDragging || appData.highlightDragging)\r
4364         && (wParam & MK_LBUTTON || dragging == 2)\r
4365         && dragInfo.from.x >= 0) \r
4366     {\r
4367       BOOL full_repaint = FALSE;\r
4368 \r
4369       if (appData.animateDragging) {\r
4370         dragInfo.pos = pt;\r
4371       }\r
4372       if (appData.highlightDragging) {\r
4373         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4374         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4375             full_repaint = TRUE;\r
4376         }\r
4377       }\r
4378       \r
4379       DrawPosition( full_repaint, NULL);\r
4380       \r
4381       dragInfo.lastpos = dragInfo.pos;\r
4382     }\r
4383     break;\r
4384 \r
4385   case WM_MOUSEWHEEL: // [DM]\r
4386     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4387        /* Mouse Wheel is being rolled forward\r
4388         * Play moves forward\r
4389         */\r
4390        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4391                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4392        /* Mouse Wheel is being rolled backward\r
4393         * Play moves backward\r
4394         */\r
4395        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4396                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4397     }\r
4398     break;\r
4399 \r
4400   case WM_MBUTTONUP:\r
4401   case WM_RBUTTONUP:\r
4402     ReleaseCapture();\r
4403     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4404     break;\r
4405  \r
4406   case WM_MBUTTONDOWN:\r
4407   case WM_RBUTTONDOWN:\r
4408     ErrorPopDown();\r
4409     ReleaseCapture();\r
4410     fromX = fromY = -1;\r
4411     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4412     dragInfo.start.x = dragInfo.start.y = -1;\r
4413     dragInfo.from = dragInfo.start;\r
4414     dragInfo.lastpos = dragInfo.pos;\r
4415     if (appData.highlightDragging) {\r
4416       ClearHighlights();\r
4417     }\r
4418     if(y == -2) {\r
4419       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4420       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4421           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4422       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4423           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4424       }\r
4425       break;\r
4426     }\r
4427     DrawPosition(TRUE, NULL);\r
4428 \r
4429     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4430     switch (menuNr) {\r
4431     case 0:\r
4432       if (message == WM_MBUTTONDOWN) {\r
4433         buttonCount = 3;  /* even if system didn't think so */\r
4434         if (wParam & MK_SHIFT) \r
4435           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4436         else\r
4437           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4438       } else { /* message == WM_RBUTTONDOWN */\r
4439         /* Just have one menu, on the right button.  Windows users don't\r
4440            think to try the middle one, and sometimes other software steals\r
4441            it, or it doesn't really exist. */\r
4442         if(gameInfo.variant != VariantShogi)\r
4443             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4444         else\r
4445             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4446       }\r
4447       break;\r
4448     case 2:\r
4449       SetCapture(hwndMain);\r
4450       break;\r
4451     case 1:\r
4452       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4453       SetupDropMenu(hmenu);\r
4454       MenuPopup(hwnd, pt, hmenu, -1);\r
4455     default:\r
4456       break;\r
4457     }\r
4458     break;\r
4459   }\r
4460 \r
4461   recursive--;\r
4462 }\r
4463 \r
4464 /* Preprocess messages for buttons in main window */\r
4465 LRESULT CALLBACK\r
4466 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4467 {\r
4468   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4469   int i, dir;\r
4470 \r
4471   for (i=0; i<N_BUTTONS; i++) {\r
4472     if (buttonDesc[i].id == id) break;\r
4473   }\r
4474   if (i == N_BUTTONS) return 0;\r
4475   switch (message) {\r
4476   case WM_KEYDOWN:\r
4477     switch (wParam) {\r
4478     case VK_LEFT:\r
4479     case VK_RIGHT:\r
4480       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4481       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4482       return TRUE;\r
4483     }\r
4484     break;\r
4485   case WM_CHAR:\r
4486     switch (wParam) {\r
4487     case '\r':\r
4488       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4489       return TRUE;\r
4490     default:\r
4491       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4492         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4493         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4494         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4495         SetFocus(h);\r
4496         SendMessage(h, WM_CHAR, wParam, lParam);\r
4497         return TRUE;\r
4498       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4499         TypeInEvent((char)wParam);\r
4500       }\r
4501       break;\r
4502     }\r
4503     break;\r
4504   }\r
4505   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4506 }\r
4507 \r
4508 static int promoStyle;\r
4509 \r
4510 /* Process messages for Promotion dialog box */\r
4511 LRESULT CALLBACK\r
4512 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4513 {\r
4514   char promoChar;\r
4515 \r
4516   switch (message) {\r
4517 \r
4518   case WM_INITDIALOG: /* message: initialize dialog box */\r
4519     /* Center the dialog over the application window */\r
4520     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4521     Translate(hDlg, DLG_PromotionKing);\r
4522     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4523       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4524        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4525        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4526                SW_SHOW : SW_HIDE);\r
4527     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4528     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4529        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4530          PieceToChar(WhiteAngel) != '~') ||\r
4531         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4532          PieceToChar(BlackAngel) != '~')   ) ?\r
4533                SW_SHOW : SW_HIDE);\r
4534     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4535        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4536          PieceToChar(WhiteMarshall) != '~') ||\r
4537         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4538          PieceToChar(BlackMarshall) != '~')   ) ?\r
4539                SW_SHOW : SW_HIDE);\r
4540     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4541     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4542     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4543     if(promoStyle) {\r
4544         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4545         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4546         SetWindowText(hDlg, "Promote?");\r
4547     }\r
4548     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4549        gameInfo.variant == VariantSuper ?\r
4550                SW_SHOW : SW_HIDE);\r
4551     return TRUE;\r
4552 \r
4553   case WM_COMMAND: /* message: received a command */\r
4554     switch (LOWORD(wParam)) {\r
4555     case IDCANCEL:\r
4556       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4557       ClearHighlights();\r
4558       DrawPosition(FALSE, NULL);\r
4559       return TRUE;\r
4560     case PB_King:\r
4561       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4562       break;\r
4563     case PB_Queen:\r
4564       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4565       break;\r
4566     case PB_Rook:\r
4567       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4568       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4569       break;\r
4570     case PB_Bishop:\r
4571       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4572       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4573       break;\r
4574     case PB_Chancellor:\r
4575       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4576       break;\r
4577     case PB_Archbishop:\r
4578       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4579       break;\r
4580     case PB_Knight:\r
4581       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4582                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4583       break;\r
4584     default:\r
4585       return FALSE;\r
4586     }\r
4587     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4588     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4589     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4590     fromX = fromY = -1;\r
4591     if (!appData.highlightLastMove) {\r
4592       ClearHighlights();\r
4593       DrawPosition(FALSE, NULL);\r
4594     }\r
4595     return TRUE;\r
4596   }\r
4597   return FALSE;\r
4598 }\r
4599 \r
4600 /* Pop up promotion dialog */\r
4601 VOID\r
4602 PromotionPopup(HWND hwnd)\r
4603 {\r
4604   FARPROC lpProc;\r
4605 \r
4606   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4607   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4608     hwnd, (DLGPROC)lpProc);\r
4609   FreeProcInstance(lpProc);\r
4610 }\r
4611 \r
4612 void\r
4613 PromotionPopUp(char choice)\r
4614 {\r
4615   promoStyle = (choice == '+');\r
4616   DrawPosition(TRUE, NULL);\r
4617   PromotionPopup(hwndMain);\r
4618 }\r
4619 \r
4620 VOID\r
4621 LoadGameDialog(HWND hwnd, char* title)\r
4622 {\r
4623   UINT number = 0;\r
4624   FILE *f;\r
4625   char fileTitle[MSG_SIZ];\r
4626   f = OpenFileDialog(hwnd, "rb", "",\r
4627                      appData.oldSaveStyle ? "gam" : "pgn",\r
4628                      GAME_FILT,\r
4629                      title, &number, fileTitle, NULL);\r
4630   if (f != NULL) {\r
4631     cmailMsgLoaded = FALSE;\r
4632     if (number == 0) {\r
4633       int error = GameListBuild(f);\r
4634       if (error) {\r
4635         DisplayError(_("Cannot build game list"), error);\r
4636       } else if (!ListEmpty(&gameList) &&\r
4637                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4638         GameListPopUp(f, fileTitle);\r
4639         return;\r
4640       }\r
4641       GameListDestroy();\r
4642       number = 1;\r
4643     }\r
4644     LoadGame(f, number, fileTitle, FALSE);\r
4645   }\r
4646 }\r
4647 \r
4648 int get_term_width()\r
4649 {\r
4650     HDC hdc;\r
4651     TEXTMETRIC tm;\r
4652     RECT rc;\r
4653     HFONT hfont, hold_font;\r
4654     LOGFONT lf;\r
4655     HWND hText;\r
4656 \r
4657     if (hwndConsole)\r
4658         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4659     else\r
4660         return 79;\r
4661 \r
4662     // get the text metrics\r
4663     hdc = GetDC(hText);\r
4664     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4665     if (consoleCF.dwEffects & CFE_BOLD)\r
4666         lf.lfWeight = FW_BOLD;\r
4667     if (consoleCF.dwEffects & CFE_ITALIC)\r
4668         lf.lfItalic = TRUE;\r
4669     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4670         lf.lfStrikeOut = TRUE;\r
4671     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4672         lf.lfUnderline = TRUE;\r
4673     hfont = CreateFontIndirect(&lf);\r
4674     hold_font = SelectObject(hdc, hfont);\r
4675     GetTextMetrics(hdc, &tm);\r
4676     SelectObject(hdc, hold_font);\r
4677     DeleteObject(hfont);\r
4678     ReleaseDC(hText, hdc);\r
4679 \r
4680     // get the rectangle\r
4681     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4682 \r
4683     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4684 }\r
4685 \r
4686 void UpdateICSWidth(HWND hText)\r
4687 {\r
4688     LONG old_width, new_width;\r
4689 \r
4690     new_width = get_term_width(hText, FALSE);\r
4691     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4692     if (new_width != old_width)\r
4693     {\r
4694         ics_update_width(new_width);\r
4695         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4696     }\r
4697 }\r
4698 \r
4699 VOID\r
4700 ChangedConsoleFont()\r
4701 {\r
4702   CHARFORMAT cfmt;\r
4703   CHARRANGE tmpsel, sel;\r
4704   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4705   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4706   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4707   PARAFORMAT paraf;\r
4708 \r
4709   cfmt.cbSize = sizeof(CHARFORMAT);\r
4710   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4711     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4712                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4713   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4714    * size.  This was undocumented in the version of MSVC++ that I had\r
4715    * when I wrote the code, but is apparently documented now.\r
4716    */\r
4717   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4718   cfmt.bCharSet = f->lf.lfCharSet;\r
4719   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4720   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4721   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4722   /* Why are the following seemingly needed too? */\r
4723   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4724   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4725   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4726   tmpsel.cpMin = 0;\r
4727   tmpsel.cpMax = -1; /*999999?*/\r
4728   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4729   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4730   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4731    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4732    */\r
4733   paraf.cbSize = sizeof(paraf);\r
4734   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4735   paraf.dxStartIndent = 0;\r
4736   paraf.dxOffset = WRAP_INDENT;\r
4737   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4738   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4739   UpdateICSWidth(hText);\r
4740 }\r
4741 \r
4742 /*---------------------------------------------------------------------------*\\r
4743  *\r
4744  * Window Proc for main window\r
4745  *\r
4746 \*---------------------------------------------------------------------------*/\r
4747 \r
4748 /* Process messages for main window, etc. */\r
4749 LRESULT CALLBACK\r
4750 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4751 {\r
4752   FARPROC lpProc;\r
4753   int wmId;\r
4754   char *defName;\r
4755   FILE *f;\r
4756   UINT number;\r
4757   char fileTitle[MSG_SIZ];\r
4758   static SnapData sd;\r
4759   static int peek=0;\r
4760 \r
4761   switch (message) {\r
4762 \r
4763   case WM_PAINT: /* message: repaint portion of window */\r
4764     PaintProc(hwnd);\r
4765     break;\r
4766 \r
4767   case WM_ERASEBKGND:\r
4768     if (IsIconic(hwnd)) {\r
4769       /* Cheat; change the message */\r
4770       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4771     } else {\r
4772       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4773     }\r
4774     break;\r
4775 \r
4776   case WM_LBUTTONDOWN:\r
4777   case WM_MBUTTONDOWN:\r
4778   case WM_RBUTTONDOWN:\r
4779   case WM_LBUTTONUP:\r
4780   case WM_MBUTTONUP:\r
4781   case WM_RBUTTONUP:\r
4782   case WM_MOUSEMOVE:\r
4783   case WM_MOUSEWHEEL:\r
4784     MouseEvent(hwnd, message, wParam, lParam);\r
4785     break;\r
4786 \r
4787   case WM_KEYUP:\r
4788     if((char)wParam == '\b') {\r
4789       ForwardEvent(); peek = 0;\r
4790     }\r
4791 \r
4792     JAWS_KBUP_NAVIGATION\r
4793 \r
4794     break;\r
4795 \r
4796   case WM_KEYDOWN:\r
4797     if((char)wParam == '\b') {\r
4798       if(!peek) BackwardEvent(), peek = 1;\r
4799     }\r
4800 \r
4801     JAWS_KBDOWN_NAVIGATION\r
4802 \r
4803     break;\r
4804 \r
4805   case WM_CHAR:\r
4806     \r
4807     JAWS_ALT_INTERCEPT\r
4808 \r
4809     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4810         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4811         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4812         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4813         SetFocus(h);\r
4814         SendMessage(h, message, wParam, lParam);\r
4815     } else if(lParam != KF_REPEAT) {\r
4816         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4817                 TypeInEvent((char)wParam);\r
4818         } else if((char)wParam == 003) CopyGameToClipboard();\r
4819          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4820     }\r
4821 \r
4822     break;\r
4823 \r
4824   case WM_PALETTECHANGED:\r
4825     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4826       int nnew;\r
4827       HDC hdc = GetDC(hwndMain);\r
4828       SelectPalette(hdc, hPal, TRUE);\r
4829       nnew = RealizePalette(hdc);\r
4830       if (nnew > 0) {\r
4831         paletteChanged = TRUE;\r
4832 \r
4833         InvalidateRect(hwnd, &boardRect, FALSE);\r
4834       }\r
4835       ReleaseDC(hwnd, hdc);\r
4836     }\r
4837     break;\r
4838 \r
4839   case WM_QUERYNEWPALETTE:\r
4840     if (!appData.monoMode /*&& paletteChanged*/) {\r
4841       int nnew;\r
4842       HDC hdc = GetDC(hwndMain);\r
4843       paletteChanged = FALSE;\r
4844       SelectPalette(hdc, hPal, FALSE);\r
4845       nnew = RealizePalette(hdc);\r
4846       if (nnew > 0) {\r
4847         InvalidateRect(hwnd, &boardRect, FALSE);\r
4848       }\r
4849       ReleaseDC(hwnd, hdc);\r
4850       return TRUE;\r
4851     }\r
4852     return FALSE;\r
4853 \r
4854   case WM_COMMAND: /* message: command from application menu */\r
4855     wmId    = LOWORD(wParam);\r
4856 \r
4857     switch (wmId) {\r
4858     case IDM_NewGame:\r
4859       ResetGameEvent();\r
4860       SAY("new game enter a move to play against the computer with white");\r
4861       break;\r
4862 \r
4863     case IDM_NewGameFRC:\r
4864       if( NewGameFRC() == 0 ) {\r
4865         ResetGameEvent();\r
4866       }\r
4867       break;\r
4868 \r
4869     case IDM_NewVariant:\r
4870       NewVariantPopup(hwnd);\r
4871       break;\r
4872 \r
4873     case IDM_LoadGame:\r
4874       LoadGameDialog(hwnd, _("Load Game from File"));\r
4875       break;\r
4876 \r
4877     case IDM_LoadNextGame:\r
4878       ReloadGame(1);\r
4879       break;\r
4880 \r
4881     case IDM_LoadPrevGame:\r
4882       ReloadGame(-1);\r
4883       break;\r
4884 \r
4885     case IDM_ReloadGame:\r
4886       ReloadGame(0);\r
4887       break;\r
4888 \r
4889     case IDM_LoadPosition:\r
4890       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4891         Reset(FALSE, TRUE);\r
4892       }\r
4893       number = 1;\r
4894       f = OpenFileDialog(hwnd, "rb", "",\r
4895                          appData.oldSaveStyle ? "pos" : "fen",\r
4896                          POSITION_FILT,\r
4897                          _("Load Position from File"), &number, fileTitle, NULL);\r
4898       if (f != NULL) {\r
4899         LoadPosition(f, number, fileTitle);\r
4900       }\r
4901       break;\r
4902 \r
4903     case IDM_LoadNextPosition:\r
4904       ReloadPosition(1);\r
4905       break;\r
4906 \r
4907     case IDM_LoadPrevPosition:\r
4908       ReloadPosition(-1);\r
4909       break;\r
4910 \r
4911     case IDM_ReloadPosition:\r
4912       ReloadPosition(0);\r
4913       break;\r
4914 \r
4915     case IDM_SaveGame:\r
4916       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4917       f = OpenFileDialog(hwnd, "a", defName,\r
4918                          appData.oldSaveStyle ? "gam" : "pgn",\r
4919                          GAME_FILT,\r
4920                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4921       if (f != NULL) {\r
4922         SaveGame(f, 0, "");\r
4923       }\r
4924       break;\r
4925 \r
4926     case IDM_SavePosition:\r
4927       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4928       f = OpenFileDialog(hwnd, "a", defName,\r
4929                          appData.oldSaveStyle ? "pos" : "fen",\r
4930                          POSITION_FILT,\r
4931                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4932       if (f != NULL) {\r
4933         SavePosition(f, 0, "");\r
4934       }\r
4935       break;\r
4936 \r
4937     case IDM_SaveDiagram:\r
4938       defName = "diagram";\r
4939       f = OpenFileDialog(hwnd, "wb", defName,\r
4940                          "bmp",\r
4941                          DIAGRAM_FILT,\r
4942                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4943       if (f != NULL) {\r
4944         SaveDiagram(f);\r
4945       }\r
4946       break;\r
4947 \r
4948     case IDM_SaveSelected:\r
4949       f = OpenFileDialog(hwnd, "a", "",\r
4950                          "pgn",\r
4951                          GAME_FILT,\r
4952                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4953       if (f != NULL) {\r
4954         SaveSelected(f, 0, "");\r
4955       }\r
4956       break;\r
4957 \r
4958     case IDM_CreateBook:\r
4959       CreateBookEvent();\r
4960       break;\r
4961 \r
4962     case IDM_CopyGame:\r
4963       CopyGameToClipboard();\r
4964       break;\r
4965 \r
4966     case IDM_PasteGame:\r
4967       PasteGameFromClipboard();\r
4968       break;\r
4969 \r
4970     case IDM_CopyGameListToClipboard:\r
4971       CopyGameListToClipboard();\r
4972       break;\r
4973 \r
4974     /* [AS] Autodetect FEN or PGN data */\r
4975     case IDM_PasteAny:\r
4976       PasteGameOrFENFromClipboard();\r
4977       break;\r
4978 \r
4979     /* [AS] Move history */\r
4980     case IDM_ShowMoveHistory:\r
4981         if( MoveHistoryIsUp() ) {\r
4982             MoveHistoryPopDown();\r
4983         }\r
4984         else {\r
4985             MoveHistoryPopUp();\r
4986         }\r
4987         break;\r
4988 \r
4989     /* [AS] Eval graph */\r
4990     case IDM_ShowEvalGraph:\r
4991         if( EvalGraphIsUp() ) {\r
4992             EvalGraphPopDown();\r
4993         }\r
4994         else {\r
4995             EvalGraphPopUp();\r
4996             SetFocus(hwndMain);\r
4997         }\r
4998         break;\r
4999 \r
5000     /* [AS] Engine output */\r
5001     case IDM_ShowEngineOutput:\r
5002         if( EngineOutputIsUp() ) {\r
5003             EngineOutputPopDown();\r
5004         }\r
5005         else {\r
5006             EngineOutputPopUp();\r
5007         }\r
5008         break;\r
5009 \r
5010     /* [AS] User adjudication */\r
5011     case IDM_UserAdjudication_White:\r
5012         UserAdjudicationEvent( +1 );\r
5013         break;\r
5014 \r
5015     case IDM_UserAdjudication_Black:\r
5016         UserAdjudicationEvent( -1 );\r
5017         break;\r
5018 \r
5019     case IDM_UserAdjudication_Draw:\r
5020         UserAdjudicationEvent( 0 );\r
5021         break;\r
5022 \r
5023     /* [AS] Game list options dialog */\r
5024     case IDM_GameListOptions:\r
5025       GameListOptions();\r
5026       break;\r
5027 \r
5028     case IDM_NewChat:\r
5029       ChatPopUp(NULL);\r
5030       break;\r
5031 \r
5032     case IDM_CopyPosition:\r
5033       CopyFENToClipboard();\r
5034       break;\r
5035 \r
5036     case IDM_PastePosition:\r
5037       PasteFENFromClipboard();\r
5038       break;\r
5039 \r
5040     case IDM_MailMove:\r
5041       MailMoveEvent();\r
5042       break;\r
5043 \r
5044     case IDM_ReloadCMailMsg:\r
5045       Reset(TRUE, TRUE);\r
5046       ReloadCmailMsgEvent(FALSE);\r
5047       break;\r
5048 \r
5049     case IDM_Minimize:\r
5050       ShowWindow(hwnd, SW_MINIMIZE);\r
5051       break;\r
5052 \r
5053     case IDM_Exit:\r
5054       ExitEvent(0);\r
5055       break;\r
5056 \r
5057     case IDM_MachineWhite:\r
5058       MachineWhiteEvent();\r
5059       /*\r
5060        * refresh the tags dialog only if it's visible\r
5061        */\r
5062       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5063           char *tags;\r
5064           tags = PGNTags(&gameInfo);\r
5065           TagsPopUp(tags, CmailMsg());\r
5066           free(tags);\r
5067       }\r
5068       SAY("computer starts playing white");\r
5069       break;\r
5070 \r
5071     case IDM_MachineBlack:\r
5072       MachineBlackEvent();\r
5073       /*\r
5074        * refresh the tags dialog only if it's visible\r
5075        */\r
5076       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5077           char *tags;\r
5078           tags = PGNTags(&gameInfo);\r
5079           TagsPopUp(tags, CmailMsg());\r
5080           free(tags);\r
5081       }\r
5082       SAY("computer starts playing black");\r
5083       break;\r
5084 \r
5085     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5086       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5087       break;\r
5088 \r
5089     case IDM_TwoMachines:\r
5090       TwoMachinesEvent();\r
5091       /*\r
5092        * refresh the tags dialog only if it's visible\r
5093        */\r
5094       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5095           char *tags;\r
5096           tags = PGNTags(&gameInfo);\r
5097           TagsPopUp(tags, CmailMsg());\r
5098           free(tags);\r
5099       }\r
5100       SAY("computer starts playing both sides");\r
5101       break;\r
5102 \r
5103     case IDM_AnalysisMode:\r
5104       if(AnalyzeModeEvent()) {\r
5105         SAY("analyzing current position");\r
5106       }\r
5107       break;\r
5108 \r
5109     case IDM_AnalyzeFile:\r
5110       AnalyzeFileEvent();\r
5111       break;\r
5112 \r
5113     case IDM_IcsClient:\r
5114       IcsClientEvent();\r
5115       break;\r
5116 \r
5117     case IDM_EditGame:\r
5118     case IDM_EditGame2:\r
5119       EditGameEvent();\r
5120       SAY("edit game");\r
5121       break;\r
5122 \r
5123     case IDM_EditPosition:\r
5124     case IDM_EditPosition2:\r
5125       EditPositionEvent();\r
5126       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5127       break;\r
5128 \r
5129     case IDM_Training:\r
5130       TrainingEvent();\r
5131       break;\r
5132 \r
5133     case IDM_ShowGameList:\r
5134       ShowGameListProc();\r
5135       break;\r
5136 \r
5137     case IDM_EditProgs1:\r
5138       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5139       break;\r
5140 \r
5141     case IDM_LoadProg1:\r
5142      LoadEnginePopUp(hwndMain, 0);\r
5143       break;\r
5144 \r
5145     case IDM_LoadProg2:\r
5146      LoadEnginePopUp(hwndMain, 1);\r
5147       break;\r
5148 \r
5149     case IDM_EditServers:\r
5150       EditTagsPopUp(icsNames, &icsNames);\r
5151       break;\r
5152 \r
5153     case IDM_EditTags:\r
5154     case IDM_Tags:\r
5155       EditTagsProc();\r
5156       break;\r
5157 \r
5158     case IDM_EditBook:\r
5159       EditBookEvent();\r
5160       break;\r
5161 \r
5162     case IDM_EditComment:\r
5163     case IDM_Comment:\r
5164       if (commentUp && editComment) {\r
5165         CommentPopDown();\r
5166       } else {\r
5167         EditCommentEvent();\r
5168       }\r
5169       break;\r
5170 \r
5171     case IDM_Pause:\r
5172       PauseEvent();\r
5173       break;\r
5174 \r
5175     case IDM_Accept:\r
5176       AcceptEvent();\r
5177       break;\r
5178 \r
5179     case IDM_Decline:\r
5180       DeclineEvent();\r
5181       break;\r
5182 \r
5183     case IDM_Rematch:\r
5184 \r
5185       RematchEvent();\r
5186       break;\r
5187 \r
5188     case IDM_CallFlag:\r
5189       CallFlagEvent();\r
5190       break;\r
5191 \r
5192     case IDM_Draw:\r
5193       DrawEvent();\r
5194       break;\r
5195 \r
5196     case IDM_Adjourn:\r
5197       AdjournEvent();\r
5198       break;\r
5199 \r
5200     case IDM_Abort:\r
5201       AbortEvent();\r
5202       break;\r
5203 \r
5204     case IDM_Resign:\r
5205       ResignEvent();\r
5206       break;\r
5207 \r
5208     case IDM_StopObserving:\r
5209       StopObservingEvent();\r
5210       break;\r
5211 \r
5212     case IDM_StopExamining:\r
5213       StopExaminingEvent();\r
5214       break;\r
5215 \r
5216     case IDM_Upload:\r
5217       UploadGameEvent();\r
5218       break;\r
5219 \r
5220     case IDM_TypeInMove:\r
5221       TypeInEvent('\000');\r
5222       break;\r
5223 \r
5224     case IDM_TypeInName:\r
5225       PopUpNameDialog('\000');\r
5226       break;\r
5227 \r
5228     case IDM_Backward:\r
5229       BackwardEvent();\r
5230       SetFocus(hwndMain);\r
5231       break;\r
5232 \r
5233     JAWS_MENU_ITEMS\r
5234 \r
5235     case IDM_Forward:\r
5236       ForwardEvent();\r
5237       SetFocus(hwndMain);\r
5238       break;\r
5239 \r
5240     case IDM_ToStart:\r
5241       ToStartEvent();\r
5242       SetFocus(hwndMain);\r
5243       break;\r
5244 \r
5245     case IDM_ToEnd:\r
5246       ToEndEvent();\r
5247       SetFocus(hwndMain);\r
5248       break;\r
5249 \r
5250     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5251     case OPT_GameListPrev:\r
5252       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5253       break;\r
5254 \r
5255     case IDM_Revert:\r
5256       RevertEvent(FALSE);\r
5257       break;\r
5258 \r
5259     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5260       RevertEvent(TRUE);\r
5261       break;\r
5262 \r
5263     case IDM_TruncateGame:\r
5264       TruncateGameEvent();\r
5265       break;\r
5266 \r
5267     case IDM_MoveNow:\r
5268       MoveNowEvent();\r
5269       break;\r
5270 \r
5271     case IDM_RetractMove:\r
5272       RetractMoveEvent();\r
5273       break;\r
5274 \r
5275     case IDM_FlipView:\r
5276       flipView = !flipView;\r
5277       DrawPosition(FALSE, NULL);\r
5278       break;\r
5279 \r
5280     case IDM_FlipClock:\r
5281       flipClock = !flipClock;\r
5282       DisplayBothClocks();\r
5283       DisplayLogos();\r
5284       break;\r
5285 \r
5286     case IDM_MuteSounds:\r
5287       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5288       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5289                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5290       break;\r
5291 \r
5292     case IDM_GeneralOptions:\r
5293       GeneralOptionsPopup(hwnd);\r
5294       DrawPosition(TRUE, NULL);\r
5295       break;\r
5296 \r
5297     case IDM_BoardOptions:\r
5298       BoardOptionsPopup(hwnd);\r
5299       break;\r
5300 \r
5301     case IDM_ThemeOptions:\r
5302       ThemeOptionsPopup(hwnd);\r
5303       break;\r
5304 \r
5305     case IDM_EnginePlayOptions:\r
5306       EnginePlayOptionsPopup(hwnd);\r
5307       break;\r
5308 \r
5309     case IDM_Engine1Options:\r
5310       EngineOptionsPopup(hwnd, &first);\r
5311       break;\r
5312 \r
5313     case IDM_Engine2Options:\r
5314       savedHwnd = hwnd;\r
5315       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5316       EngineOptionsPopup(hwnd, &second);\r
5317       break;\r
5318 \r
5319     case IDM_OptionsUCI:\r
5320       UciOptionsPopup(hwnd);\r
5321       break;\r
5322 \r
5323     case IDM_Tourney:\r
5324       TourneyPopup(hwnd);\r
5325       break;\r
5326 \r
5327     case IDM_IcsOptions:\r
5328       IcsOptionsPopup(hwnd);\r
5329       break;\r
5330 \r
5331     case IDM_Fonts:\r
5332       FontsOptionsPopup(hwnd);\r
5333       break;\r
5334 \r
5335     case IDM_Sounds:\r
5336       SoundOptionsPopup(hwnd);\r
5337       break;\r
5338 \r
5339     case IDM_CommPort:\r
5340       CommPortOptionsPopup(hwnd);\r
5341       break;\r
5342 \r
5343     case IDM_LoadOptions:\r
5344       LoadOptionsPopup(hwnd);\r
5345       break;\r
5346 \r
5347     case IDM_SaveOptions:\r
5348       SaveOptionsPopup(hwnd);\r
5349       break;\r
5350 \r
5351     case IDM_TimeControl:\r
5352       TimeControlOptionsPopup(hwnd);\r
5353       break;\r
5354 \r
5355     case IDM_SaveSettings:\r
5356       SaveSettings(settingsFileName);\r
5357       break;\r
5358 \r
5359     case IDM_SaveSettingsOnExit:\r
5360       saveSettingsOnExit = !saveSettingsOnExit;\r
5361       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5362                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5363                                          MF_CHECKED : MF_UNCHECKED));\r
5364       break;\r
5365 \r
5366     case IDM_Hint:\r
5367       HintEvent();\r
5368       break;\r
5369 \r
5370     case IDM_Book:\r
5371       BookEvent();\r
5372       break;\r
5373 \r
5374     case IDM_AboutGame:\r
5375       AboutGameEvent();\r
5376       break;\r
5377 \r
5378     case IDM_Debug:\r
5379       appData.debugMode = !appData.debugMode;\r
5380       if (appData.debugMode) {\r
5381         char dir[MSG_SIZ];\r
5382         GetCurrentDirectory(MSG_SIZ, dir);\r
5383         SetCurrentDirectory(installDir);\r
5384         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5385         SetCurrentDirectory(dir);\r
5386         setbuf(debugFP, NULL);\r
5387       } else {\r
5388         fclose(debugFP);\r
5389         debugFP = NULL;\r
5390       }\r
5391       break;\r
5392 \r
5393     case IDM_HELPCONTENTS:\r
5394       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5395           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5396           MessageBox (GetFocus(),\r
5397                     _("Unable to activate help"),\r
5398                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5399       }\r
5400       break;\r
5401 \r
5402     case IDM_HELPSEARCH:\r
5403         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5404             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5405         MessageBox (GetFocus(),\r
5406                     _("Unable to activate help"),\r
5407                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5408       }\r
5409       break;\r
5410 \r
5411     case IDM_HELPHELP:\r
5412       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5413         MessageBox (GetFocus(),\r
5414                     _("Unable to activate help"),\r
5415                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5416       }\r
5417       break;\r
5418 \r
5419     case IDM_ABOUT:\r
5420       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5421       DialogBox(hInst, \r
5422         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5423         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5424       FreeProcInstance(lpProc);\r
5425       break;\r
5426 \r
5427     case IDM_DirectCommand1:\r
5428       AskQuestionEvent(_("Direct Command"),\r
5429                        _("Send to chess program:"), "", "1");\r
5430       break;\r
5431     case IDM_DirectCommand2:\r
5432       AskQuestionEvent(_("Direct Command"),\r
5433                        _("Send to second chess program:"), "", "2");\r
5434       break;\r
5435 \r
5436     case EP_WhitePawn:\r
5437       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5438       fromX = fromY = -1;\r
5439       break;\r
5440 \r
5441     case EP_WhiteKnight:\r
5442       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5443       fromX = fromY = -1;\r
5444       break;\r
5445 \r
5446     case EP_WhiteBishop:\r
5447       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5448       fromX = fromY = -1;\r
5449       break;\r
5450 \r
5451     case EP_WhiteRook:\r
5452       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5453       fromX = fromY = -1;\r
5454       break;\r
5455 \r
5456     case EP_WhiteQueen:\r
5457       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5458       fromX = fromY = -1;\r
5459       break;\r
5460 \r
5461     case EP_WhiteFerz:\r
5462       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5463       fromX = fromY = -1;\r
5464       break;\r
5465 \r
5466     case EP_WhiteWazir:\r
5467       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5468       fromX = fromY = -1;\r
5469       break;\r
5470 \r
5471     case EP_WhiteAlfil:\r
5472       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5473       fromX = fromY = -1;\r
5474       break;\r
5475 \r
5476     case EP_WhiteCannon:\r
5477       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5478       fromX = fromY = -1;\r
5479       break;\r
5480 \r
5481     case EP_WhiteCardinal:\r
5482       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5483       fromX = fromY = -1;\r
5484       break;\r
5485 \r
5486     case EP_WhiteMarshall:\r
5487       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5488       fromX = fromY = -1;\r
5489       break;\r
5490 \r
5491     case EP_WhiteKing:\r
5492       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5493       fromX = fromY = -1;\r
5494       break;\r
5495 \r
5496     case EP_BlackPawn:\r
5497       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5498       fromX = fromY = -1;\r
5499       break;\r
5500 \r
5501     case EP_BlackKnight:\r
5502       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5503       fromX = fromY = -1;\r
5504       break;\r
5505 \r
5506     case EP_BlackBishop:\r
5507       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5508       fromX = fromY = -1;\r
5509       break;\r
5510 \r
5511     case EP_BlackRook:\r
5512       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5513       fromX = fromY = -1;\r
5514       break;\r
5515 \r
5516     case EP_BlackQueen:\r
5517       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5518       fromX = fromY = -1;\r
5519       break;\r
5520 \r
5521     case EP_BlackFerz:\r
5522       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5523       fromX = fromY = -1;\r
5524       break;\r
5525 \r
5526     case EP_BlackWazir:\r
5527       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5528       fromX = fromY = -1;\r
5529       break;\r
5530 \r
5531     case EP_BlackAlfil:\r
5532       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5533       fromX = fromY = -1;\r
5534       break;\r
5535 \r
5536     case EP_BlackCannon:\r
5537       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5538       fromX = fromY = -1;\r
5539       break;\r
5540 \r
5541     case EP_BlackCardinal:\r
5542       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5543       fromX = fromY = -1;\r
5544       break;\r
5545 \r
5546     case EP_BlackMarshall:\r
5547       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5548       fromX = fromY = -1;\r
5549       break;\r
5550 \r
5551     case EP_BlackKing:\r
5552       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5553       fromX = fromY = -1;\r
5554       break;\r
5555 \r
5556     case EP_EmptySquare:\r
5557       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5558       fromX = fromY = -1;\r
5559       break;\r
5560 \r
5561     case EP_ClearBoard:\r
5562       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5563       fromX = fromY = -1;\r
5564       break;\r
5565 \r
5566     case EP_White:\r
5567       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5568       fromX = fromY = -1;\r
5569       break;\r
5570 \r
5571     case EP_Black:\r
5572       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5573       fromX = fromY = -1;\r
5574       break;\r
5575 \r
5576     case EP_Promote:\r
5577       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5578       fromX = fromY = -1;\r
5579       break;\r
5580 \r
5581     case EP_Demote:\r
5582       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5583       fromX = fromY = -1;\r
5584       break;\r
5585 \r
5586     case DP_Pawn:\r
5587       DropMenuEvent(WhitePawn, fromX, fromY);\r
5588       fromX = fromY = -1;\r
5589       break;\r
5590 \r
5591     case DP_Knight:\r
5592       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5593       fromX = fromY = -1;\r
5594       break;\r
5595 \r
5596     case DP_Bishop:\r
5597       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5598       fromX = fromY = -1;\r
5599       break;\r
5600 \r
5601     case DP_Rook:\r
5602       DropMenuEvent(WhiteRook, fromX, fromY);\r
5603       fromX = fromY = -1;\r
5604       break;\r
5605 \r
5606     case DP_Queen:\r
5607       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5608       fromX = fromY = -1;\r
5609       break;\r
5610 \r
5611     case IDM_English:\r
5612       barbaric = 0; appData.language = "";\r
5613       TranslateMenus(0);\r
5614       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5615       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5616       lastChecked = wmId;\r
5617       break;\r
5618 \r
5619     default:\r
5620       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5621           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5622       else\r
5623       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5624           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5625           TranslateMenus(0);\r
5626           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5627           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5628           lastChecked = wmId;\r
5629           break;\r
5630       }\r
5631       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5632     }\r
5633     break;\r
5634 \r
5635   case WM_TIMER:\r
5636     switch (wParam) {\r
5637     case CLOCK_TIMER_ID:\r
5638       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5639       clockTimerEvent = 0;\r
5640       DecrementClocks(); /* call into back end */\r
5641       break;\r
5642     case LOAD_GAME_TIMER_ID:\r
5643       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5644       loadGameTimerEvent = 0;\r
5645       AutoPlayGameLoop(); /* call into back end */\r
5646       break;\r
5647     case ANALYSIS_TIMER_ID:\r
5648       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5649                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5650         AnalysisPeriodicEvent(0);\r
5651       } else {\r
5652         KillTimer(hwnd, analysisTimerEvent);\r
5653         analysisTimerEvent = 0;\r
5654       }\r
5655       break;\r
5656     case DELAYED_TIMER_ID:\r
5657       KillTimer(hwnd, delayedTimerEvent);\r
5658       delayedTimerEvent = 0;\r
5659       delayedTimerCallback();\r
5660       break;\r
5661     }\r
5662     break;\r
5663 \r
5664   case WM_USER_Input:\r
5665     InputEvent(hwnd, message, wParam, lParam);\r
5666     break;\r
5667 \r
5668   /* [AS] Also move "attached" child windows */\r
5669   case WM_WINDOWPOSCHANGING:\r
5670 \r
5671     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5672         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5673 \r
5674         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5675             /* Window is moving */\r
5676             RECT rcMain;\r
5677 \r
5678 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5679             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5680             rcMain.right  = wpMain.x + wpMain.width;\r
5681             rcMain.top    = wpMain.y;\r
5682             rcMain.bottom = wpMain.y + wpMain.height;\r
5683             \r
5684             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5685             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5686             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5687             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5688             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5689             wpMain.x = lpwp->x;\r
5690             wpMain.y = lpwp->y;\r
5691         }\r
5692     }\r
5693     break;\r
5694 \r
5695   /* [AS] Snapping */\r
5696   case WM_ENTERSIZEMOVE:\r
5697     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5698     if (hwnd == hwndMain) {\r
5699       doingSizing = TRUE;\r
5700       lastSizing = 0;\r
5701     }\r
5702     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5703     break;\r
5704 \r
5705   case WM_SIZING:\r
5706     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5707     if (hwnd == hwndMain) {\r
5708       lastSizing = wParam;\r
5709     }\r
5710     break;\r
5711 \r
5712   case WM_MOVING:\r
5713     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5714       return OnMoving( &sd, hwnd, wParam, lParam );\r
5715 \r
5716   case WM_EXITSIZEMOVE:\r
5717     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5718     if (hwnd == hwndMain) {\r
5719       RECT client;\r
5720       doingSizing = FALSE;\r
5721       InvalidateRect(hwnd, &boardRect, FALSE);\r
5722       GetClientRect(hwnd, &client);\r
5723       ResizeBoard(client.right, client.bottom, lastSizing);\r
5724       lastSizing = 0;\r
5725       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5726     }\r
5727     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5728     break;\r
5729 \r
5730   case WM_DESTROY: /* message: window being destroyed */\r
5731     PostQuitMessage(0);\r
5732     break;\r
5733 \r
5734   case WM_CLOSE:\r
5735     if (hwnd == hwndMain) {\r
5736       ExitEvent(0);\r
5737     }\r
5738     break;\r
5739 \r
5740   default:      /* Passes it on if unprocessed */\r
5741     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5742   }\r
5743 \r
5744 \r
5745   return 0;\r
5746 }\r
5747 \r
5748 /*---------------------------------------------------------------------------*\\r
5749  *\r
5750  * Misc utility routines\r
5751  *\r
5752 \*---------------------------------------------------------------------------*/\r
5753 \r
5754 /*\r
5755  * Decent random number generator, at least not as bad as Windows\r
5756  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5757  */\r
5758 unsigned int randstate;\r
5759 \r
5760 int\r
5761 myrandom(void)\r
5762 {\r
5763   randstate = randstate * 1664525 + 1013904223;\r
5764   return (int) randstate & 0x7fffffff;\r
5765 }\r
5766 \r
5767 void\r
5768 mysrandom(unsigned int seed)\r
5769 {\r
5770   randstate = seed;\r
5771 }\r
5772 \r
5773 \r
5774 /* \r
5775  * returns TRUE if user selects a different color, FALSE otherwise \r
5776  */\r
5777 \r
5778 BOOL\r
5779 ChangeColor(HWND hwnd, COLORREF *which)\r
5780 {\r
5781   static BOOL firstTime = TRUE;\r
5782   static DWORD customColors[16];\r
5783   CHOOSECOLOR cc;\r
5784   COLORREF newcolor;\r
5785   int i;\r
5786   ColorClass ccl;\r
5787 \r
5788   if (firstTime) {\r
5789     /* Make initial colors in use available as custom colors */\r
5790     /* Should we put the compiled-in defaults here instead? */\r
5791     i = 0;\r
5792     customColors[i++] = lightSquareColor & 0xffffff;\r
5793     customColors[i++] = darkSquareColor & 0xffffff;\r
5794     customColors[i++] = whitePieceColor & 0xffffff;\r
5795     customColors[i++] = blackPieceColor & 0xffffff;\r
5796     customColors[i++] = highlightSquareColor & 0xffffff;\r
5797     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5798 \r
5799     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5800       customColors[i++] = textAttribs[ccl].color;\r
5801     }\r
5802     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5803     firstTime = FALSE;\r
5804   }\r
5805 \r
5806   cc.lStructSize = sizeof(cc);\r
5807   cc.hwndOwner = hwnd;\r
5808   cc.hInstance = NULL;\r
5809   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5810   cc.lpCustColors = (LPDWORD) customColors;\r
5811   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5812 \r
5813   if (!ChooseColor(&cc)) return FALSE;\r
5814 \r
5815   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5816   if (newcolor == *which) return FALSE;\r
5817   *which = newcolor;\r
5818   return TRUE;\r
5819 \r
5820   /*\r
5821   InitDrawingColors();\r
5822   InvalidateRect(hwnd, &boardRect, FALSE);\r
5823   */\r
5824 }\r
5825 \r
5826 BOOLEAN\r
5827 MyLoadSound(MySound *ms)\r
5828 {\r
5829   BOOL ok = FALSE;\r
5830   struct stat st;\r
5831   FILE *f;\r
5832 \r
5833   if (ms->data && ms->flag) free(ms->data);\r
5834   ms->data = NULL;\r
5835 \r
5836   switch (ms->name[0]) {\r
5837   case NULLCHAR:\r
5838     /* Silence */\r
5839     ok = TRUE;\r
5840     break;\r
5841   case '$':\r
5842     /* System sound from Control Panel.  Don't preload here. */\r
5843     ok = TRUE;\r
5844     break;\r
5845   case '!':\r
5846     if (ms->name[1] == NULLCHAR) {\r
5847       /* "!" alone = silence */\r
5848       ok = TRUE;\r
5849     } else {\r
5850       /* Builtin wave resource.  Error if not found. */\r
5851       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5852       if (h == NULL) break;\r
5853       ms->data = (void *)LoadResource(hInst, h);\r
5854       ms->flag = 0; // not maloced, so cannot be freed!\r
5855       if (h == NULL) break;\r
5856       ok = TRUE;\r
5857     }\r
5858     break;\r
5859   default:\r
5860     /* .wav file.  Error if not found. */\r
5861     f = fopen(ms->name, "rb");\r
5862     if (f == NULL) break;\r
5863     if (fstat(fileno(f), &st) < 0) break;\r
5864     ms->data = malloc(st.st_size);\r
5865     ms->flag = 1;\r
5866     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5867     fclose(f);\r
5868     ok = TRUE;\r
5869     break;\r
5870   }\r
5871   if (!ok) {\r
5872     char buf[MSG_SIZ];\r
5873       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5874     DisplayError(buf, GetLastError());\r
5875   }\r
5876   return ok;\r
5877 }\r
5878 \r
5879 BOOLEAN\r
5880 MyPlaySound(MySound *ms)\r
5881 {\r
5882   BOOLEAN ok = FALSE;\r
5883 \r
5884   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5885   switch (ms->name[0]) {\r
5886   case NULLCHAR:\r
5887         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5888     /* Silence */\r
5889     ok = TRUE;\r
5890     break;\r
5891   case '$':\r
5892     /* System sound from Control Panel (deprecated feature).\r
5893        "$" alone or an unset sound name gets default beep (still in use). */\r
5894     if (ms->name[1]) {\r
5895       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5896     }\r
5897     if (!ok) ok = MessageBeep(MB_OK);\r
5898     break; \r
5899   case '!':\r
5900     /* Builtin wave resource, or "!" alone for silence */\r
5901     if (ms->name[1]) {\r
5902       if (ms->data == NULL) return FALSE;\r
5903       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5904     } else {\r
5905       ok = TRUE;\r
5906     }\r
5907     break;\r
5908   default:\r
5909     /* .wav file.  Error if not found. */\r
5910     if (ms->data == NULL) return FALSE;\r
5911     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5912     break;\r
5913   }\r
5914   /* Don't print an error: this can happen innocently if the sound driver\r
5915      is busy; for instance, if another instance of WinBoard is playing\r
5916      a sound at about the same time. */\r
5917   return ok;\r
5918 }\r
5919 \r
5920 \r
5921 LRESULT CALLBACK\r
5922 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5923 {\r
5924   BOOL ok;\r
5925   OPENFILENAME *ofn;\r
5926   static UINT *number; /* gross that this is static */\r
5927 \r
5928   switch (message) {\r
5929   case WM_INITDIALOG: /* message: initialize dialog box */\r
5930     /* Center the dialog over the application window */\r
5931     ofn = (OPENFILENAME *) lParam;\r
5932     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5933       number = (UINT *) ofn->lCustData;\r
5934       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5935     } else {\r
5936       number = NULL;\r
5937     }\r
5938     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5939     Translate(hDlg, 1536);\r
5940     return FALSE;  /* Allow for further processing */\r
5941 \r
5942   case WM_COMMAND:\r
5943     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5944       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5945     }\r
5946     return FALSE;  /* Allow for further processing */\r
5947   }\r
5948   return FALSE;\r
5949 }\r
5950 \r
5951 UINT APIENTRY\r
5952 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5953 {\r
5954   static UINT *number;\r
5955   OPENFILENAME *ofname;\r
5956   OFNOTIFY *ofnot;\r
5957   switch (uiMsg) {\r
5958   case WM_INITDIALOG:\r
5959     Translate(hdlg, DLG_IndexNumber);\r
5960     ofname = (OPENFILENAME *)lParam;\r
5961     number = (UINT *)(ofname->lCustData);\r
5962     break;\r
5963   case WM_NOTIFY:\r
5964     ofnot = (OFNOTIFY *)lParam;\r
5965     if (ofnot->hdr.code == CDN_FILEOK) {\r
5966       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5967     }\r
5968     break;\r
5969   }\r
5970   return 0;\r
5971 }\r
5972 \r
5973 \r
5974 FILE *\r
5975 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5976                char *nameFilt, char *dlgTitle, UINT *number,\r
5977                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5978 {\r
5979   OPENFILENAME openFileName;\r
5980   char buf1[MSG_SIZ];\r
5981   FILE *f;\r
5982 \r
5983   if (fileName == NULL) fileName = buf1;\r
5984   if (defName == NULL) {\r
5985     safeStrCpy(fileName, "*.", 3 );\r
5986     strcat(fileName, defExt);\r
5987   } else {\r
5988     safeStrCpy(fileName, defName, MSG_SIZ );\r
5989   }\r
5990     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5991   if (number) *number = 0;\r
5992 \r
5993   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5994   openFileName.hwndOwner         = hwnd;\r
5995   openFileName.hInstance         = (HANDLE) hInst;\r
5996   openFileName.lpstrFilter       = nameFilt;\r
5997   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5998   openFileName.nMaxCustFilter    = 0L;\r
5999   openFileName.nFilterIndex      = 1L;\r
6000   openFileName.lpstrFile         = fileName;\r
6001   openFileName.nMaxFile          = MSG_SIZ;\r
6002   openFileName.lpstrFileTitle    = fileTitle;\r
6003   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6004   openFileName.lpstrInitialDir   = NULL;\r
6005   openFileName.lpstrTitle        = dlgTitle;\r
6006   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6007     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6008     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6009     | (oldDialog ? 0 : OFN_EXPLORER);\r
6010   openFileName.nFileOffset       = 0;\r
6011   openFileName.nFileExtension    = 0;\r
6012   openFileName.lpstrDefExt       = defExt;\r
6013   openFileName.lCustData         = (LONG) number;\r
6014   openFileName.lpfnHook          = oldDialog ?\r
6015     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6016   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6017 \r
6018   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6019                         GetOpenFileName(&openFileName)) {\r
6020     /* open the file */\r
6021     f = fopen(openFileName.lpstrFile, write);\r
6022     if (f == NULL) {\r
6023       MessageBox(hwnd, _("File open failed"), NULL,\r
6024                  MB_OK|MB_ICONEXCLAMATION);\r
6025       return NULL;\r
6026     }\r
6027   } else {\r
6028     int err = CommDlgExtendedError();\r
6029     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6030     return FALSE;\r
6031   }\r
6032   return f;\r
6033 }\r
6034 \r
6035 \r
6036 \r
6037 VOID APIENTRY\r
6038 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6039 {\r
6040   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6041 \r
6042   /*\r
6043    * Get the first pop-up menu in the menu template. This is the\r
6044    * menu that TrackPopupMenu displays.\r
6045    */\r
6046   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6047   TranslateOneMenu(10, hmenuTrackPopup);\r
6048 \r
6049   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6050 \r
6051   /*\r
6052    * TrackPopup uses screen coordinates, so convert the\r
6053    * coordinates of the mouse click to screen coordinates.\r
6054    */\r
6055   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6056 \r
6057   /* Draw and track the floating pop-up menu. */\r
6058   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6059                  pt.x, pt.y, 0, hwnd, NULL);\r
6060 \r
6061   /* Destroy the menu.*/\r
6062   DestroyMenu(hmenu);\r
6063 }\r
6064    \r
6065 typedef struct {\r
6066   HWND hDlg, hText;\r
6067   int sizeX, sizeY, newSizeX, newSizeY;\r
6068   HDWP hdwp;\r
6069 } ResizeEditPlusButtonsClosure;\r
6070 \r
6071 BOOL CALLBACK\r
6072 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6073 {\r
6074   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6075   RECT rect;\r
6076   POINT pt;\r
6077 \r
6078   if (hChild == cl->hText) return TRUE;\r
6079   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6080   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6081   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6082   ScreenToClient(cl->hDlg, &pt);\r
6083   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6084     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6085   return TRUE;\r
6086 }\r
6087 \r
6088 /* Resize a dialog that has a (rich) edit field filling most of\r
6089    the top, with a row of buttons below */\r
6090 VOID\r
6091 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6092 {\r
6093   RECT rectText;\r
6094   int newTextHeight, newTextWidth;\r
6095   ResizeEditPlusButtonsClosure cl;\r
6096   \r
6097   /*if (IsIconic(hDlg)) return;*/\r
6098   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6099   \r
6100   cl.hdwp = BeginDeferWindowPos(8);\r
6101 \r
6102   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6103   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6104   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6105   if (newTextHeight < 0) {\r
6106     newSizeY += -newTextHeight;\r
6107     newTextHeight = 0;\r
6108   }\r
6109   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6110     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6111 \r
6112   cl.hDlg = hDlg;\r
6113   cl.hText = hText;\r
6114   cl.sizeX = sizeX;\r
6115   cl.sizeY = sizeY;\r
6116   cl.newSizeX = newSizeX;\r
6117   cl.newSizeY = newSizeY;\r
6118   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6119 \r
6120   EndDeferWindowPos(cl.hdwp);\r
6121 }\r
6122 \r
6123 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6124 {\r
6125     RECT    rChild, rParent;\r
6126     int     wChild, hChild, wParent, hParent;\r
6127     int     wScreen, hScreen, xNew, yNew;\r
6128     HDC     hdc;\r
6129 \r
6130     /* Get the Height and Width of the child window */\r
6131     GetWindowRect (hwndChild, &rChild);\r
6132     wChild = rChild.right - rChild.left;\r
6133     hChild = rChild.bottom - rChild.top;\r
6134 \r
6135     /* Get the Height and Width of the parent window */\r
6136     GetWindowRect (hwndParent, &rParent);\r
6137     wParent = rParent.right - rParent.left;\r
6138     hParent = rParent.bottom - rParent.top;\r
6139 \r
6140     /* Get the display limits */\r
6141     hdc = GetDC (hwndChild);\r
6142     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6143     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6144     ReleaseDC(hwndChild, hdc);\r
6145 \r
6146     /* Calculate new X position, then adjust for screen */\r
6147     xNew = rParent.left + ((wParent - wChild) /2);\r
6148     if (xNew < 0) {\r
6149         xNew = 0;\r
6150     } else if ((xNew+wChild) > wScreen) {\r
6151         xNew = wScreen - wChild;\r
6152     }\r
6153 \r
6154     /* Calculate new Y position, then adjust for screen */\r
6155     if( mode == 0 ) {\r
6156         yNew = rParent.top  + ((hParent - hChild) /2);\r
6157     }\r
6158     else {\r
6159         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6160     }\r
6161 \r
6162     if (yNew < 0) {\r
6163         yNew = 0;\r
6164     } else if ((yNew+hChild) > hScreen) {\r
6165         yNew = hScreen - hChild;\r
6166     }\r
6167 \r
6168     /* Set it, and return */\r
6169     return SetWindowPos (hwndChild, NULL,\r
6170                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6171 }\r
6172 \r
6173 /* Center one window over another */\r
6174 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6175 {\r
6176     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6177 }\r
6178 \r
6179 /*---------------------------------------------------------------------------*\\r
6180  *\r
6181  * Startup Dialog functions\r
6182  *\r
6183 \*---------------------------------------------------------------------------*/\r
6184 void\r
6185 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6186 {\r
6187   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6188 \r
6189   while (*cd != NULL) {\r
6190     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6191     cd++;\r
6192   }\r
6193 }\r
6194 \r
6195 void\r
6196 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6197 {\r
6198   char buf1[MAX_ARG_LEN];\r
6199   int len;\r
6200 \r
6201   if (str[0] == '@') {\r
6202     FILE* f = fopen(str + 1, "r");\r
6203     if (f == NULL) {\r
6204       DisplayFatalError(str + 1, errno, 2);\r
6205       return;\r
6206     }\r
6207     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6208     fclose(f);\r
6209     buf1[len] = NULLCHAR;\r
6210     str = buf1;\r
6211   }\r
6212 \r
6213   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6214 \r
6215   for (;;) {\r
6216     char buf[MSG_SIZ];\r
6217     char *end = strchr(str, '\n');\r
6218     if (end == NULL) return;\r
6219     memcpy(buf, str, end - str);\r
6220     buf[end - str] = NULLCHAR;\r
6221     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6222     str = end + 1;\r
6223   }\r
6224 }\r
6225 \r
6226 void\r
6227 SetStartupDialogEnables(HWND hDlg)\r
6228 {\r
6229   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6230     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6231     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6232   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6233     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6234   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6235     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6236   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6237     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6238   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6239     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6240     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6241     IsDlgButtonChecked(hDlg, OPT_View));\r
6242 }\r
6243 \r
6244 char *\r
6245 QuoteForFilename(char *filename)\r
6246 {\r
6247   int dquote, space;\r
6248   dquote = strchr(filename, '"') != NULL;\r
6249   space = strchr(filename, ' ') != NULL;\r
6250   if (dquote || space) {\r
6251     if (dquote) {\r
6252       return "'";\r
6253     } else {\r
6254       return "\"";\r
6255     }\r
6256   } else {\r
6257     return "";\r
6258   }\r
6259 }\r
6260 \r
6261 VOID\r
6262 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6263 {\r
6264   char buf[MSG_SIZ];\r
6265   char *q;\r
6266 \r
6267   InitComboStringsFromOption(hwndCombo, nthnames);\r
6268   q = QuoteForFilename(nthcp);\r
6269     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6270   if (*nthdir != NULLCHAR) {\r
6271     q = QuoteForFilename(nthdir);\r
6272       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6273   }\r
6274   if (*nthcp == NULLCHAR) {\r
6275     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6276   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6277     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6278     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6279   }\r
6280 }\r
6281 \r
6282 LRESULT CALLBACK\r
6283 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6284 {\r
6285   char buf[MSG_SIZ];\r
6286   HANDLE hwndCombo;\r
6287   char *p;\r
6288 \r
6289   switch (message) {\r
6290   case WM_INITDIALOG:\r
6291     /* Center the dialog */\r
6292     CenterWindow (hDlg, GetDesktopWindow());\r
6293     Translate(hDlg, DLG_Startup);\r
6294     /* Initialize the dialog items */\r
6295     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6296                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6297                   firstChessProgramNames);\r
6298     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6299                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6300                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6301     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6302     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6303       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6304     if (*appData.icsHelper != NULLCHAR) {\r
6305       char *q = QuoteForFilename(appData.icsHelper);\r
6306       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6307     }\r
6308     if (*appData.icsHost == NULLCHAR) {\r
6309       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6310       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6311     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6312       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6313       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6314     }\r
6315 \r
6316     if (appData.icsActive) {\r
6317       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6318     }\r
6319     else if (appData.noChessProgram) {\r
6320       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6321     }\r
6322     else {\r
6323       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6324     }\r
6325 \r
6326     SetStartupDialogEnables(hDlg);\r
6327     return TRUE;\r
6328 \r
6329   case WM_COMMAND:\r
6330     switch (LOWORD(wParam)) {\r
6331     case IDOK:\r
6332       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6333         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6334         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6335         p = buf;\r
6336         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6337         ParseArgs(StringGet, &p);\r
6338         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6339         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6340         p = buf;\r
6341         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6342         ParseArgs(StringGet, &p);\r
6343         SwapEngines(singleList); // ... and then make it 'second'\r
6344 \r
6345         appData.noChessProgram = FALSE;\r
6346         appData.icsActive = FALSE;\r
6347       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6348         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6349         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6350         p = buf;\r
6351         ParseArgs(StringGet, &p);\r
6352         if (appData.zippyPlay) {\r
6353           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6354           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6355           p = buf;\r
6356           ParseArgs(StringGet, &p);\r
6357         }\r
6358       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6359         appData.noChessProgram = TRUE;\r
6360         appData.icsActive = FALSE;\r
6361       } else {\r
6362         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6363                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6364         return TRUE;\r
6365       }\r
6366       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6367         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6368         p = buf;\r
6369         ParseArgs(StringGet, &p);\r
6370       }\r
6371       EndDialog(hDlg, TRUE);\r
6372       return TRUE;\r
6373 \r
6374     case IDCANCEL:\r
6375       ExitEvent(0);\r
6376       return TRUE;\r
6377 \r
6378     case IDM_HELPCONTENTS:\r
6379       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6380         MessageBox (GetFocus(),\r
6381                     _("Unable to activate help"),\r
6382                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6383       }\r
6384       break;\r
6385 \r
6386     default:\r
6387       SetStartupDialogEnables(hDlg);\r
6388       break;\r
6389     }\r
6390     break;\r
6391   }\r
6392   return FALSE;\r
6393 }\r
6394 \r
6395 /*---------------------------------------------------------------------------*\\r
6396  *\r
6397  * About box dialog functions\r
6398  *\r
6399 \*---------------------------------------------------------------------------*/\r
6400 \r
6401 /* Process messages for "About" dialog box */\r
6402 LRESULT CALLBACK\r
6403 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6404 {\r
6405   switch (message) {\r
6406   case WM_INITDIALOG: /* message: initialize dialog box */\r
6407     /* Center the dialog over the application window */\r
6408     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6409     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6410     Translate(hDlg, ABOUTBOX);\r
6411     JAWS_COPYRIGHT\r
6412     return (TRUE);\r
6413 \r
6414   case WM_COMMAND: /* message: received a command */\r
6415     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6416         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6417       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6418       return (TRUE);\r
6419     }\r
6420     break;\r
6421   }\r
6422   return (FALSE);\r
6423 }\r
6424 \r
6425 /*---------------------------------------------------------------------------*\\r
6426  *\r
6427  * Comment Dialog functions\r
6428  *\r
6429 \*---------------------------------------------------------------------------*/\r
6430 \r
6431 LRESULT CALLBACK\r
6432 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6433 {\r
6434   static HANDLE hwndText = NULL;\r
6435   int len, newSizeX, newSizeY;\r
6436   static int sizeX, sizeY;\r
6437   char *str;\r
6438   RECT rect;\r
6439   MINMAXINFO *mmi;\r
6440 \r
6441   switch (message) {\r
6442   case WM_INITDIALOG: /* message: initialize dialog box */\r
6443     /* Initialize the dialog items */\r
6444     Translate(hDlg, DLG_EditComment);\r
6445     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6446     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6447     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6448     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6449     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6450     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6451     SetWindowText(hDlg, commentTitle);\r
6452     if (editComment) {\r
6453       SetFocus(hwndText);\r
6454     } else {\r
6455       SetFocus(GetDlgItem(hDlg, IDOK));\r
6456     }\r
6457     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6458                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6459                 MAKELPARAM(FALSE, 0));\r
6460     /* Size and position the dialog */\r
6461     if (!commentDialog) {\r
6462       commentDialog = hDlg;\r
6463       GetClientRect(hDlg, &rect);\r
6464       sizeX = rect.right;\r
6465       sizeY = rect.bottom;\r
6466       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6467           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6468         WINDOWPLACEMENT wp;\r
6469         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6470         wp.length = sizeof(WINDOWPLACEMENT);\r
6471         wp.flags = 0;\r
6472         wp.showCmd = SW_SHOW;\r
6473         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6474         wp.rcNormalPosition.left = wpComment.x;\r
6475         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6476         wp.rcNormalPosition.top = wpComment.y;\r
6477         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6478         SetWindowPlacement(hDlg, &wp);\r
6479 \r
6480         GetClientRect(hDlg, &rect);\r
6481         newSizeX = rect.right;\r
6482         newSizeY = rect.bottom;\r
6483         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6484                               newSizeX, newSizeY);\r
6485         sizeX = newSizeX;\r
6486         sizeY = newSizeY;\r
6487       }\r
6488     }\r
6489     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6490     return FALSE;\r
6491 \r
6492   case WM_COMMAND: /* message: received a command */\r
6493     switch (LOWORD(wParam)) {\r
6494     case IDOK:\r
6495       if (editComment) {\r
6496         char *p, *q;\r
6497         /* Read changed options from the dialog box */\r
6498         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6499         len = GetWindowTextLength(hwndText);\r
6500         str = (char *) malloc(len + 1);\r
6501         GetWindowText(hwndText, str, len + 1);\r
6502         p = q = str;\r
6503         while (*q) {\r
6504           if (*q == '\r')\r
6505             q++;\r
6506           else\r
6507             *p++ = *q++;\r
6508         }\r
6509         *p = NULLCHAR;\r
6510         ReplaceComment(commentIndex, str);\r
6511         free(str);\r
6512       }\r
6513       CommentPopDown();\r
6514       return TRUE;\r
6515 \r
6516     case IDCANCEL:\r
6517     case OPT_CancelComment:\r
6518       CommentPopDown();\r
6519       return TRUE;\r
6520 \r
6521     case OPT_ClearComment:\r
6522       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6523       break;\r
6524 \r
6525     case OPT_EditComment:\r
6526       EditCommentEvent();\r
6527       return TRUE;\r
6528 \r
6529     default:\r
6530       break;\r
6531     }\r
6532     break;\r
6533 \r
6534   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6535         if( wParam == OPT_CommentText ) {\r
6536             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6537 \r
6538             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6539                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6540                 POINTL pt;\r
6541                 LRESULT index;\r
6542 \r
6543                 pt.x = LOWORD( lpMF->lParam );\r
6544                 pt.y = HIWORD( lpMF->lParam );\r
6545 \r
6546                 if(lpMF->msg == WM_CHAR) {\r
6547                         CHARRANGE sel;\r
6548                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6549                         index = sel.cpMin;\r
6550                 } else\r
6551                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6552 \r
6553                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6554                 len = GetWindowTextLength(hwndText);\r
6555                 str = (char *) malloc(len + 1);\r
6556                 GetWindowText(hwndText, str, len + 1);\r
6557                 ReplaceComment(commentIndex, str);\r
6558                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6559                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6560                 free(str);\r
6561 \r
6562                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6563                 lpMF->msg = WM_USER;\r
6564 \r
6565                 return TRUE;\r
6566             }\r
6567         }\r
6568         break;\r
6569 \r
6570   case WM_SIZE:\r
6571     newSizeX = LOWORD(lParam);\r
6572     newSizeY = HIWORD(lParam);\r
6573     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6574     sizeX = newSizeX;\r
6575     sizeY = newSizeY;\r
6576     break;\r
6577 \r
6578   case WM_GETMINMAXINFO:\r
6579     /* Prevent resizing window too small */\r
6580     mmi = (MINMAXINFO *) lParam;\r
6581     mmi->ptMinTrackSize.x = 100;\r
6582     mmi->ptMinTrackSize.y = 100;\r
6583     break;\r
6584   }\r
6585   return FALSE;\r
6586 }\r
6587 \r
6588 VOID\r
6589 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6590 {\r
6591   FARPROC lpProc;\r
6592   char *p, *q;\r
6593 \r
6594   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6595 \r
6596   if (str == NULL) str = "";\r
6597   p = (char *) malloc(2 * strlen(str) + 2);\r
6598   q = p;\r
6599   while (*str) {\r
6600     if (*str == '\n') *q++ = '\r';\r
6601     *q++ = *str++;\r
6602   }\r
6603   *q = NULLCHAR;\r
6604   if (commentText != NULL) free(commentText);\r
6605 \r
6606   commentIndex = index;\r
6607   commentTitle = title;\r
6608   commentText = p;\r
6609   editComment = edit;\r
6610 \r
6611   if (commentDialog) {\r
6612     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6613     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6614   } else {\r
6615     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6616     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6617                  hwndMain, (DLGPROC)lpProc);\r
6618     FreeProcInstance(lpProc);\r
6619   }\r
6620   commentUp = TRUE;\r
6621 }\r
6622 \r
6623 \r
6624 /*---------------------------------------------------------------------------*\\r
6625  *\r
6626  * Type-in move dialog functions\r
6627  * \r
6628 \*---------------------------------------------------------------------------*/\r
6629 \r
6630 LRESULT CALLBACK\r
6631 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6632 {\r
6633   char move[MSG_SIZ];\r
6634   HWND hInput;\r
6635 \r
6636   switch (message) {\r
6637   case WM_INITDIALOG:\r
6638     move[0] = (char) lParam;\r
6639     move[1] = NULLCHAR;\r
6640     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6641     Translate(hDlg, DLG_TypeInMove);\r
6642     hInput = GetDlgItem(hDlg, OPT_Move);\r
6643     SetWindowText(hInput, move);\r
6644     SetFocus(hInput);\r
6645     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6646     return FALSE;\r
6647 \r
6648   case WM_COMMAND:\r
6649     switch (LOWORD(wParam)) {\r
6650     case IDOK:\r
6651 \r
6652       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6653       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6654       TypeInDoneEvent(move);\r
6655       EndDialog(hDlg, TRUE);\r
6656       return TRUE;\r
6657     case IDCANCEL:\r
6658       EndDialog(hDlg, FALSE);\r
6659       return TRUE;\r
6660     default:\r
6661       break;\r
6662     }\r
6663     break;\r
6664   }\r
6665   return FALSE;\r
6666 }\r
6667 \r
6668 VOID\r
6669 PopUpMoveDialog(char firstchar)\r
6670 {\r
6671     FARPROC lpProc;\r
6672 \r
6673       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6674       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6675         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6676       FreeProcInstance(lpProc);\r
6677 }\r
6678 \r
6679 /*---------------------------------------------------------------------------*\\r
6680  *\r
6681  * Type-in name dialog functions\r
6682  * \r
6683 \*---------------------------------------------------------------------------*/\r
6684 \r
6685 LRESULT CALLBACK\r
6686 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6687 {\r
6688   char move[MSG_SIZ];\r
6689   HWND hInput;\r
6690 \r
6691   switch (message) {\r
6692   case WM_INITDIALOG:\r
6693     move[0] = (char) lParam;\r
6694     move[1] = NULLCHAR;\r
6695     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6696     Translate(hDlg, DLG_TypeInName);\r
6697     hInput = GetDlgItem(hDlg, OPT_Name);\r
6698     SetWindowText(hInput, move);\r
6699     SetFocus(hInput);\r
6700     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6701     return FALSE;\r
6702 \r
6703   case WM_COMMAND:\r
6704     switch (LOWORD(wParam)) {\r
6705     case IDOK:\r
6706       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6707       appData.userName = strdup(move);\r
6708       SetUserLogo();\r
6709       SetGameInfo();\r
6710       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6711         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6712         DisplayTitle(move);\r
6713       }\r
6714 \r
6715 \r
6716       EndDialog(hDlg, TRUE);\r
6717       return TRUE;\r
6718     case IDCANCEL:\r
6719       EndDialog(hDlg, FALSE);\r
6720       return TRUE;\r
6721     default:\r
6722       break;\r
6723     }\r
6724     break;\r
6725   }\r
6726   return FALSE;\r
6727 }\r
6728 \r
6729 VOID\r
6730 PopUpNameDialog(char firstchar)\r
6731 {\r
6732     FARPROC lpProc;\r
6733     \r
6734       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6735       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6736         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6737       FreeProcInstance(lpProc);\r
6738 }\r
6739 \r
6740 /*---------------------------------------------------------------------------*\\r
6741  *\r
6742  *  Error dialogs\r
6743  * \r
6744 \*---------------------------------------------------------------------------*/\r
6745 \r
6746 /* Nonmodal error box */\r
6747 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6748                              WPARAM wParam, LPARAM lParam);\r
6749 \r
6750 VOID\r
6751 ErrorPopUp(char *title, char *content)\r
6752 {\r
6753   FARPROC lpProc;\r
6754   char *p, *q;\r
6755   BOOLEAN modal = hwndMain == NULL;\r
6756 \r
6757   p = content;\r
6758   q = errorMessage;\r
6759   while (*p) {\r
6760     if (*p == '\n') {\r
6761       if (modal) {\r
6762         *q++ = ' ';\r
6763         p++;\r
6764       } else {\r
6765         *q++ = '\r';\r
6766         *q++ = *p++;\r
6767       }\r
6768     } else {\r
6769       *q++ = *p++;\r
6770     }\r
6771   }\r
6772   *q = NULLCHAR;\r
6773   strncpy(errorTitle, title, sizeof(errorTitle));\r
6774   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6775   \r
6776   if (modal) {\r
6777     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6778   } else {\r
6779     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6780     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6781                  hwndMain, (DLGPROC)lpProc);\r
6782     FreeProcInstance(lpProc);\r
6783   }\r
6784 }\r
6785 \r
6786 VOID\r
6787 ErrorPopDown()\r
6788 {\r
6789   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6790   if (errorDialog == NULL) return;\r
6791   DestroyWindow(errorDialog);\r
6792   errorDialog = NULL;\r
6793   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6794 }\r
6795 \r
6796 LRESULT CALLBACK\r
6797 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6798 {\r
6799   RECT rChild;\r
6800 \r
6801   switch (message) {\r
6802   case WM_INITDIALOG:\r
6803     GetWindowRect(hDlg, &rChild);\r
6804 \r
6805     /*\r
6806     SetWindowPos(hDlg, NULL, rChild.left,\r
6807       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6808       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6809     */\r
6810 \r
6811     /* \r
6812         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6813         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6814         and it doesn't work when you resize the dialog.\r
6815         For now, just give it a default position.\r
6816     */\r
6817     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6818     Translate(hDlg, DLG_Error);\r
6819 \r
6820     errorDialog = hDlg;\r
6821     SetWindowText(hDlg, errorTitle);\r
6822     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6823     return FALSE;\r
6824 \r
6825   case WM_COMMAND:\r
6826     switch (LOWORD(wParam)) {\r
6827     case IDOK:\r
6828     case IDCANCEL:\r
6829       if (errorDialog == hDlg) errorDialog = NULL;\r
6830       DestroyWindow(hDlg);\r
6831       return TRUE;\r
6832 \r
6833     default:\r
6834       break;\r
6835     }\r
6836     break;\r
6837   }\r
6838   return FALSE;\r
6839 }\r
6840 \r
6841 #ifdef GOTHIC\r
6842 HWND gothicDialog = NULL;\r
6843 \r
6844 LRESULT CALLBACK\r
6845 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6846 {\r
6847   RECT rChild;\r
6848   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6849 \r
6850   switch (message) {\r
6851   case WM_INITDIALOG:\r
6852     GetWindowRect(hDlg, &rChild);\r
6853 \r
6854     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6855                                                              SWP_NOZORDER);\r
6856 \r
6857     /* \r
6858         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6859         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6860         and it doesn't work when you resize the dialog.\r
6861         For now, just give it a default position.\r
6862     */\r
6863     gothicDialog = hDlg;\r
6864     SetWindowText(hDlg, errorTitle);\r
6865     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6866     return FALSE;\r
6867 \r
6868   case WM_COMMAND:\r
6869     switch (LOWORD(wParam)) {\r
6870     case IDOK:\r
6871     case IDCANCEL:\r
6872       if (errorDialog == hDlg) errorDialog = NULL;\r
6873       DestroyWindow(hDlg);\r
6874       return TRUE;\r
6875 \r
6876     default:\r
6877       break;\r
6878     }\r
6879     break;\r
6880   }\r
6881   return FALSE;\r
6882 }\r
6883 \r
6884 VOID\r
6885 GothicPopUp(char *title, VariantClass variant)\r
6886 {\r
6887   FARPROC lpProc;\r
6888   static char *lastTitle;\r
6889 \r
6890   strncpy(errorTitle, title, sizeof(errorTitle));\r
6891   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6892 \r
6893   if(lastTitle != title && gothicDialog != NULL) {\r
6894     DestroyWindow(gothicDialog);\r
6895     gothicDialog = NULL;\r
6896   }\r
6897   if(variant != VariantNormal && gothicDialog == NULL) {\r
6898     title = lastTitle;\r
6899     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6900     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6901                  hwndMain, (DLGPROC)lpProc);\r
6902     FreeProcInstance(lpProc);\r
6903   }\r
6904 }\r
6905 #endif\r
6906 \r
6907 /*---------------------------------------------------------------------------*\\r
6908  *\r
6909  *  Ics Interaction console functions\r
6910  *\r
6911 \*---------------------------------------------------------------------------*/\r
6912 \r
6913 #define HISTORY_SIZE 64\r
6914 static char *history[HISTORY_SIZE];\r
6915 int histIn = 0, histP = 0;\r
6916 \r
6917 \r
6918 VOID\r
6919 SaveInHistory(char *cmd)\r
6920 {\r
6921   if (history[histIn] != NULL) {\r
6922     free(history[histIn]);\r
6923     history[histIn] = NULL;\r
6924   }\r
6925   if (*cmd == NULLCHAR) return;\r
6926   history[histIn] = StrSave(cmd);\r
6927   histIn = (histIn + 1) % HISTORY_SIZE;\r
6928   if (history[histIn] != NULL) {\r
6929     free(history[histIn]);\r
6930 \r
6931     history[histIn] = NULL;\r
6932   }\r
6933   histP = histIn;\r
6934 }\r
6935 \r
6936 char *\r
6937 PrevInHistory(char *cmd)\r
6938 {\r
6939   int newhp;\r
6940   if (histP == histIn) {\r
6941     if (history[histIn] != NULL) free(history[histIn]);\r
6942     history[histIn] = StrSave(cmd);\r
6943   }\r
6944   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6945   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6946   histP = newhp;\r
6947   return history[histP];\r
6948 }\r
6949 \r
6950 char *\r
6951 NextInHistory()\r
6952 {\r
6953   if (histP == histIn) return NULL;\r
6954   histP = (histP + 1) % HISTORY_SIZE;\r
6955   return history[histP];   \r
6956 }\r
6957 \r
6958 HMENU\r
6959 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6960 {\r
6961   HMENU hmenu, h;\r
6962   int i = 0;\r
6963   hmenu = LoadMenu(hInst, "TextMenu");\r
6964   h = GetSubMenu(hmenu, 0);\r
6965   while (e->item) {\r
6966     if (strcmp(e->item, "-") == 0) {\r
6967       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6968     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6969       int flags = MF_STRING, j = 0;\r
6970       if (e->item[0] == '|') {\r
6971         flags |= MF_MENUBARBREAK;\r
6972         j++;\r
6973       }\r
6974       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6975       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6976     }\r
6977     e++;\r
6978     i++;\r
6979   } \r
6980   return hmenu;\r
6981 }\r
6982 \r
6983 WNDPROC consoleTextWindowProc;\r
6984 \r
6985 void\r
6986 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6987 {\r
6988   char buf[MSG_SIZ], name[MSG_SIZ];\r
6989   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6990   CHARRANGE sel;\r
6991 \r
6992   if (!getname) {\r
6993     SetWindowText(hInput, command);\r
6994     if (immediate) {\r
6995       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6996     } else {\r
6997       sel.cpMin = 999999;\r
6998       sel.cpMax = 999999;\r
6999       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7000       SetFocus(hInput);\r
7001     }\r
7002     return;\r
7003   }    \r
7004   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7005   if (sel.cpMin == sel.cpMax) {\r
7006     /* Expand to surrounding word */\r
7007     TEXTRANGE tr;\r
7008     do {\r
7009       tr.chrg.cpMax = sel.cpMin;\r
7010       tr.chrg.cpMin = --sel.cpMin;\r
7011       if (sel.cpMin < 0) break;\r
7012       tr.lpstrText = name;\r
7013       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7014     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7015     sel.cpMin++;\r
7016 \r
7017     do {\r
7018       tr.chrg.cpMin = sel.cpMax;\r
7019       tr.chrg.cpMax = ++sel.cpMax;\r
7020       tr.lpstrText = name;\r
7021       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7022     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7023     sel.cpMax--;\r
7024 \r
7025     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7026       MessageBeep(MB_ICONEXCLAMATION);\r
7027       return;\r
7028     }\r
7029     tr.chrg = sel;\r
7030     tr.lpstrText = name;\r
7031     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7032   } else {\r
7033     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7034       MessageBeep(MB_ICONEXCLAMATION);\r
7035       return;\r
7036     }\r
7037     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7038   }\r
7039   if (immediate) {\r
7040     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7041     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7042     SetWindowText(hInput, buf);\r
7043     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7044   } else {\r
7045     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7046       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7047     SetWindowText(hInput, buf);\r
7048     sel.cpMin = 999999;\r
7049     sel.cpMax = 999999;\r
7050     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7051     SetFocus(hInput);\r
7052   }\r
7053 }\r
7054 \r
7055 LRESULT CALLBACK \r
7056 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7057 {\r
7058   HWND hInput;\r
7059   CHARRANGE sel;\r
7060 \r
7061   switch (message) {\r
7062   case WM_KEYDOWN:\r
7063     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7064     if(wParam=='R') return 0;\r
7065     switch (wParam) {\r
7066     case VK_PRIOR:\r
7067       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7068       return 0;\r
7069     case VK_NEXT:\r
7070       sel.cpMin = 999999;\r
7071       sel.cpMax = 999999;\r
7072       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7073       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7074       return 0;\r
7075     }\r
7076     break;\r
7077   case WM_CHAR:\r
7078    if(wParam != '\022') {\r
7079     if (wParam == '\t') {\r
7080       if (GetKeyState(VK_SHIFT) < 0) {\r
7081         /* shifted */\r
7082         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7083         if (buttonDesc[0].hwnd) {\r
7084           SetFocus(buttonDesc[0].hwnd);\r
7085         } else {\r
7086           SetFocus(hwndMain);\r
7087         }\r
7088       } else {\r
7089         /* unshifted */\r
7090         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7091       }\r
7092     } else {\r
7093       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7094       JAWS_DELETE( SetFocus(hInput); )\r
7095       SendMessage(hInput, message, wParam, lParam);\r
7096     }\r
7097     return 0;\r
7098    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7099    lParam = -1;\r
7100   case WM_RBUTTONDOWN:\r
7101     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7102       /* Move selection here if it was empty */\r
7103       POINT pt;\r
7104       pt.x = LOWORD(lParam);\r
7105       pt.y = HIWORD(lParam);\r
7106       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7107       if (sel.cpMin == sel.cpMax) {\r
7108         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7109         sel.cpMax = sel.cpMin;\r
7110         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7111       }\r
7112       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7113 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7114       POINT pt;\r
7115       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7116       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7117       if (sel.cpMin == sel.cpMax) {\r
7118         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7119         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7120       }\r
7121       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7122         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7123       }\r
7124       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7125       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7126       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7127       MenuPopup(hwnd, pt, hmenu, -1);\r
7128 }\r
7129     }\r
7130     return 0;\r
7131   case WM_RBUTTONUP:\r
7132     if (GetKeyState(VK_SHIFT) & ~1) {\r
7133       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7134         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7135     }\r
7136     return 0;\r
7137   case WM_PASTE:\r
7138     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7139     SetFocus(hInput);\r
7140     return SendMessage(hInput, message, wParam, lParam);\r
7141   case WM_MBUTTONDOWN:\r
7142     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7143   case WM_COMMAND:\r
7144     switch (LOWORD(wParam)) {\r
7145     case IDM_QuickPaste:\r
7146       {\r
7147         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7148         if (sel.cpMin == sel.cpMax) {\r
7149           MessageBeep(MB_ICONEXCLAMATION);\r
7150           return 0;\r
7151         }\r
7152         SendMessage(hwnd, WM_COPY, 0, 0);\r
7153         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7154         SendMessage(hInput, WM_PASTE, 0, 0);\r
7155         SetFocus(hInput);\r
7156         return 0;\r
7157       }\r
7158     case IDM_Cut:\r
7159       SendMessage(hwnd, WM_CUT, 0, 0);\r
7160       return 0;\r
7161     case IDM_Paste:\r
7162       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7163       return 0;\r
7164     case IDM_Copy:\r
7165       SendMessage(hwnd, WM_COPY, 0, 0);\r
7166       return 0;\r
7167     default:\r
7168       {\r
7169         int i = LOWORD(wParam) - IDM_CommandX;\r
7170         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7171             icsTextMenuEntry[i].command != NULL) {\r
7172           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7173                    icsTextMenuEntry[i].getname,\r
7174                    icsTextMenuEntry[i].immediate);\r
7175           return 0;\r
7176         }\r
7177       }\r
7178       break;\r
7179     }\r
7180     break;\r
7181   }\r
7182   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7183 }\r
7184 \r
7185 WNDPROC consoleInputWindowProc;\r
7186 \r
7187 LRESULT CALLBACK\r
7188 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7189 {\r
7190   char buf[MSG_SIZ];\r
7191   char *p;\r
7192   static BOOL sendNextChar = FALSE;\r
7193   static BOOL quoteNextChar = FALSE;\r
7194   InputSource *is = consoleInputSource;\r
7195   CHARFORMAT cf;\r
7196   CHARRANGE sel;\r
7197 \r
7198   switch (message) {\r
7199   case WM_CHAR:\r
7200     if (!appData.localLineEditing || sendNextChar) {\r
7201       is->buf[0] = (CHAR) wParam;\r
7202       is->count = 1;\r
7203       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7204       sendNextChar = FALSE;\r
7205       return 0;\r
7206     }\r
7207     if (quoteNextChar) {\r
7208       buf[0] = (char) wParam;\r
7209       buf[1] = NULLCHAR;\r
7210       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7211       quoteNextChar = FALSE;\r
7212       return 0;\r
7213     }\r
7214     switch (wParam) {\r
7215     case '\r':   /* Enter key */\r
7216       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7217       if (consoleEcho) SaveInHistory(is->buf);\r
7218       is->buf[is->count++] = '\n';\r
7219       is->buf[is->count] = NULLCHAR;\r
7220       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7221       if (consoleEcho) {\r
7222         ConsoleOutput(is->buf, is->count, TRUE);\r
7223       } else if (appData.localLineEditing) {\r
7224         ConsoleOutput("\n", 1, TRUE);\r
7225       }\r
7226       /* fall thru */\r
7227     case '\033': /* Escape key */\r
7228       SetWindowText(hwnd, "");\r
7229       cf.cbSize = sizeof(CHARFORMAT);\r
7230       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7231       if (consoleEcho) {\r
7232         cf.crTextColor = textAttribs[ColorNormal].color;\r
7233       } else {\r
7234         cf.crTextColor = COLOR_ECHOOFF;\r
7235       }\r
7236       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7237       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7238       return 0;\r
7239     case '\t':   /* Tab key */\r
7240       if (GetKeyState(VK_SHIFT) < 0) {\r
7241         /* shifted */\r
7242         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7243       } else {\r
7244         /* unshifted */\r
7245         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7246         if (buttonDesc[0].hwnd) {\r
7247           SetFocus(buttonDesc[0].hwnd);\r
7248         } else {\r
7249           SetFocus(hwndMain);\r
7250         }\r
7251       }\r
7252       return 0;\r
7253     case '\023': /* Ctrl+S */\r
7254       sendNextChar = TRUE;\r
7255       return 0;\r
7256     case '\021': /* Ctrl+Q */\r
7257       quoteNextChar = TRUE;\r
7258       return 0;\r
7259     JAWS_REPLAY\r
7260     default:\r
7261       break;\r
7262     }\r
7263     break;\r
7264   case WM_KEYDOWN:\r
7265     switch (wParam) {\r
7266     case VK_UP:\r
7267       GetWindowText(hwnd, buf, MSG_SIZ);\r
7268       p = PrevInHistory(buf);\r
7269       if (p != NULL) {\r
7270         SetWindowText(hwnd, p);\r
7271         sel.cpMin = 999999;\r
7272         sel.cpMax = 999999;\r
7273         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7274         return 0;\r
7275       }\r
7276       break;\r
7277     case VK_DOWN:\r
7278       p = NextInHistory();\r
7279       if (p != NULL) {\r
7280         SetWindowText(hwnd, p);\r
7281         sel.cpMin = 999999;\r
7282         sel.cpMax = 999999;\r
7283         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7284         return 0;\r
7285       }\r
7286       break;\r
7287     case VK_HOME:\r
7288     case VK_END:\r
7289       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7290       /* fall thru */\r
7291     case VK_PRIOR:\r
7292     case VK_NEXT:\r
7293       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7294       return 0;\r
7295     }\r
7296     break;\r
7297   case WM_MBUTTONDOWN:\r
7298     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7299       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7300     break;\r
7301   case WM_RBUTTONUP:\r
7302     if (GetKeyState(VK_SHIFT) & ~1) {\r
7303       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7304         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7305     } else {\r
7306       POINT pt;\r
7307       HMENU hmenu;\r
7308       hmenu = LoadMenu(hInst, "InputMenu");\r
7309       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7310       if (sel.cpMin == sel.cpMax) {\r
7311         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7312         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7313       }\r
7314       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7315         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7316       }\r
7317       pt.x = LOWORD(lParam);\r
7318       pt.y = HIWORD(lParam);\r
7319       MenuPopup(hwnd, pt, hmenu, -1);\r
7320     }\r
7321     return 0;\r
7322   case WM_COMMAND:\r
7323     switch (LOWORD(wParam)) { \r
7324     case IDM_Undo:\r
7325       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7326       return 0;\r
7327     case IDM_SelectAll:\r
7328       sel.cpMin = 0;\r
7329       sel.cpMax = -1; /*999999?*/\r
7330       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7331       return 0;\r
7332     case IDM_Cut:\r
7333       SendMessage(hwnd, WM_CUT, 0, 0);\r
7334       return 0;\r
7335     case IDM_Paste:\r
7336       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7337       return 0;\r
7338     case IDM_Copy:\r
7339       SendMessage(hwnd, WM_COPY, 0, 0);\r
7340       return 0;\r
7341     }\r
7342     break;\r
7343   }\r
7344   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7345 }\r
7346 \r
7347 #define CO_MAX  100000\r
7348 #define CO_TRIM   1000\r
7349 \r
7350 LRESULT CALLBACK\r
7351 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7352 {\r
7353   static SnapData sd;\r
7354   HWND hText, hInput;\r
7355   RECT rect;\r
7356   static int sizeX, sizeY;\r
7357   int newSizeX, newSizeY;\r
7358   MINMAXINFO *mmi;\r
7359   WORD wMask;\r
7360 \r
7361   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7362   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7363 \r
7364   switch (message) {\r
7365   case WM_NOTIFY:\r
7366     if (((NMHDR*)lParam)->code == EN_LINK)\r
7367     {\r
7368       ENLINK *pLink = (ENLINK*)lParam;\r
7369       if (pLink->msg == WM_LBUTTONUP)\r
7370       {\r
7371         TEXTRANGE tr;\r
7372 \r
7373         tr.chrg = pLink->chrg;\r
7374         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7375         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7376         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7377         free(tr.lpstrText);\r
7378       }\r
7379     }\r
7380     break;\r
7381   case WM_INITDIALOG: /* message: initialize dialog box */\r
7382     hwndConsole = hDlg;\r
7383     SetFocus(hInput);\r
7384     consoleTextWindowProc = (WNDPROC)\r
7385       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7386     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7387     consoleInputWindowProc = (WNDPROC)\r
7388       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7389     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7390     Colorize(ColorNormal, TRUE);\r
7391     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7392     ChangedConsoleFont();\r
7393     GetClientRect(hDlg, &rect);\r
7394     sizeX = rect.right;\r
7395     sizeY = rect.bottom;\r
7396     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7397         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7398       WINDOWPLACEMENT wp;\r
7399       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7400       wp.length = sizeof(WINDOWPLACEMENT);\r
7401       wp.flags = 0;\r
7402       wp.showCmd = SW_SHOW;\r
7403       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7404       wp.rcNormalPosition.left = wpConsole.x;\r
7405       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7406       wp.rcNormalPosition.top = wpConsole.y;\r
7407       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7408       SetWindowPlacement(hDlg, &wp);\r
7409     }\r
7410 \r
7411    // [HGM] Chessknight's change 2004-07-13\r
7412    else { /* Determine Defaults */\r
7413        WINDOWPLACEMENT wp;\r
7414        wpConsole.x = wpMain.width + 1;\r
7415        wpConsole.y = wpMain.y;\r
7416        wpConsole.width = screenWidth -  wpMain.width;\r
7417        wpConsole.height = wpMain.height;\r
7418        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7419        wp.length = sizeof(WINDOWPLACEMENT);\r
7420        wp.flags = 0;\r
7421        wp.showCmd = SW_SHOW;\r
7422        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7423        wp.rcNormalPosition.left = wpConsole.x;\r
7424        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7425        wp.rcNormalPosition.top = wpConsole.y;\r
7426        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7427        SetWindowPlacement(hDlg, &wp);\r
7428     }\r
7429 \r
7430    // Allow hText to highlight URLs and send notifications on them\r
7431    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7432    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7433    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7434    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7435 \r
7436     return FALSE;\r
7437 \r
7438   case WM_SETFOCUS:\r
7439     SetFocus(hInput);\r
7440     return 0;\r
7441 \r
7442   case WM_CLOSE:\r
7443     ExitEvent(0);\r
7444     /* not reached */\r
7445     break;\r
7446 \r
7447   case WM_SIZE:\r
7448     if (IsIconic(hDlg)) break;\r
7449     newSizeX = LOWORD(lParam);\r
7450     newSizeY = HIWORD(lParam);\r
7451     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7452       RECT rectText, rectInput;\r
7453       POINT pt;\r
7454       int newTextHeight, newTextWidth;\r
7455       GetWindowRect(hText, &rectText);\r
7456       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7457       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7458       if (newTextHeight < 0) {\r
7459         newSizeY += -newTextHeight;\r
7460         newTextHeight = 0;\r
7461       }\r
7462       SetWindowPos(hText, NULL, 0, 0,\r
7463         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7464       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7465       pt.x = rectInput.left;\r
7466       pt.y = rectInput.top + newSizeY - sizeY;\r
7467       ScreenToClient(hDlg, &pt);\r
7468       SetWindowPos(hInput, NULL, \r
7469         pt.x, pt.y, /* needs client coords */   \r
7470         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7471         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7472     }\r
7473     sizeX = newSizeX;\r
7474     sizeY = newSizeY;\r
7475     break;\r
7476 \r
7477   case WM_GETMINMAXINFO:\r
7478     /* Prevent resizing window too small */\r
7479     mmi = (MINMAXINFO *) lParam;\r
7480     mmi->ptMinTrackSize.x = 100;\r
7481     mmi->ptMinTrackSize.y = 100;\r
7482     break;\r
7483 \r
7484   /* [AS] Snapping */\r
7485   case WM_ENTERSIZEMOVE:\r
7486     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7487 \r
7488   case WM_SIZING:\r
7489     return OnSizing( &sd, hDlg, wParam, lParam );\r
7490 \r
7491   case WM_MOVING:\r
7492     return OnMoving( &sd, hDlg, wParam, lParam );\r
7493 \r
7494   case WM_EXITSIZEMOVE:\r
7495         UpdateICSWidth(hText);\r
7496     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7497   }\r
7498 \r
7499   return DefWindowProc(hDlg, message, wParam, lParam);\r
7500 }\r
7501 \r
7502 \r
7503 VOID\r
7504 ConsoleCreate()\r
7505 {\r
7506   HWND hCons;\r
7507   if (hwndConsole) return;\r
7508   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7509   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7510 }\r
7511 \r
7512 \r
7513 VOID\r
7514 ConsoleOutput(char* data, int length, int forceVisible)\r
7515 {\r
7516   HWND hText;\r
7517   int trim, exlen;\r
7518   char *p, *q;\r
7519   char buf[CO_MAX+1];\r
7520   POINT pEnd;\r
7521   RECT rect;\r
7522   static int delayLF = 0;\r
7523   CHARRANGE savesel, sel;\r
7524 \r
7525   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7526   p = data;\r
7527   q = buf;\r
7528   if (delayLF) {\r
7529     *q++ = '\r';\r
7530     *q++ = '\n';\r
7531     delayLF = 0;\r
7532   }\r
7533   while (length--) {\r
7534     if (*p == '\n') {\r
7535       if (*++p) {\r
7536         *q++ = '\r';\r
7537         *q++ = '\n';\r
7538       } else {\r
7539         delayLF = 1;\r
7540       }\r
7541     } else if (*p == '\007') {\r
7542        MyPlaySound(&sounds[(int)SoundBell]);\r
7543        p++;\r
7544     } else {\r
7545       *q++ = *p++;\r
7546     }\r
7547   }\r
7548   *q = NULLCHAR;\r
7549   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7550   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7551   /* Save current selection */\r
7552   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7553   exlen = GetWindowTextLength(hText);\r
7554   /* Find out whether current end of text is visible */\r
7555   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7556   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7557   /* Trim existing text if it's too long */\r
7558   if (exlen + (q - buf) > CO_MAX) {\r
7559     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7560     sel.cpMin = 0;\r
7561     sel.cpMax = trim;\r
7562     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7563     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7564     exlen -= trim;\r
7565     savesel.cpMin -= trim;\r
7566     savesel.cpMax -= trim;\r
7567     if (exlen < 0) exlen = 0;\r
7568     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7569     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7570   }\r
7571   /* Append the new text */\r
7572   sel.cpMin = exlen;\r
7573   sel.cpMax = exlen;\r
7574   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7575   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7576   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7577   if (forceVisible || exlen == 0 ||\r
7578       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7579        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7580     /* Scroll to make new end of text visible if old end of text\r
7581        was visible or new text is an echo of user typein */\r
7582     sel.cpMin = 9999999;\r
7583     sel.cpMax = 9999999;\r
7584     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7585     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7586     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7587     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7588   }\r
7589   if (savesel.cpMax == exlen || forceVisible) {\r
7590     /* Move insert point to new end of text if it was at the old\r
7591        end of text or if the new text is an echo of user typein */\r
7592     sel.cpMin = 9999999;\r
7593     sel.cpMax = 9999999;\r
7594     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7595   } else {\r
7596     /* Restore previous selection */\r
7597     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7598   }\r
7599   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7600 }\r
7601 \r
7602 /*---------*/\r
7603 \r
7604 \r
7605 void\r
7606 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7607 {\r
7608   char buf[100];\r
7609   char *str;\r
7610   COLORREF oldFg, oldBg;\r
7611   HFONT oldFont;\r
7612   RECT rect;\r
7613 \r
7614   if(copyNumber > 1)\r
7615     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7616 \r
7617   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7618   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7619   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7620 \r
7621   rect.left = x;\r
7622   rect.right = x + squareSize;\r
7623   rect.top  = y;\r
7624   rect.bottom = y + squareSize;\r
7625   str = buf;\r
7626 \r
7627   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7628                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7629              y, ETO_CLIPPED|ETO_OPAQUE,\r
7630              &rect, str, strlen(str), NULL);\r
7631 \r
7632   (void) SetTextColor(hdc, oldFg);\r
7633   (void) SetBkColor(hdc, oldBg);\r
7634   (void) SelectObject(hdc, oldFont);\r
7635 }\r
7636 \r
7637 void\r
7638 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7639               RECT *rect, char *color, char *flagFell)\r
7640 {\r
7641   char buf[100];\r
7642   char *str;\r
7643   COLORREF oldFg, oldBg;\r
7644   HFONT oldFont;\r
7645 \r
7646   if (twoBoards && partnerUp) return;\r
7647   if (appData.clockMode) {\r
7648     if (tinyLayout)\r
7649       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7650     else\r
7651       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7652     str = buf;\r
7653   } else {\r
7654     str = color;\r
7655   }\r
7656 \r
7657   if (highlight) {\r
7658     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7659     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7660   } else {\r
7661     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7662     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7663   }\r
7664   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7665 \r
7666   JAWS_SILENCE\r
7667 \r
7668   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7669              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7670              rect, str, strlen(str), NULL);\r
7671   if(logoHeight > 0 && appData.clockMode) {\r
7672       RECT r;\r
7673       str += strlen(color)+2;\r
7674       r.top = rect->top + logoHeight/2;\r
7675       r.left = rect->left;\r
7676       r.right = rect->right;\r
7677       r.bottom = rect->bottom;\r
7678       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7679                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7680                  &r, str, strlen(str), NULL);\r
7681   }\r
7682   (void) SetTextColor(hdc, oldFg);\r
7683   (void) SetBkColor(hdc, oldBg);\r
7684   (void) SelectObject(hdc, oldFont);\r
7685 }\r
7686 \r
7687 \r
7688 int\r
7689 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7690            OVERLAPPED *ovl)\r
7691 {\r
7692   int ok, err;\r
7693 \r
7694   /* [AS]  */\r
7695   if( count <= 0 ) {\r
7696     if (appData.debugMode) {\r
7697       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7698     }\r
7699 \r
7700     return ERROR_INVALID_USER_BUFFER;\r
7701   }\r
7702 \r
7703   ResetEvent(ovl->hEvent);\r
7704   ovl->Offset = ovl->OffsetHigh = 0;\r
7705   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7706   if (ok) {\r
7707     err = NO_ERROR;\r
7708   } else {\r
7709     err = GetLastError();\r
7710     if (err == ERROR_IO_PENDING) {\r
7711       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7712       if (ok)\r
7713         err = NO_ERROR;\r
7714       else\r
7715         err = GetLastError();\r
7716     }\r
7717   }\r
7718   return err;\r
7719 }\r
7720 \r
7721 int\r
7722 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7723             OVERLAPPED *ovl)\r
7724 {\r
7725   int ok, err;\r
7726 \r
7727   ResetEvent(ovl->hEvent);\r
7728   ovl->Offset = ovl->OffsetHigh = 0;\r
7729   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7730   if (ok) {\r
7731     err = NO_ERROR;\r
7732   } else {\r
7733     err = GetLastError();\r
7734     if (err == ERROR_IO_PENDING) {\r
7735       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7736       if (ok)\r
7737         err = NO_ERROR;\r
7738       else\r
7739         err = GetLastError();\r
7740     }\r
7741 \r
7742   }\r
7743   return err;\r
7744 }\r
7745 \r
7746 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7747 void CheckForInputBufferFull( InputSource * is )\r
7748 {\r
7749     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7750         /* Look for end of line */\r
7751         char * p = is->buf;\r
7752         \r
7753         while( p < is->next && *p != '\n' ) {\r
7754             p++;\r
7755         }\r
7756 \r
7757         if( p >= is->next ) {\r
7758             if (appData.debugMode) {\r
7759                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7760             }\r
7761 \r
7762             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7763             is->count = (DWORD) -1;\r
7764             is->next = is->buf;\r
7765         }\r
7766     }\r
7767 }\r
7768 \r
7769 DWORD\r
7770 InputThread(LPVOID arg)\r
7771 {\r
7772   InputSource *is;\r
7773   OVERLAPPED ovl;\r
7774 \r
7775   is = (InputSource *) arg;\r
7776   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7777   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7778   while (is->hThread != NULL) {\r
7779     is->error = DoReadFile(is->hFile, is->next,\r
7780                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7781                            &is->count, &ovl);\r
7782     if (is->error == NO_ERROR) {\r
7783       is->next += is->count;\r
7784     } else {\r
7785       if (is->error == ERROR_BROKEN_PIPE) {\r
7786         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7787         is->count = 0;\r
7788       } else {\r
7789         is->count = (DWORD) -1;\r
7790         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7791         break; \r
7792       }\r
7793     }\r
7794 \r
7795     CheckForInputBufferFull( is );\r
7796 \r
7797     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7798 \r
7799     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7800 \r
7801     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7802   }\r
7803 \r
7804   CloseHandle(ovl.hEvent);\r
7805   CloseHandle(is->hFile);\r
7806 \r
7807   if (appData.debugMode) {\r
7808     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7809   }\r
7810 \r
7811   return 0;\r
7812 }\r
7813 \r
7814 \r
7815 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7816 DWORD\r
7817 NonOvlInputThread(LPVOID arg)\r
7818 {\r
7819   InputSource *is;\r
7820   char *p, *q;\r
7821   int i;\r
7822   char prev;\r
7823 \r
7824   is = (InputSource *) arg;\r
7825   while (is->hThread != NULL) {\r
7826     is->error = ReadFile(is->hFile, is->next,\r
7827                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7828                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7829     if (is->error == NO_ERROR) {\r
7830       /* Change CRLF to LF */\r
7831       if (is->next > is->buf) {\r
7832         p = is->next - 1;\r
7833         i = is->count + 1;\r
7834       } else {\r
7835         p = is->next;\r
7836         i = is->count;\r
7837       }\r
7838       q = p;\r
7839       prev = NULLCHAR;\r
7840       while (i > 0) {\r
7841         if (prev == '\r' && *p == '\n') {\r
7842           *(q-1) = '\n';\r
7843           is->count--;\r
7844         } else { \r
7845           *q++ = *p;\r
7846         }\r
7847         prev = *p++;\r
7848         i--;\r
7849       }\r
7850       *q = NULLCHAR;\r
7851       is->next = q;\r
7852     } else {\r
7853       if (is->error == ERROR_BROKEN_PIPE) {\r
7854         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7855         is->count = 0; \r
7856       } else {\r
7857         is->count = (DWORD) -1;\r
7858       }\r
7859     }\r
7860 \r
7861     CheckForInputBufferFull( is );\r
7862 \r
7863     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7864 \r
7865     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7866 \r
7867     if (is->count < 0) break;  /* Quit on error */\r
7868   }\r
7869   CloseHandle(is->hFile);\r
7870   return 0;\r
7871 }\r
7872 \r
7873 DWORD\r
7874 SocketInputThread(LPVOID arg)\r
7875 {\r
7876   InputSource *is;\r
7877 \r
7878   is = (InputSource *) arg;\r
7879   while (is->hThread != NULL) {\r
7880     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7881     if ((int)is->count == SOCKET_ERROR) {\r
7882       is->count = (DWORD) -1;\r
7883       is->error = WSAGetLastError();\r
7884     } else {\r
7885       is->error = NO_ERROR;\r
7886       is->next += is->count;\r
7887       if (is->count == 0 && is->second == is) {\r
7888         /* End of file on stderr; quit with no message */\r
7889         break;\r
7890       }\r
7891     }\r
7892     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7893 \r
7894     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7895 \r
7896     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7897   }\r
7898   return 0;\r
7899 }\r
7900 \r
7901 VOID\r
7902 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7903 {\r
7904   InputSource *is;\r
7905 \r
7906   is = (InputSource *) lParam;\r
7907   if (is->lineByLine) {\r
7908     /* Feed in lines one by one */\r
7909     char *p = is->buf;\r
7910     char *q = p;\r
7911     while (q < is->next) {\r
7912       if (*q++ == '\n') {\r
7913         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7914         p = q;\r
7915       }\r
7916     }\r
7917     \r
7918     /* Move any partial line to the start of the buffer */\r
7919     q = is->buf;\r
7920     while (p < is->next) {\r
7921       *q++ = *p++;\r
7922     }\r
7923     is->next = q;\r
7924 \r
7925     if (is->error != NO_ERROR || is->count == 0) {\r
7926       /* Notify backend of the error.  Note: If there was a partial\r
7927          line at the end, it is not flushed through. */\r
7928       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7929     }\r
7930   } else {\r
7931     /* Feed in the whole chunk of input at once */\r
7932     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7933     is->next = is->buf;\r
7934   }\r
7935 }\r
7936 \r
7937 /*---------------------------------------------------------------------------*\\r
7938  *\r
7939  *  Menu enables. Used when setting various modes.\r
7940  *\r
7941 \*---------------------------------------------------------------------------*/\r
7942 \r
7943 typedef struct {\r
7944   int item;\r
7945   int flags;\r
7946 } Enables;\r
7947 \r
7948 VOID\r
7949 GreyRevert(Boolean grey)\r
7950 { // [HGM] vari: for retracting variations in local mode\r
7951   HMENU hmenu = GetMenu(hwndMain);\r
7952   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7953   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7954 }\r
7955 \r
7956 VOID\r
7957 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7958 {\r
7959   while (enab->item > 0) {\r
7960     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7961     enab++;\r
7962   }\r
7963 }\r
7964 \r
7965 Enables gnuEnables[] = {\r
7966   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7977   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7979 \r
7980   // Needed to switch from ncp to GNU mode on Engine Load\r
7981   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7982   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7983   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7984   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7989   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7990   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7991   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7992   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7993   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7994   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7995   { -1, -1 }\r
7996 };\r
7997 \r
7998 Enables icsEnables[] = {\r
7999   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8001   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8002   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8003   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8004   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8005   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8007   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8015   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8016   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8017   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8018   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8019   { -1, -1 }\r
8020 };\r
8021 \r
8022 #if ZIPPY\r
8023 Enables zippyEnables[] = {\r
8024   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8025   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8026   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8027   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8028   { -1, -1 }\r
8029 };\r
8030 #endif\r
8031 \r
8032 Enables ncpEnables[] = {\r
8033   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8042   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8043   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8044   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8045   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8046   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8047   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8048   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8050   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8051   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8052   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8053   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8054   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8055   { -1, -1 }\r
8056 };\r
8057 \r
8058 Enables trainingOnEnables[] = {\r
8059   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8060   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8065   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8066   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8068   { -1, -1 }\r
8069 };\r
8070 \r
8071 Enables trainingOffEnables[] = {\r
8072   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8073   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8074   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8075   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8076   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8077   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8078   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8079   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8080   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8081   { -1, -1 }\r
8082 };\r
8083 \r
8084 /* These modify either ncpEnables or gnuEnables */\r
8085 Enables cmailEnables[] = {\r
8086   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8087   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8088   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8089   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8090   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8091   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8092   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8093   { -1, -1 }\r
8094 };\r
8095 \r
8096 Enables machineThinkingEnables[] = {\r
8097   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8104   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8105   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8107   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8108   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8109   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8110 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8111   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8112   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8113   { -1, -1 }\r
8114 };\r
8115 \r
8116 Enables userThinkingEnables[] = {\r
8117   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8118   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8119   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8120   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8121   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8122   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8123   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8124   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8125   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8126   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8127   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8128   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8129   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8130 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8131   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8132   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8133   { -1, -1 }\r
8134 };\r
8135 \r
8136 /*---------------------------------------------------------------------------*\\r
8137  *\r
8138  *  Front-end interface functions exported by XBoard.\r
8139  *  Functions appear in same order as prototypes in frontend.h.\r
8140  * \r
8141 \*---------------------------------------------------------------------------*/\r
8142 VOID\r
8143 CheckMark(UINT item, int state)\r
8144 {\r
8145     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8146 }\r
8147 \r
8148 VOID\r
8149 ModeHighlight()\r
8150 {\r
8151   static UINT prevChecked = 0;\r
8152   static int prevPausing = 0;\r
8153   UINT nowChecked;\r
8154 \r
8155   if (pausing != prevPausing) {\r
8156     prevPausing = pausing;\r
8157     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8158                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8159     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8160   }\r
8161 \r
8162   switch (gameMode) {\r
8163   case BeginningOfGame:\r
8164     if (appData.icsActive)\r
8165       nowChecked = IDM_IcsClient;\r
8166     else if (appData.noChessProgram)\r
8167       nowChecked = IDM_EditGame;\r
8168     else\r
8169       nowChecked = IDM_MachineBlack;\r
8170     break;\r
8171   case MachinePlaysBlack:\r
8172     nowChecked = IDM_MachineBlack;\r
8173     break;\r
8174   case MachinePlaysWhite:\r
8175     nowChecked = IDM_MachineWhite;\r
8176     break;\r
8177   case TwoMachinesPlay:\r
8178     nowChecked = IDM_TwoMachines;\r
8179     break;\r
8180   case AnalyzeMode:\r
8181     nowChecked = IDM_AnalysisMode;\r
8182     break;\r
8183   case AnalyzeFile:\r
8184     nowChecked = IDM_AnalyzeFile;\r
8185     break;\r
8186   case EditGame:\r
8187     nowChecked = IDM_EditGame;\r
8188     break;\r
8189   case PlayFromGameFile:\r
8190     nowChecked = IDM_LoadGame;\r
8191     break;\r
8192   case EditPosition:\r
8193     nowChecked = IDM_EditPosition;\r
8194     break;\r
8195   case Training:\r
8196     nowChecked = IDM_Training;\r
8197     break;\r
8198   case IcsPlayingWhite:\r
8199   case IcsPlayingBlack:\r
8200   case IcsObserving:\r
8201   case IcsIdle:\r
8202     nowChecked = IDM_IcsClient;\r
8203     break;\r
8204   default:\r
8205   case EndOfGame:\r
8206     nowChecked = 0;\r
8207     break;\r
8208   }\r
8209   CheckMark(prevChecked, MF_UNCHECKED);\r
8210   CheckMark(nowChecked, MF_CHECKED);\r
8211   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8212 \r
8213   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8214     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8215                           MF_BYCOMMAND|MF_ENABLED);\r
8216   } else {\r
8217     (void) EnableMenuItem(GetMenu(hwndMain), \r
8218                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8219   }\r
8220 \r
8221   prevChecked = nowChecked;\r
8222 \r
8223   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8224   if (appData.icsActive) {\r
8225        if (appData.icsEngineAnalyze) {\r
8226                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8227        } else {\r
8228                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8229        }\r
8230   }\r
8231   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8232 }\r
8233 \r
8234 VOID\r
8235 SetICSMode()\r
8236 {\r
8237   HMENU hmenu = GetMenu(hwndMain);\r
8238   SetMenuEnables(hmenu, icsEnables);\r
8239   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8240     MF_BYCOMMAND|MF_ENABLED);\r
8241 #if ZIPPY\r
8242   if (appData.zippyPlay) {\r
8243     SetMenuEnables(hmenu, zippyEnables);\r
8244     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8245          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8246           MF_BYCOMMAND|MF_ENABLED);\r
8247   }\r
8248 #endif\r
8249 }\r
8250 \r
8251 VOID\r
8252 SetGNUMode()\r
8253 {\r
8254   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8255 }\r
8256 \r
8257 VOID\r
8258 SetNCPMode()\r
8259 {\r
8260   HMENU hmenu = GetMenu(hwndMain);\r
8261   SetMenuEnables(hmenu, ncpEnables);\r
8262     DrawMenuBar(hwndMain);\r
8263 }\r
8264 \r
8265 VOID\r
8266 SetCmailMode()\r
8267 {\r
8268   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8269 }\r
8270 \r
8271 VOID \r
8272 SetTrainingModeOn()\r
8273 {\r
8274   int i;\r
8275   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8276   for (i = 0; i < N_BUTTONS; i++) {\r
8277     if (buttonDesc[i].hwnd != NULL)\r
8278       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8279   }\r
8280   CommentPopDown();\r
8281 }\r
8282 \r
8283 VOID SetTrainingModeOff()\r
8284 {\r
8285   int i;\r
8286   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8287   for (i = 0; i < N_BUTTONS; i++) {\r
8288     if (buttonDesc[i].hwnd != NULL)\r
8289       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8290   }\r
8291 }\r
8292 \r
8293 \r
8294 VOID\r
8295 SetUserThinkingEnables()\r
8296 {\r
8297   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8298 }\r
8299 \r
8300 VOID\r
8301 SetMachineThinkingEnables()\r
8302 {\r
8303   HMENU hMenu = GetMenu(hwndMain);\r
8304   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8305 \r
8306   SetMenuEnables(hMenu, machineThinkingEnables);\r
8307 \r
8308   if (gameMode == MachinePlaysBlack) {\r
8309     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8310   } else if (gameMode == MachinePlaysWhite) {\r
8311     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8312   } else if (gameMode == TwoMachinesPlay) {\r
8313     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8314   }\r
8315 }\r
8316 \r
8317 \r
8318 VOID\r
8319 DisplayTitle(char *str)\r
8320 {\r
8321   char title[MSG_SIZ], *host;\r
8322   if (str[0] != NULLCHAR) {\r
8323     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8324   } else if (appData.icsActive) {\r
8325     if (appData.icsCommPort[0] != NULLCHAR)\r
8326       host = "ICS";\r
8327     else \r
8328       host = appData.icsHost;\r
8329       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8330   } else if (appData.noChessProgram) {\r
8331     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8332   } else {\r
8333     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8334     strcat(title, ": ");\r
8335     strcat(title, first.tidy);\r
8336   }\r
8337   SetWindowText(hwndMain, title);\r
8338 }\r
8339 \r
8340 \r
8341 VOID\r
8342 DisplayMessage(char *str1, char *str2)\r
8343 {\r
8344   HDC hdc;\r
8345   HFONT oldFont;\r
8346   int remain = MESSAGE_TEXT_MAX - 1;\r
8347   int len;\r
8348 \r
8349   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8350   messageText[0] = NULLCHAR;\r
8351   if (*str1) {\r
8352     len = strlen(str1);\r
8353     if (len > remain) len = remain;\r
8354     strncpy(messageText, str1, len);\r
8355     messageText[len] = NULLCHAR;\r
8356     remain -= len;\r
8357   }\r
8358   if (*str2 && remain >= 2) {\r
8359     if (*str1) {\r
8360       strcat(messageText, "  ");\r
8361       remain -= 2;\r
8362     }\r
8363     len = strlen(str2);\r
8364     if (len > remain) len = remain;\r
8365     strncat(messageText, str2, len);\r
8366   }\r
8367   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8368   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8369 \r
8370   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8371 \r
8372   SAYMACHINEMOVE();\r
8373 \r
8374   hdc = GetDC(hwndMain);\r
8375   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8376   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8377              &messageRect, messageText, strlen(messageText), NULL);\r
8378   (void) SelectObject(hdc, oldFont);\r
8379   (void) ReleaseDC(hwndMain, hdc);\r
8380 }\r
8381 \r
8382 VOID\r
8383 DisplayError(char *str, int error)\r
8384 {\r
8385   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8386   int len;\r
8387 \r
8388   if (error == 0) {\r
8389     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8390   } else {\r
8391     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8392                         NULL, error, LANG_NEUTRAL,\r
8393                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8394     if (len > 0) {\r
8395       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8396     } else {\r
8397       ErrorMap *em = errmap;\r
8398       while (em->err != 0 && em->err != error) em++;\r
8399       if (em->err != 0) {\r
8400         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8401       } else {\r
8402         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8403       }\r
8404     }\r
8405   }\r
8406   \r
8407   ErrorPopUp(_("Error"), buf);\r
8408 }\r
8409 \r
8410 \r
8411 VOID\r
8412 DisplayMoveError(char *str)\r
8413 {\r
8414   fromX = fromY = -1;\r
8415   ClearHighlights();\r
8416   DrawPosition(FALSE, NULL);\r
8417   if (appData.popupMoveErrors) {\r
8418     ErrorPopUp(_("Error"), str);\r
8419   } else {\r
8420     DisplayMessage(str, "");\r
8421     moveErrorMessageUp = TRUE;\r
8422   }\r
8423 }\r
8424 \r
8425 VOID\r
8426 DisplayFatalError(char *str, int error, int exitStatus)\r
8427 {\r
8428   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8429   int len;\r
8430   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8431 \r
8432   if (error != 0) {\r
8433     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8434                         NULL, error, LANG_NEUTRAL,\r
8435                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8436     if (len > 0) {\r
8437       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8438     } else {\r
8439       ErrorMap *em = errmap;\r
8440       while (em->err != 0 && em->err != error) em++;\r
8441       if (em->err != 0) {\r
8442         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8443       } else {\r
8444         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8445       }\r
8446     }\r
8447     str = buf;\r
8448   }\r
8449   if (appData.debugMode) {\r
8450     fprintf(debugFP, "%s: %s\n", label, str);\r
8451   }\r
8452   if (appData.popupExitMessage) {\r
8453     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8454                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8455   }\r
8456   ExitEvent(exitStatus);\r
8457 }\r
8458 \r
8459 \r
8460 VOID\r
8461 DisplayInformation(char *str)\r
8462 {\r
8463   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8464 }\r
8465 \r
8466 \r
8467 VOID\r
8468 DisplayNote(char *str)\r
8469 {\r
8470   ErrorPopUp(_("Note"), str);\r
8471 }\r
8472 \r
8473 \r
8474 typedef struct {\r
8475   char *title, *question, *replyPrefix;\r
8476   ProcRef pr;\r
8477 } QuestionParams;\r
8478 \r
8479 LRESULT CALLBACK\r
8480 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8481 {\r
8482   static QuestionParams *qp;\r
8483   char reply[MSG_SIZ];\r
8484   int len, err;\r
8485 \r
8486   switch (message) {\r
8487   case WM_INITDIALOG:\r
8488     qp = (QuestionParams *) lParam;\r
8489     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8490     Translate(hDlg, DLG_Question);\r
8491     SetWindowText(hDlg, qp->title);\r
8492     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8493     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8494     return FALSE;\r
8495 \r
8496   case WM_COMMAND:\r
8497     switch (LOWORD(wParam)) {\r
8498     case IDOK:\r
8499       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8500       if (*reply) strcat(reply, " ");\r
8501       len = strlen(reply);\r
8502       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8503       strcat(reply, "\n");\r
8504       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8505       EndDialog(hDlg, TRUE);\r
8506       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8507       return TRUE;\r
8508     case IDCANCEL:\r
8509       EndDialog(hDlg, FALSE);\r
8510       return TRUE;\r
8511     default:\r
8512       break;\r
8513     }\r
8514     break;\r
8515   }\r
8516   return FALSE;\r
8517 }\r
8518 \r
8519 VOID\r
8520 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8521 {\r
8522     QuestionParams qp;\r
8523     FARPROC lpProc;\r
8524     \r
8525     qp.title = title;\r
8526     qp.question = question;\r
8527     qp.replyPrefix = replyPrefix;\r
8528     qp.pr = pr;\r
8529     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8530     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8531       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8532     FreeProcInstance(lpProc);\r
8533 }\r
8534 \r
8535 /* [AS] Pick FRC position */\r
8536 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8537 {\r
8538     static int * lpIndexFRC;\r
8539     BOOL index_is_ok;\r
8540     char buf[16];\r
8541 \r
8542     switch( message )\r
8543     {\r
8544     case WM_INITDIALOG:\r
8545         lpIndexFRC = (int *) lParam;\r
8546 \r
8547         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8548         Translate(hDlg, DLG_NewGameFRC);\r
8549 \r
8550         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8551         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8552         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8553         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8554 \r
8555         break;\r
8556 \r
8557     case WM_COMMAND:\r
8558         switch( LOWORD(wParam) ) {\r
8559         case IDOK:\r
8560             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8561             EndDialog( hDlg, 0 );\r
8562             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8563             return TRUE;\r
8564         case IDCANCEL:\r
8565             EndDialog( hDlg, 1 );   \r
8566             return TRUE;\r
8567         case IDC_NFG_Edit:\r
8568             if( HIWORD(wParam) == EN_CHANGE ) {\r
8569                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8570 \r
8571                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8572             }\r
8573             return TRUE;\r
8574         case IDC_NFG_Random:\r
8575           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8576             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8577             return TRUE;\r
8578         }\r
8579 \r
8580         break;\r
8581     }\r
8582 \r
8583     return FALSE;\r
8584 }\r
8585 \r
8586 int NewGameFRC()\r
8587 {\r
8588     int result;\r
8589     int index = appData.defaultFrcPosition;\r
8590     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8591 \r
8592     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8593 \r
8594     if( result == 0 ) {\r
8595         appData.defaultFrcPosition = index;\r
8596     }\r
8597 \r
8598     return result;\r
8599 }\r
8600 \r
8601 /* [AS] Game list options. Refactored by HGM */\r
8602 \r
8603 HWND gameListOptionsDialog;\r
8604 \r
8605 // low-level front-end: clear text edit / list widget\r
8606 void\r
8607 \r
8608 GLT_ClearList()\r
8609 {\r
8610     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8611 }\r
8612 \r
8613 // low-level front-end: clear text edit / list widget\r
8614 void\r
8615 GLT_DeSelectList()\r
8616 {\r
8617     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8618 }\r
8619 \r
8620 // low-level front-end: append line to text edit / list widget\r
8621 void\r
8622 GLT_AddToList( char *name )\r
8623 {\r
8624     if( name != 0 ) {\r
8625             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8626     }\r
8627 }\r
8628 \r
8629 // low-level front-end: get line from text edit / list widget\r
8630 Boolean\r
8631 GLT_GetFromList( int index, char *name )\r
8632 {\r
8633     if( name != 0 ) {\r
8634             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8635                 return TRUE;\r
8636     }\r
8637     return FALSE;\r
8638 }\r
8639 \r
8640 void GLT_MoveSelection( HWND hDlg, int delta )\r
8641 {\r
8642     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8643     int idx2 = idx1 + delta;\r
8644     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8645 \r
8646     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8647         char buf[128];\r
8648 \r
8649         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8650         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8651         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8652         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8653     }\r
8654 }\r
8655 \r
8656 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8657 {\r
8658     switch( message )\r
8659     {\r
8660     case WM_INITDIALOG:\r
8661         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8662         \r
8663         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8664         Translate(hDlg, DLG_GameListOptions);\r
8665 \r
8666         /* Initialize list */\r
8667         GLT_TagsToList( lpUserGLT );\r
8668 \r
8669         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8670 \r
8671         break;\r
8672 \r
8673     case WM_COMMAND:\r
8674         switch( LOWORD(wParam) ) {\r
8675         case IDOK:\r
8676             GLT_ParseList();\r
8677             EndDialog( hDlg, 0 );\r
8678             return TRUE;\r
8679         case IDCANCEL:\r
8680             EndDialog( hDlg, 1 );\r
8681             return TRUE;\r
8682 \r
8683         case IDC_GLT_Default:\r
8684             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8685             return TRUE;\r
8686 \r
8687         case IDC_GLT_Restore:\r
8688             GLT_TagsToList( appData.gameListTags );\r
8689             return TRUE;\r
8690 \r
8691         case IDC_GLT_Up:\r
8692             GLT_MoveSelection( hDlg, -1 );\r
8693             return TRUE;\r
8694 \r
8695         case IDC_GLT_Down:\r
8696             GLT_MoveSelection( hDlg, +1 );\r
8697             return TRUE;\r
8698         }\r
8699 \r
8700         break;\r
8701     }\r
8702 \r
8703     return FALSE;\r
8704 }\r
8705 \r
8706 int GameListOptions()\r
8707 {\r
8708     int result;\r
8709     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8710 \r
8711       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8712 \r
8713     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8714 \r
8715     if( result == 0 ) {\r
8716         char *oldTags = appData.gameListTags;\r
8717         /* [AS] Memory leak here! */\r
8718         appData.gameListTags = strdup( lpUserGLT ); \r
8719         if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something\r
8720             GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all\r
8721     }\r
8722 \r
8723     return result;\r
8724 }\r
8725 \r
8726 VOID\r
8727 DisplayIcsInteractionTitle(char *str)\r
8728 {\r
8729   char consoleTitle[MSG_SIZ];\r
8730 \r
8731     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8732     SetWindowText(hwndConsole, consoleTitle);\r
8733 \r
8734     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8735       char buf[MSG_SIZ], *p = buf, *q;\r
8736         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8737       do {\r
8738         q = strchr(p, ';');\r
8739         if(q) *q++ = 0;\r
8740         if(*p) ChatPopUp(p);\r
8741       } while(p=q);\r
8742     }\r
8743 \r
8744     SetActiveWindow(hwndMain);\r
8745 }\r
8746 \r
8747 void\r
8748 DrawPosition(int fullRedraw, Board board)\r
8749 {\r
8750   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8751 }\r
8752 \r
8753 void NotifyFrontendLogin()\r
8754 {\r
8755         if (hwndConsole)\r
8756                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8757 }\r
8758 \r
8759 VOID\r
8760 ResetFrontEnd()\r
8761 {\r
8762   fromX = fromY = -1;\r
8763   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8764     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8765     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8766     dragInfo.lastpos = dragInfo.pos;\r
8767     dragInfo.start.x = dragInfo.start.y = -1;\r
8768     dragInfo.from = dragInfo.start;\r
8769     ReleaseCapture();\r
8770     DrawPosition(TRUE, NULL);\r
8771   }\r
8772   TagsPopDown();\r
8773 }\r
8774 \r
8775 \r
8776 VOID\r
8777 CommentPopUp(char *title, char *str)\r
8778 {\r
8779   HWND hwnd = GetActiveWindow();\r
8780   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8781   SAY(str);\r
8782   SetActiveWindow(hwnd);\r
8783 }\r
8784 \r
8785 VOID\r
8786 CommentPopDown(void)\r
8787 {\r
8788   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8789   if (commentDialog) {\r
8790     ShowWindow(commentDialog, SW_HIDE);\r
8791   }\r
8792   commentUp = FALSE;\r
8793 }\r
8794 \r
8795 VOID\r
8796 EditCommentPopUp(int index, char *title, char *str)\r
8797 {\r
8798   EitherCommentPopUp(index, title, str, TRUE);\r
8799 }\r
8800 \r
8801 \r
8802 int\r
8803 Roar()\r
8804 {\r
8805   MyPlaySound(&sounds[(int)SoundRoar]);\r
8806   return 1;\r
8807 }\r
8808 \r
8809 VOID\r
8810 RingBell()\r
8811 {\r
8812   MyPlaySound(&sounds[(int)SoundMove]);\r
8813 }\r
8814 \r
8815 VOID PlayIcsWinSound()\r
8816 {\r
8817   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8818 }\r
8819 \r
8820 VOID PlayIcsLossSound()\r
8821 {\r
8822   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8823 }\r
8824 \r
8825 VOID PlayIcsDrawSound()\r
8826 {\r
8827   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8828 }\r
8829 \r
8830 VOID PlayIcsUnfinishedSound()\r
8831 {\r
8832   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8833 }\r
8834 \r
8835 VOID\r
8836 PlayAlarmSound()\r
8837 {\r
8838   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8839 }\r
8840 \r
8841 VOID\r
8842 PlayTellSound()\r
8843 {\r
8844   MyPlaySound(&textAttribs[ColorTell].sound);\r
8845 }\r
8846 \r
8847 \r
8848 VOID\r
8849 EchoOn()\r
8850 {\r
8851   HWND hInput;\r
8852   consoleEcho = TRUE;\r
8853   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8854   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8855   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8856 }\r
8857 \r
8858 \r
8859 VOID\r
8860 EchoOff()\r
8861 {\r
8862   CHARFORMAT cf;\r
8863   HWND hInput;\r
8864   consoleEcho = FALSE;\r
8865   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8866   /* This works OK: set text and background both to the same color */\r
8867   cf = consoleCF;\r
8868   cf.crTextColor = COLOR_ECHOOFF;\r
8869   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8870   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8871 }\r
8872 \r
8873 /* No Raw()...? */\r
8874 \r
8875 void Colorize(ColorClass cc, int continuation)\r
8876 {\r
8877   currentColorClass = cc;\r
8878   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8879   consoleCF.crTextColor = textAttribs[cc].color;\r
8880   consoleCF.dwEffects = textAttribs[cc].effects;\r
8881   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8882 }\r
8883 \r
8884 char *\r
8885 UserName()\r
8886 {\r
8887   static char buf[MSG_SIZ];\r
8888   DWORD bufsiz = MSG_SIZ;\r
8889 \r
8890   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8891         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8892   }\r
8893   if (!GetUserName(buf, &bufsiz)) {\r
8894     /*DisplayError("Error getting user name", GetLastError());*/\r
8895     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8896   }\r
8897   return buf;\r
8898 }\r
8899 \r
8900 char *\r
8901 HostName()\r
8902 {\r
8903   static char buf[MSG_SIZ];\r
8904   DWORD bufsiz = MSG_SIZ;\r
8905 \r
8906   if (!GetComputerName(buf, &bufsiz)) {\r
8907     /*DisplayError("Error getting host name", GetLastError());*/\r
8908     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8909   }\r
8910   return buf;\r
8911 }\r
8912 \r
8913 \r
8914 int\r
8915 ClockTimerRunning()\r
8916 {\r
8917   return clockTimerEvent != 0;\r
8918 }\r
8919 \r
8920 int\r
8921 StopClockTimer()\r
8922 {\r
8923   if (clockTimerEvent == 0) return FALSE;\r
8924   KillTimer(hwndMain, clockTimerEvent);\r
8925   clockTimerEvent = 0;\r
8926   return TRUE;\r
8927 }\r
8928 \r
8929 void\r
8930 StartClockTimer(long millisec)\r
8931 {\r
8932   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8933                              (UINT) millisec, NULL);\r
8934 }\r
8935 \r
8936 void\r
8937 DisplayWhiteClock(long timeRemaining, int highlight)\r
8938 {\r
8939   HDC hdc;\r
8940   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8941 \r
8942   if(appData.noGUI) return;\r
8943   hdc = GetDC(hwndMain);\r
8944   if (!IsIconic(hwndMain)) {\r
8945     DisplayAClock(hdc, timeRemaining, highlight, \r
8946                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8947   }\r
8948   if (highlight && iconCurrent == iconBlack) {\r
8949     iconCurrent = iconWhite;\r
8950     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8951     if (IsIconic(hwndMain)) {\r
8952       DrawIcon(hdc, 2, 2, iconCurrent);\r
8953     }\r
8954   }\r
8955   (void) ReleaseDC(hwndMain, hdc);\r
8956   if (hwndConsole)\r
8957     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8958 }\r
8959 \r
8960 void\r
8961 DisplayBlackClock(long timeRemaining, int highlight)\r
8962 {\r
8963   HDC hdc;\r
8964   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8965 \r
8966 \r
8967   if(appData.noGUI) return;\r
8968   hdc = GetDC(hwndMain);\r
8969   if (!IsIconic(hwndMain)) {\r
8970     DisplayAClock(hdc, timeRemaining, highlight, \r
8971                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8972   }\r
8973   if (highlight && iconCurrent == iconWhite) {\r
8974     iconCurrent = iconBlack;\r
8975     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8976     if (IsIconic(hwndMain)) {\r
8977       DrawIcon(hdc, 2, 2, iconCurrent);\r
8978     }\r
8979   }\r
8980   (void) ReleaseDC(hwndMain, hdc);\r
8981   if (hwndConsole)\r
8982     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8983 }\r
8984 \r
8985 \r
8986 int\r
8987 LoadGameTimerRunning()\r
8988 {\r
8989   return loadGameTimerEvent != 0;\r
8990 }\r
8991 \r
8992 int\r
8993 StopLoadGameTimer()\r
8994 {\r
8995   if (loadGameTimerEvent == 0) return FALSE;\r
8996   KillTimer(hwndMain, loadGameTimerEvent);\r
8997   loadGameTimerEvent = 0;\r
8998   return TRUE;\r
8999 }\r
9000 \r
9001 void\r
9002 StartLoadGameTimer(long millisec)\r
9003 {\r
9004   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9005                                 (UINT) millisec, NULL);\r
9006 }\r
9007 \r
9008 void\r
9009 AutoSaveGame()\r
9010 {\r
9011   char *defName;\r
9012   FILE *f;\r
9013   char fileTitle[MSG_SIZ];\r
9014 \r
9015   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9016   f = OpenFileDialog(hwndMain, "a", defName,\r
9017                      appData.oldSaveStyle ? "gam" : "pgn",\r
9018                      GAME_FILT, \r
9019                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9020   if (f != NULL) {\r
9021     SaveGame(f, 0, "");\r
9022     fclose(f);\r
9023   }\r
9024 }\r
9025 \r
9026 \r
9027 void\r
9028 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9029 {\r
9030   if (delayedTimerEvent != 0) {\r
9031     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9032       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9033     }\r
9034     KillTimer(hwndMain, delayedTimerEvent);\r
9035     delayedTimerEvent = 0;\r
9036     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9037     delayedTimerCallback();\r
9038   }\r
9039   delayedTimerCallback = cb;\r
9040   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9041                                 (UINT) millisec, NULL);\r
9042 }\r
9043 \r
9044 DelayedEventCallback\r
9045 GetDelayedEvent()\r
9046 {\r
9047   if (delayedTimerEvent) {\r
9048     return delayedTimerCallback;\r
9049   } else {\r
9050     return NULL;\r
9051   }\r
9052 }\r
9053 \r
9054 void\r
9055 CancelDelayedEvent()\r
9056 {\r
9057   if (delayedTimerEvent) {\r
9058     KillTimer(hwndMain, delayedTimerEvent);\r
9059     delayedTimerEvent = 0;\r
9060   }\r
9061 }\r
9062 \r
9063 DWORD GetWin32Priority(int nice)\r
9064 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9065 /*\r
9066 REALTIME_PRIORITY_CLASS     0x00000100\r
9067 HIGH_PRIORITY_CLASS         0x00000080\r
9068 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9069 NORMAL_PRIORITY_CLASS       0x00000020\r
9070 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9071 IDLE_PRIORITY_CLASS         0x00000040\r
9072 */\r
9073         if (nice < -15) return 0x00000080;\r
9074         if (nice < 0)   return 0x00008000;\r
9075         if (nice == 0)  return 0x00000020;\r
9076         if (nice < 15)  return 0x00004000;\r
9077         return 0x00000040;\r
9078 }\r
9079 \r
9080 void RunCommand(char *cmdLine)\r
9081 {\r
9082   /* Now create the child process. */\r
9083   STARTUPINFO siStartInfo;\r
9084   PROCESS_INFORMATION piProcInfo;\r
9085 \r
9086   siStartInfo.cb = sizeof(STARTUPINFO);\r
9087   siStartInfo.lpReserved = NULL;\r
9088   siStartInfo.lpDesktop = NULL;\r
9089   siStartInfo.lpTitle = NULL;\r
9090   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9091   siStartInfo.cbReserved2 = 0;\r
9092   siStartInfo.lpReserved2 = NULL;\r
9093   siStartInfo.hStdInput = NULL;\r
9094   siStartInfo.hStdOutput = NULL;\r
9095   siStartInfo.hStdError = NULL;\r
9096 \r
9097   CreateProcess(NULL,\r
9098                 cmdLine,           /* command line */\r
9099                 NULL,      /* process security attributes */\r
9100                 NULL,      /* primary thread security attrs */\r
9101                 TRUE,      /* handles are inherited */\r
9102                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9103                 NULL,      /* use parent's environment */\r
9104                 NULL,\r
9105                 &siStartInfo, /* STARTUPINFO pointer */\r
9106                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9107 \r
9108   CloseHandle(piProcInfo.hThread);\r
9109 }\r
9110 \r
9111 /* Start a child process running the given program.\r
9112    The process's standard output can be read from "from", and its\r
9113    standard input can be written to "to".\r
9114    Exit with fatal error if anything goes wrong.\r
9115    Returns an opaque pointer that can be used to destroy the process\r
9116    later.\r
9117 */\r
9118 int\r
9119 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9120 {\r
9121 #define BUFSIZE 4096\r
9122 \r
9123   HANDLE hChildStdinRd, hChildStdinWr,\r
9124     hChildStdoutRd, hChildStdoutWr;\r
9125   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9126   SECURITY_ATTRIBUTES saAttr;\r
9127   BOOL fSuccess;\r
9128   PROCESS_INFORMATION piProcInfo;\r
9129   STARTUPINFO siStartInfo;\r
9130   ChildProc *cp;\r
9131   char buf[MSG_SIZ];\r
9132   DWORD err;\r
9133 \r
9134   if (appData.debugMode) {\r
9135     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9136   }\r
9137 \r
9138   *pr = NoProc;\r
9139 \r
9140   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9141   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9142   saAttr.bInheritHandle = TRUE;\r
9143   saAttr.lpSecurityDescriptor = NULL;\r
9144 \r
9145   /*\r
9146    * The steps for redirecting child's STDOUT:\r
9147    *     1. Create anonymous pipe to be STDOUT for child.\r
9148    *     2. Create a noninheritable duplicate of read handle,\r
9149    *         and close the inheritable read handle.\r
9150    */\r
9151 \r
9152   /* Create a pipe for the child's STDOUT. */\r
9153   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9154     return GetLastError();\r
9155   }\r
9156 \r
9157   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9158   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9159                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9160                              FALSE,     /* not inherited */\r
9161                              DUPLICATE_SAME_ACCESS);\r
9162   if (! fSuccess) {\r
9163     return GetLastError();\r
9164   }\r
9165   CloseHandle(hChildStdoutRd);\r
9166 \r
9167   /*\r
9168    * The steps for redirecting child's STDIN:\r
9169    *     1. Create anonymous pipe to be STDIN for child.\r
9170    *     2. Create a noninheritable duplicate of write handle,\r
9171    *         and close the inheritable write handle.\r
9172    */\r
9173 \r
9174   /* Create a pipe for the child's STDIN. */\r
9175   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9176     return GetLastError();\r
9177   }\r
9178 \r
9179   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9180   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9181                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9182                              FALSE,     /* not inherited */\r
9183                              DUPLICATE_SAME_ACCESS);\r
9184   if (! fSuccess) {\r
9185     return GetLastError();\r
9186   }\r
9187   CloseHandle(hChildStdinWr);\r
9188 \r
9189   /* Arrange to (1) look in dir for the child .exe file, and\r
9190    * (2) have dir be the child's working directory.  Interpret\r
9191    * dir relative to the directory WinBoard loaded from. */\r
9192   GetCurrentDirectory(MSG_SIZ, buf);\r
9193   SetCurrentDirectory(installDir);\r
9194   SetCurrentDirectory(dir);\r
9195 \r
9196   /* Now create the child process. */\r
9197 \r
9198   siStartInfo.cb = sizeof(STARTUPINFO);\r
9199   siStartInfo.lpReserved = NULL;\r
9200   siStartInfo.lpDesktop = NULL;\r
9201   siStartInfo.lpTitle = NULL;\r
9202   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9203   siStartInfo.cbReserved2 = 0;\r
9204   siStartInfo.lpReserved2 = NULL;\r
9205   siStartInfo.hStdInput = hChildStdinRd;\r
9206   siStartInfo.hStdOutput = hChildStdoutWr;\r
9207   siStartInfo.hStdError = hChildStdoutWr;\r
9208 \r
9209   fSuccess = CreateProcess(NULL,\r
9210                            cmdLine,        /* command line */\r
9211                            NULL,           /* process security attributes */\r
9212                            NULL,           /* primary thread security attrs */\r
9213                            TRUE,           /* handles are inherited */\r
9214                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9215                            NULL,           /* use parent's environment */\r
9216                            NULL,\r
9217                            &siStartInfo, /* STARTUPINFO pointer */\r
9218                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9219 \r
9220   err = GetLastError();\r
9221   SetCurrentDirectory(buf); /* return to prev directory */\r
9222   if (! fSuccess) {\r
9223     return err;\r
9224   }\r
9225 \r
9226   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9227     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9228     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9229   }\r
9230 \r
9231   /* Close the handles we don't need in the parent */\r
9232   CloseHandle(piProcInfo.hThread);\r
9233   CloseHandle(hChildStdinRd);\r
9234   CloseHandle(hChildStdoutWr);\r
9235 \r
9236   /* Prepare return value */\r
9237   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9238   cp->kind = CPReal;\r
9239   cp->hProcess = piProcInfo.hProcess;\r
9240   cp->pid = piProcInfo.dwProcessId;\r
9241   cp->hFrom = hChildStdoutRdDup;\r
9242   cp->hTo = hChildStdinWrDup;\r
9243 \r
9244   *pr = (void *) cp;\r
9245 \r
9246   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9247      2000 where engines sometimes don't see the initial command(s)\r
9248      from WinBoard and hang.  I don't understand how that can happen,\r
9249      but the Sleep is harmless, so I've put it in.  Others have also\r
9250      reported what may be the same problem, so hopefully this will fix\r
9251      it for them too.  */\r
9252   Sleep(500);\r
9253 \r
9254   return NO_ERROR;\r
9255 }\r
9256 \r
9257 \r
9258 void\r
9259 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9260 {\r
9261   ChildProc *cp; int result;\r
9262 \r
9263   cp = (ChildProc *) pr;\r
9264   if (cp == NULL) return;\r
9265 \r
9266   switch (cp->kind) {\r
9267   case CPReal:\r
9268     /* TerminateProcess is considered harmful, so... */\r
9269     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9270     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9271     /* The following doesn't work because the chess program\r
9272        doesn't "have the same console" as WinBoard.  Maybe\r
9273        we could arrange for this even though neither WinBoard\r
9274        nor the chess program uses a console for stdio? */\r
9275     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9276 \r
9277     /* [AS] Special termination modes for misbehaving programs... */\r
9278     if( signal & 8 ) { \r
9279         result = TerminateProcess( cp->hProcess, 0 );\r
9280 \r
9281         if ( appData.debugMode) {\r
9282             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9283         }\r
9284     }\r
9285     else if( signal & 4 ) {\r
9286         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9287 \r
9288         if( dw != WAIT_OBJECT_0 ) {\r
9289             result = TerminateProcess( cp->hProcess, 0 );\r
9290 \r
9291             if ( appData.debugMode) {\r
9292                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9293             }\r
9294 \r
9295         }\r
9296     }\r
9297 \r
9298     CloseHandle(cp->hProcess);\r
9299     break;\r
9300 \r
9301   case CPComm:\r
9302     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9303     break;\r
9304 \r
9305   case CPSock:\r
9306     closesocket(cp->sock);\r
9307     WSACleanup();\r
9308     break;\r
9309 \r
9310   case CPRcmd:\r
9311     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9312     closesocket(cp->sock);\r
9313     closesocket(cp->sock2);\r
9314     WSACleanup();\r
9315     break;\r
9316   }\r
9317   free(cp);\r
9318 }\r
9319 \r
9320 void\r
9321 InterruptChildProcess(ProcRef pr)\r
9322 {\r
9323   ChildProc *cp;\r
9324 \r
9325   cp = (ChildProc *) pr;\r
9326   if (cp == NULL) return;\r
9327   switch (cp->kind) {\r
9328   case CPReal:\r
9329     /* The following doesn't work because the chess program\r
9330        doesn't "have the same console" as WinBoard.  Maybe\r
9331        we could arrange for this even though neither WinBoard\r
9332        nor the chess program uses a console for stdio */\r
9333     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9334     break;\r
9335 \r
9336   case CPComm:\r
9337   case CPSock:\r
9338     /* Can't interrupt */\r
9339     break;\r
9340 \r
9341   case CPRcmd:\r
9342     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9343     break;\r
9344   }\r
9345 }\r
9346 \r
9347 \r
9348 int\r
9349 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9350 {\r
9351   char cmdLine[MSG_SIZ];\r
9352 \r
9353   if (port[0] == NULLCHAR) {\r
9354     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9355   } else {\r
9356     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9357   }\r
9358   return StartChildProcess(cmdLine, "", pr);\r
9359 }\r
9360 \r
9361 \r
9362 /* Code to open TCP sockets */\r
9363 \r
9364 int\r
9365 OpenTCP(char *host, char *port, ProcRef *pr)\r
9366 {\r
9367   ChildProc *cp;\r
9368   int err;\r
9369   SOCKET s;\r
9370 \r
9371   struct sockaddr_in sa, mysa;\r
9372   struct hostent FAR *hp;\r
9373   unsigned short uport;\r
9374   WORD wVersionRequested;\r
9375   WSADATA wsaData;\r
9376 \r
9377   /* Initialize socket DLL */\r
9378   wVersionRequested = MAKEWORD(1, 1);\r
9379   err = WSAStartup(wVersionRequested, &wsaData);\r
9380   if (err != 0) return err;\r
9381 \r
9382   /* Make socket */\r
9383   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9384     err = WSAGetLastError();\r
9385     WSACleanup();\r
9386     return err;\r
9387   }\r
9388 \r
9389   /* Bind local address using (mostly) don't-care values.\r
9390    */\r
9391   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9392   mysa.sin_family = AF_INET;\r
9393   mysa.sin_addr.s_addr = INADDR_ANY;\r
9394   uport = (unsigned short) 0;\r
9395   mysa.sin_port = htons(uport);\r
9396   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9397       == SOCKET_ERROR) {\r
9398     err = WSAGetLastError();\r
9399     WSACleanup();\r
9400     return err;\r
9401   }\r
9402 \r
9403   /* Resolve remote host name */\r
9404   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9405   if (!(hp = gethostbyname(host))) {\r
9406     unsigned int b0, b1, b2, b3;\r
9407 \r
9408     err = WSAGetLastError();\r
9409 \r
9410     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9411       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9412       hp->h_addrtype = AF_INET;\r
9413       hp->h_length = 4;\r
9414       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9415       hp->h_addr_list[0] = (char *) malloc(4);\r
9416       hp->h_addr_list[0][0] = (char) b0;\r
9417       hp->h_addr_list[0][1] = (char) b1;\r
9418       hp->h_addr_list[0][2] = (char) b2;\r
9419       hp->h_addr_list[0][3] = (char) b3;\r
9420     } else {\r
9421       WSACleanup();\r
9422       return err;\r
9423     }\r
9424   }\r
9425   sa.sin_family = hp->h_addrtype;\r
9426   uport = (unsigned short) atoi(port);\r
9427   sa.sin_port = htons(uport);\r
9428   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9429 \r
9430   /* Make connection */\r
9431   if (connect(s, (struct sockaddr *) &sa,\r
9432               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9433     err = WSAGetLastError();\r
9434     WSACleanup();\r
9435     return err;\r
9436   }\r
9437 \r
9438   /* Prepare return value */\r
9439   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9440   cp->kind = CPSock;\r
9441   cp->sock = s;\r
9442   *pr = (ProcRef *) cp;\r
9443 \r
9444   return NO_ERROR;\r
9445 }\r
9446 \r
9447 int\r
9448 OpenCommPort(char *name, ProcRef *pr)\r
9449 {\r
9450   HANDLE h;\r
9451   COMMTIMEOUTS ct;\r
9452   ChildProc *cp;\r
9453   char fullname[MSG_SIZ];\r
9454 \r
9455   if (*name != '\\')\r
9456     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9457   else\r
9458     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9459 \r
9460   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9461                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9462   if (h == (HANDLE) -1) {\r
9463     return GetLastError();\r
9464   }\r
9465   hCommPort = h;\r
9466 \r
9467   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9468 \r
9469   /* Accumulate characters until a 100ms pause, then parse */\r
9470   ct.ReadIntervalTimeout = 100;\r
9471   ct.ReadTotalTimeoutMultiplier = 0;\r
9472   ct.ReadTotalTimeoutConstant = 0;\r
9473   ct.WriteTotalTimeoutMultiplier = 0;\r
9474   ct.WriteTotalTimeoutConstant = 0;\r
9475   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9476 \r
9477   /* Prepare return value */\r
9478   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9479   cp->kind = CPComm;\r
9480   cp->hFrom = h;\r
9481   cp->hTo = h;\r
9482   *pr = (ProcRef *) cp;\r
9483 \r
9484   return NO_ERROR;\r
9485 }\r
9486 \r
9487 int\r
9488 OpenLoopback(ProcRef *pr)\r
9489 {\r
9490   DisplayFatalError(_("Not implemented"), 0, 1);\r
9491   return NO_ERROR;\r
9492 }\r
9493 \r
9494 \r
9495 int\r
9496 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9497 {\r
9498   ChildProc *cp;\r
9499   int err;\r
9500   SOCKET s, s2, s3;\r
9501   struct sockaddr_in sa, mysa;\r
9502   struct hostent FAR *hp;\r
9503   unsigned short uport;\r
9504   WORD wVersionRequested;\r
9505   WSADATA wsaData;\r
9506   int fromPort;\r
9507   char stderrPortStr[MSG_SIZ];\r
9508 \r
9509   /* Initialize socket DLL */\r
9510   wVersionRequested = MAKEWORD(1, 1);\r
9511   err = WSAStartup(wVersionRequested, &wsaData);\r
9512   if (err != 0) return err;\r
9513 \r
9514   /* Resolve remote host name */\r
9515   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9516   if (!(hp = gethostbyname(host))) {\r
9517     unsigned int b0, b1, b2, b3;\r
9518 \r
9519     err = WSAGetLastError();\r
9520 \r
9521     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9522       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9523       hp->h_addrtype = AF_INET;\r
9524       hp->h_length = 4;\r
9525       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9526       hp->h_addr_list[0] = (char *) malloc(4);\r
9527       hp->h_addr_list[0][0] = (char) b0;\r
9528       hp->h_addr_list[0][1] = (char) b1;\r
9529       hp->h_addr_list[0][2] = (char) b2;\r
9530       hp->h_addr_list[0][3] = (char) b3;\r
9531     } else {\r
9532       WSACleanup();\r
9533       return err;\r
9534     }\r
9535   }\r
9536   sa.sin_family = hp->h_addrtype;\r
9537   uport = (unsigned short) 514;\r
9538   sa.sin_port = htons(uport);\r
9539   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9540 \r
9541   /* Bind local socket to unused "privileged" port address\r
9542    */\r
9543   s = INVALID_SOCKET;\r
9544   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9545   mysa.sin_family = AF_INET;\r
9546   mysa.sin_addr.s_addr = INADDR_ANY;\r
9547   for (fromPort = 1023;; fromPort--) {\r
9548     if (fromPort < 0) {\r
9549       WSACleanup();\r
9550       return WSAEADDRINUSE;\r
9551     }\r
9552     if (s == INVALID_SOCKET) {\r
9553       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9554         err = WSAGetLastError();\r
9555         WSACleanup();\r
9556         return err;\r
9557       }\r
9558     }\r
9559     uport = (unsigned short) fromPort;\r
9560     mysa.sin_port = htons(uport);\r
9561     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9562         == SOCKET_ERROR) {\r
9563       err = WSAGetLastError();\r
9564       if (err == WSAEADDRINUSE) continue;\r
9565       WSACleanup();\r
9566       return err;\r
9567     }\r
9568     if (connect(s, (struct sockaddr *) &sa,\r
9569       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9570       err = WSAGetLastError();\r
9571       if (err == WSAEADDRINUSE) {\r
9572         closesocket(s);\r
9573         s = -1;\r
9574         continue;\r
9575       }\r
9576       WSACleanup();\r
9577       return err;\r
9578     }\r
9579     break;\r
9580   }\r
9581 \r
9582   /* Bind stderr local socket to unused "privileged" port address\r
9583    */\r
9584   s2 = INVALID_SOCKET;\r
9585   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9586   mysa.sin_family = AF_INET;\r
9587   mysa.sin_addr.s_addr = INADDR_ANY;\r
9588   for (fromPort = 1023;; fromPort--) {\r
9589     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9590     if (fromPort < 0) {\r
9591       (void) closesocket(s);\r
9592       WSACleanup();\r
9593       return WSAEADDRINUSE;\r
9594     }\r
9595     if (s2 == INVALID_SOCKET) {\r
9596       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9597         err = WSAGetLastError();\r
9598         closesocket(s);\r
9599         WSACleanup();\r
9600         return err;\r
9601       }\r
9602     }\r
9603     uport = (unsigned short) fromPort;\r
9604     mysa.sin_port = htons(uport);\r
9605     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9606         == SOCKET_ERROR) {\r
9607       err = WSAGetLastError();\r
9608       if (err == WSAEADDRINUSE) continue;\r
9609       (void) closesocket(s);\r
9610       WSACleanup();\r
9611       return err;\r
9612     }\r
9613     if (listen(s2, 1) == SOCKET_ERROR) {\r
9614       err = WSAGetLastError();\r
9615       if (err == WSAEADDRINUSE) {\r
9616         closesocket(s2);\r
9617         s2 = INVALID_SOCKET;\r
9618         continue;\r
9619       }\r
9620       (void) closesocket(s);\r
9621       (void) closesocket(s2);\r
9622       WSACleanup();\r
9623       return err;\r
9624     }\r
9625     break;\r
9626   }\r
9627   prevStderrPort = fromPort; // remember port used\r
9628   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9629 \r
9630   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9631     err = WSAGetLastError();\r
9632     (void) closesocket(s);\r
9633     (void) closesocket(s2);\r
9634     WSACleanup();\r
9635     return err;\r
9636   }\r
9637 \r
9638   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9639     err = WSAGetLastError();\r
9640     (void) closesocket(s);\r
9641     (void) closesocket(s2);\r
9642     WSACleanup();\r
9643     return err;\r
9644   }\r
9645   if (*user == NULLCHAR) user = UserName();\r
9646   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9647     err = WSAGetLastError();\r
9648     (void) closesocket(s);\r
9649     (void) closesocket(s2);\r
9650     WSACleanup();\r
9651     return err;\r
9652   }\r
9653   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9654     err = WSAGetLastError();\r
9655     (void) closesocket(s);\r
9656     (void) closesocket(s2);\r
9657     WSACleanup();\r
9658     return err;\r
9659   }\r
9660 \r
9661   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9662     err = WSAGetLastError();\r
9663     (void) closesocket(s);\r
9664     (void) closesocket(s2);\r
9665     WSACleanup();\r
9666     return err;\r
9667   }\r
9668   (void) closesocket(s2);  /* Stop listening */\r
9669 \r
9670   /* Prepare return value */\r
9671   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9672   cp->kind = CPRcmd;\r
9673   cp->sock = s;\r
9674   cp->sock2 = s3;\r
9675   *pr = (ProcRef *) cp;\r
9676 \r
9677   return NO_ERROR;\r
9678 }\r
9679 \r
9680 \r
9681 InputSourceRef\r
9682 AddInputSource(ProcRef pr, int lineByLine,\r
9683                InputCallback func, VOIDSTAR closure)\r
9684 {\r
9685   InputSource *is, *is2 = NULL;\r
9686   ChildProc *cp = (ChildProc *) pr;\r
9687 \r
9688   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9689   is->lineByLine = lineByLine;\r
9690   is->func = func;\r
9691   is->closure = closure;\r
9692   is->second = NULL;\r
9693   is->next = is->buf;\r
9694   if (pr == NoProc) {\r
9695     is->kind = CPReal;\r
9696     consoleInputSource = is;\r
9697   } else {\r
9698     is->kind = cp->kind;\r
9699     /* \r
9700         [AS] Try to avoid a race condition if the thread is given control too early:\r
9701         we create all threads suspended so that the is->hThread variable can be\r
9702         safely assigned, then let the threads start with ResumeThread.\r
9703     */\r
9704     switch (cp->kind) {\r
9705     case CPReal:\r
9706       is->hFile = cp->hFrom;\r
9707       cp->hFrom = NULL; /* now owned by InputThread */\r
9708       is->hThread =\r
9709         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9710                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9711       break;\r
9712 \r
9713     case CPComm:\r
9714       is->hFile = cp->hFrom;\r
9715       cp->hFrom = NULL; /* now owned by InputThread */\r
9716       is->hThread =\r
9717         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9718                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9719       break;\r
9720 \r
9721     case CPSock:\r
9722       is->sock = cp->sock;\r
9723       is->hThread =\r
9724         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9725                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9726       break;\r
9727 \r
9728     case CPRcmd:\r
9729       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9730       *is2 = *is;\r
9731       is->sock = cp->sock;\r
9732       is->second = is2;\r
9733       is2->sock = cp->sock2;\r
9734       is2->second = is2;\r
9735       is->hThread =\r
9736         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9737                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9738       is2->hThread =\r
9739         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9740                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9741       break;\r
9742     }\r
9743 \r
9744     if( is->hThread != NULL ) {\r
9745         ResumeThread( is->hThread );\r
9746     }\r
9747 \r
9748     if( is2 != NULL && is2->hThread != NULL ) {\r
9749         ResumeThread( is2->hThread );\r
9750     }\r
9751   }\r
9752 \r
9753   return (InputSourceRef) is;\r
9754 }\r
9755 \r
9756 void\r
9757 RemoveInputSource(InputSourceRef isr)\r
9758 {\r
9759   InputSource *is;\r
9760 \r
9761   is = (InputSource *) isr;\r
9762   is->hThread = NULL;  /* tell thread to stop */\r
9763   CloseHandle(is->hThread);\r
9764   if (is->second != NULL) {\r
9765     is->second->hThread = NULL;\r
9766     CloseHandle(is->second->hThread);\r
9767   }\r
9768 }\r
9769 \r
9770 int no_wrap(char *message, int count)\r
9771 {\r
9772     ConsoleOutput(message, count, FALSE);\r
9773     return count;\r
9774 }\r
9775 \r
9776 int\r
9777 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9778 {\r
9779   DWORD dOutCount;\r
9780   int outCount = SOCKET_ERROR;\r
9781   ChildProc *cp = (ChildProc *) pr;\r
9782   static OVERLAPPED ovl;\r
9783 \r
9784   static int line = 0;\r
9785 \r
9786   if (pr == NoProc)\r
9787   {\r
9788     if (appData.noJoin || !appData.useInternalWrap)\r
9789       return no_wrap(message, count);\r
9790     else\r
9791     {\r
9792       int width = get_term_width();\r
9793       int len = wrap(NULL, message, count, width, &line);\r
9794       char *msg = malloc(len);\r
9795       int dbgchk;\r
9796 \r
9797       if (!msg)\r
9798         return no_wrap(message, count);\r
9799       else\r
9800       {\r
9801         dbgchk = wrap(msg, message, count, width, &line);\r
9802         if (dbgchk != len && appData.debugMode)\r
9803             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9804         ConsoleOutput(msg, len, FALSE);\r
9805         free(msg);\r
9806         return len;\r
9807       }\r
9808     }\r
9809   }\r
9810 \r
9811   if (ovl.hEvent == NULL) {\r
9812     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9813   }\r
9814   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9815 \r
9816   switch (cp->kind) {\r
9817   case CPSock:\r
9818   case CPRcmd:\r
9819     outCount = send(cp->sock, message, count, 0);\r
9820     if (outCount == SOCKET_ERROR) {\r
9821       *outError = WSAGetLastError();\r
9822     } else {\r
9823       *outError = NO_ERROR;\r
9824     }\r
9825     break;\r
9826 \r
9827   case CPReal:\r
9828     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9829                   &dOutCount, NULL)) {\r
9830       *outError = NO_ERROR;\r
9831       outCount = (int) dOutCount;\r
9832     } else {\r
9833       *outError = GetLastError();\r
9834     }\r
9835     break;\r
9836 \r
9837   case CPComm:\r
9838     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9839                             &dOutCount, &ovl);\r
9840     if (*outError == NO_ERROR) {\r
9841       outCount = (int) dOutCount;\r
9842     }\r
9843     break;\r
9844   }\r
9845   return outCount;\r
9846 }\r
9847 \r
9848 void\r
9849 DoSleep(int n)\r
9850 {\r
9851     if(n != 0) Sleep(n);\r
9852 }\r
9853 \r
9854 int\r
9855 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9856                        long msdelay)\r
9857 {\r
9858   /* Ignore delay, not implemented for WinBoard */\r
9859   return OutputToProcess(pr, message, count, outError);\r
9860 }\r
9861 \r
9862 \r
9863 void\r
9864 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9865                         char *buf, int count, int error)\r
9866 {\r
9867   DisplayFatalError(_("Not implemented"), 0, 1);\r
9868 }\r
9869 \r
9870 /* see wgamelist.c for Game List functions */\r
9871 /* see wedittags.c for Edit Tags functions */\r
9872 \r
9873 \r
9874 int\r
9875 ICSInitScript()\r
9876 {\r
9877   FILE *f;\r
9878   char buf[MSG_SIZ];\r
9879   char *dummy;\r
9880 \r
9881   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9882     f = fopen(buf, "r");\r
9883     if (f != NULL) {\r
9884       ProcessICSInitScript(f);\r
9885       fclose(f);\r
9886       return TRUE;\r
9887     }\r
9888   }\r
9889   return FALSE;\r
9890 }\r
9891 \r
9892 \r
9893 VOID\r
9894 StartAnalysisClock()\r
9895 {\r
9896   if (analysisTimerEvent) return;\r
9897   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9898                                         (UINT) 2000, NULL);\r
9899 }\r
9900 \r
9901 VOID\r
9902 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9903 {\r
9904   highlightInfo.sq[0].x = fromX;\r
9905   highlightInfo.sq[0].y = fromY;\r
9906   highlightInfo.sq[1].x = toX;\r
9907   highlightInfo.sq[1].y = toY;\r
9908 }\r
9909 \r
9910 VOID\r
9911 ClearHighlights()\r
9912 {\r
9913   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9914     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9915 }\r
9916 \r
9917 VOID\r
9918 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9919 {\r
9920   premoveHighlightInfo.sq[0].x = fromX;\r
9921   premoveHighlightInfo.sq[0].y = fromY;\r
9922   premoveHighlightInfo.sq[1].x = toX;\r
9923   premoveHighlightInfo.sq[1].y = toY;\r
9924 }\r
9925 \r
9926 VOID\r
9927 ClearPremoveHighlights()\r
9928 {\r
9929   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9930     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9931 }\r
9932 \r
9933 VOID\r
9934 ShutDownFrontEnd()\r
9935 {\r
9936   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9937   DeleteClipboardTempFiles();\r
9938 }\r
9939 \r
9940 void\r
9941 BoardToTop()\r
9942 {\r
9943     if (IsIconic(hwndMain))\r
9944       ShowWindow(hwndMain, SW_RESTORE);\r
9945 \r
9946     SetActiveWindow(hwndMain);\r
9947 }\r
9948 \r
9949 /*\r
9950  * Prototypes for animation support routines\r
9951  */\r
9952 static void ScreenSquare(int column, int row, POINT * pt);\r
9953 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9954      POINT frames[], int * nFrames);\r
9955 \r
9956 \r
9957 #define kFactor 4\r
9958 \r
9959 void\r
9960 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9961 {       // [HGM] atomic: animate blast wave\r
9962         int i;\r
9963 \r
9964         explodeInfo.fromX = fromX;\r
9965         explodeInfo.fromY = fromY;\r
9966         explodeInfo.toX = toX;\r
9967         explodeInfo.toY = toY;\r
9968         for(i=1; i<4*kFactor; i++) {\r
9969             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9970             DrawPosition(FALSE, board);\r
9971             Sleep(appData.animSpeed);\r
9972         }\r
9973         explodeInfo.radius = 0;\r
9974         DrawPosition(TRUE, board);\r
9975 }\r
9976 \r
9977 void\r
9978 AnimateMove(board, fromX, fromY, toX, toY)\r
9979      Board board;\r
9980      int fromX;\r
9981      int fromY;\r
9982      int toX;\r
9983      int toY;\r
9984 {\r
9985   ChessSquare piece;\r
9986   int x = toX, y = toY;\r
9987   POINT start, finish, mid;\r
9988   POINT frames[kFactor * 2 + 1];\r
9989   int nFrames, n;\r
9990 \r
9991   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9992 \r
9993   if (!appData.animate) return;\r
9994   if (doingSizing) return;\r
9995   if (fromY < 0 || fromX < 0) return;\r
9996   piece = board[fromY][fromX];\r
9997   if (piece >= EmptySquare) return;\r
9998 \r
9999   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
10000 \r
10001 again:\r
10002 \r
10003   ScreenSquare(fromX, fromY, &start);\r
10004   ScreenSquare(toX, toY, &finish);\r
10005 \r
10006   /* All moves except knight jumps move in straight line */\r
10007   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10008     mid.x = start.x + (finish.x - start.x) / 2;\r
10009     mid.y = start.y + (finish.y - start.y) / 2;\r
10010   } else {\r
10011     /* Knight: make straight movement then diagonal */\r
10012     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10013        mid.x = start.x + (finish.x - start.x) / 2;\r
10014        mid.y = start.y;\r
10015      } else {\r
10016        mid.x = start.x;\r
10017        mid.y = start.y + (finish.y - start.y) / 2;\r
10018      }\r
10019   }\r
10020   \r
10021   /* Don't use as many frames for very short moves */\r
10022   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10023     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10024   else\r
10025     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10026 \r
10027   animInfo.from.x = fromX;\r
10028   animInfo.from.y = fromY;\r
10029   animInfo.to.x = toX;\r
10030   animInfo.to.y = toY;\r
10031   animInfo.lastpos = start;\r
10032   animInfo.piece = piece;\r
10033   for (n = 0; n < nFrames; n++) {\r
10034     animInfo.pos = frames[n];\r
10035     DrawPosition(FALSE, NULL);\r
10036     animInfo.lastpos = animInfo.pos;\r
10037     Sleep(appData.animSpeed);\r
10038   }\r
10039   animInfo.pos = finish;\r
10040   DrawPosition(FALSE, NULL);\r
10041 \r
10042   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10043 \r
10044   animInfo.piece = EmptySquare;\r
10045   Explode(board, fromX, fromY, toX, toY);\r
10046 }\r
10047 \r
10048 /*      Convert board position to corner of screen rect and color       */\r
10049 \r
10050 static void\r
10051 ScreenSquare(column, row, pt)\r
10052      int column; int row; POINT * pt;\r
10053 {\r
10054   if (flipView) {\r
10055     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10056     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10057   } else {\r
10058     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10059     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10060   }\r
10061 }\r
10062 \r
10063 /*      Generate a series of frame coords from start->mid->finish.\r
10064         The movement rate doubles until the half way point is\r
10065         reached, then halves back down to the final destination,\r
10066         which gives a nice slow in/out effect. The algorithmn\r
10067         may seem to generate too many intermediates for short\r
10068         moves, but remember that the purpose is to attract the\r
10069         viewers attention to the piece about to be moved and\r
10070         then to where it ends up. Too few frames would be less\r
10071         noticeable.                                             */\r
10072 \r
10073 static void\r
10074 Tween(start, mid, finish, factor, frames, nFrames)\r
10075      POINT * start; POINT * mid;\r
10076      POINT * finish; int factor;\r
10077      POINT frames[]; int * nFrames;\r
10078 {\r
10079   int n, fraction = 1, count = 0;\r
10080 \r
10081   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10082   for (n = 0; n < factor; n++)\r
10083     fraction *= 2;\r
10084   for (n = 0; n < factor; n++) {\r
10085     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10086     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10087     count ++;\r
10088     fraction = fraction / 2;\r
10089   }\r
10090   \r
10091   /* Midpoint */\r
10092   frames[count] = *mid;\r
10093   count ++;\r
10094   \r
10095   /* Slow out, stepping 1/2, then 1/4, ... */\r
10096   fraction = 2;\r
10097   for (n = 0; n < factor; n++) {\r
10098     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10099     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10100     count ++;\r
10101     fraction = fraction * 2;\r
10102   }\r
10103   *nFrames = count;\r
10104 }\r
10105 \r
10106 void\r
10107 SettingsPopUp(ChessProgramState *cps)\r
10108 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10109       EngineOptionsPopup(savedHwnd, cps);\r
10110 }\r
10111 \r
10112 int flock(int fid, int code)\r
10113 {\r
10114     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10115     OVERLAPPED ov;\r
10116     ov.hEvent = NULL;\r
10117     ov.Offset = 0;\r
10118     ov.OffsetHigh = 0;\r
10119     switch(code) {\r
10120       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10121 \r
10122       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10123       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10124       default: return -1;\r
10125     }\r
10126     return 0;\r
10127 }\r
10128 \r
10129 char *\r
10130 Col2Text (int n)\r
10131 {\r
10132     static int i=0;\r
10133     static char col[8][20];\r
10134     COLORREF color = *(COLORREF *) colorVariable[n];\r
10135     i = i+1 & 7;\r
10136     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10137     return col[i];\r
10138 }\r
10139 \r
10140 void\r
10141 ActivateTheme (int new)\r
10142 {   // Redo initialization of features depending on options that can occur in themes\r
10143    InitTextures();\r
10144    if(new) InitDrawingColors();\r
10145    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10146    InitDrawingSizes(boardSize, 0);\r
10147    InvalidateRect(hwndMain, NULL, TRUE);\r
10148 }\r