Control Eval Graph with mouse
[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 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 #define SLASH '/'\r
96 #define DATADIR "~~"\r
97 \r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
99 \r
100   int myrandom(void);\r
101   void mysrandom(unsigned int seed);\r
102 \r
103 extern int whiteFlag, blackFlag;\r
104 Boolean flipClock = FALSE;\r
105 extern HANDLE chatHandle[];\r
106 extern enum ICS_TYPE ics_type;\r
107 \r
108 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
109 int  MyGetFullPathName P((char *name, char *fullname));\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
111 VOID NewVariantPopup(HWND hwnd);\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
113                    /*char*/int promoChar));\r
114 void DisplayMove P((int moveNumber));\r
115 void ChatPopUp P((char *s));\r
116 typedef struct {\r
117   ChessSquare piece;  \r
118   POINT pos;      /* window coordinates of current pos */\r
119   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
120   POINT from;     /* board coordinates of the piece's orig pos */\r
121   POINT to;       /* board coordinates of the piece's new pos */\r
122 } AnimInfo;\r
123 \r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
125 \r
126 typedef struct {\r
127   POINT start;    /* window coordinates of start pos */\r
128   POINT pos;      /* window coordinates of current pos */\r
129   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
130   POINT from;     /* board coordinates of the piece's orig pos */\r
131   ChessSquare piece;\r
132 } DragInfo;\r
133 \r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
135 \r
136 typedef struct {\r
137   POINT sq[2];    /* board coordinates of from, to squares */\r
138 } HighlightInfo;\r
139 \r
140 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
144 \r
145 typedef struct { // [HGM] atomic\r
146   int fromX, fromY, toX, toY, radius;\r
147 } ExplodeInfo;\r
148 \r
149 static ExplodeInfo explodeInfo;\r
150 \r
151 /* Window class names */\r
152 char szAppName[] = "WinBoard";\r
153 char szConsoleName[] = "WBConsole";\r
154 \r
155 /* Title bar text */\r
156 char szTitle[] = "WinBoard";\r
157 char szConsoleTitle[] = "I C S Interaction";\r
158 \r
159 char *programName;\r
160 char *settingsFileName;\r
161 Boolean saveSettingsOnExit;\r
162 char installDir[MSG_SIZ];\r
163 int errorExitStatus;\r
164 \r
165 BoardSize boardSize;\r
166 Boolean chessProgram;\r
167 //static int boardX, boardY;\r
168 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
169 int squareSize, lineGap, minorSize, border;\r
170 static int winW, winH;\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
172 static int logoHeight = 0;\r
173 static char messageText[MESSAGE_TEXT_MAX];\r
174 static int clockTimerEvent = 0;\r
175 static int loadGameTimerEvent = 0;\r
176 static int analysisTimerEvent = 0;\r
177 static DelayedEventCallback delayedTimerCallback;\r
178 static int delayedTimerEvent = 0;\r
179 static int buttonCount = 2;\r
180 char *icsTextMenuString;\r
181 char *icsNames;\r
182 char *firstChessProgramNames;\r
183 char *secondChessProgramNames;\r
184 \r
185 #define PALETTESIZE 256\r
186 \r
187 HINSTANCE hInst;          /* current instance */\r
188 Boolean alwaysOnTop = FALSE;\r
189 RECT boardRect;\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
191   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
193 HPALETTE hPal;\r
194 ColorClass currentColorClass;\r
195 \r
196 static HWND savedHwnd;\r
197 HWND hCommPort = NULL;    /* currently open comm port */\r
198 static HWND hwndPause;    /* pause button */\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,\r
201   blackSquareBrush, /* [HGM] for band between board and holdings */\r
202   explodeBrush,     /* [HGM] atomic */\r
203   markerBrush[8],   /* [HGM] markers */\r
204   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
207 static HPEN gridPen = NULL;\r
208 static HPEN highlightPen = NULL;\r
209 static HPEN premovePen = NULL;\r
210 static NPLOGPALETTE pLogPal;\r
211 static BOOL paletteChanged = FALSE;\r
212 static HICON iconWhite, iconBlack, iconCurrent;\r
213 static int doingSizing = FALSE;\r
214 static int lastSizing = 0;\r
215 static int prevStderrPort;\r
216 static HBITMAP userLogo;\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 \r
229 #if defined(_winmajor)\r
230 #define oldDialog (_winmajor < 4)\r
231 #else\r
232 #define oldDialog 0\r
233 #endif\r
234 #endif\r
235 \r
236 #define INTERNATIONAL\r
237 \r
238 #ifdef INTERNATIONAL\r
239 #  define _(s) T_(s)\r
240 #  define N_(s) s\r
241 #else\r
242 #  define _(s) s\r
243 #  define N_(s) s\r
244 #  define T_(s) s\r
245 #  define Translate(x, y)\r
246 #  define LoadLanguageFile(s)\r
247 #endif\r
248 \r
249 #ifdef INTERNATIONAL\r
250 \r
251 Boolean barbaric; // flag indicating if translation is needed\r
252 \r
253 // list of item numbers used in each dialog (used to alter language at run time)\r
254 \r
255 #define ABOUTBOX -1  /* not sure why these are needed */\r
256 #define ABOUTBOX2 -1\r
257 \r
258 int dialogItems[][42] = {\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
261   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
263   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
264   OPT_Ranget, IDOK, IDCANCEL }, \r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
266   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
269   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
272   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
273 { ABOUTBOX2, IDC_ChessBoard }, \r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
275   OPT_GameListClose, IDC_GameListDoFilter }, \r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
277 { DLG_Error, IDOK }, \r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
279   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
282   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
283   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
284 { DLG_IndexNumber, IDC_Index }, \r
285 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
286 { DLG_TypeInName, IDOK, IDCANCEL }, \r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
288   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
290   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
291   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
292   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
293   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
294   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
295   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
297   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
298   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
299   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
300   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
301   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
302   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
303   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
304   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
306   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
307   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
308   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
309   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
310   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
311   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
312   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
314   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
315   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
316   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
317   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
318   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
319   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
320   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
321   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
323   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
324   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
325   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
326   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
329   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
330 { DLG_MoveHistory }, \r
331 { DLG_EvalGraph }, \r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
335   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
336   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
337   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
339   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
340   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
341 { 0 }\r
342 };\r
343 \r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
345 static int lastChecked;\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
347 extern int tinyLayout;\r
348 extern char * menuBarText[][10];\r
349 \r
350 void\r
351 LoadLanguageFile(char *name)\r
352 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
353     FILE *f;\r
354     int i=0, j=0, n=0, k;\r
355     char buf[MSG_SIZ];\r
356 \r
357     if(!name || name[0] == NULLCHAR) return;\r
358       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
359     appData.language = oldLanguage;\r
360     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
361     if((f = fopen(buf, "r")) == NULL) return;\r
362     while((k = fgetc(f)) != EOF) {\r
363         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
364         languageBuf[i] = k;\r
365         if(k == '\n') {\r
366             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
367                 char *p;\r
368                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
369                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
370                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
371                         english[j] = languageBuf + n + 1; *p = 0;\r
372                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
374                     }\r
375                 }\r
376             }\r
377             n = i + 1;\r
378         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
379             switch(k) {\r
380               case 'n': k = '\n'; break;\r
381               case 'r': k = '\r'; break;\r
382               case 't': k = '\t'; break;\r
383             }\r
384             languageBuf[--i] = k;\r
385         }\r
386         i++;\r
387     }\r
388     fclose(f);\r
389     barbaric = (j != 0);\r
390     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
391 }\r
392 \r
393 char *\r
394 T_(char *s)\r
395 {   // return the translation of the given string\r
396     // efficiency can be improved a lot...\r
397     int i=0;\r
398     static char buf[MSG_SIZ];\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
400     if(!barbaric) return s;\r
401     if(!s) return ""; // sanity\r
402     while(english[i]) {\r
403         if(!strcmp(s, english[i])) return foreign[i];\r
404         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
405             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
406             return buf;\r
407         }\r
408         i++;\r
409     }\r
410     return s;\r
411 }\r
412 \r
413 void\r
414 Translate(HWND hDlg, int dialogID)\r
415 {   // translate all text items in the given dialog\r
416     int i=0, j, k;\r
417     char buf[MSG_SIZ], *s;\r
418     if(!barbaric) return;\r
419     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
420     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
421     GetWindowText( hDlg, buf, MSG_SIZ );\r
422     s = T_(buf);\r
423     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
424     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
425         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
426         if(strlen(buf) == 0) continue;\r
427         s = T_(buf);\r
428         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
429     }\r
430 }\r
431 \r
432 HMENU\r
433 TranslateOneMenu(int i, HMENU subMenu)\r
434 {\r
435     int j;\r
436     static MENUITEMINFO info;\r
437 \r
438     info.cbSize = sizeof(MENUITEMINFO);\r
439     info.fMask = MIIM_STATE | MIIM_TYPE;\r
440           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
441             char buf[MSG_SIZ];\r
442             info.dwTypeData = buf;\r
443             info.cch = sizeof(buf);\r
444             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
445             if(i < 10) {\r
446                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
447                 else menuText[i][j] = strdup(buf); // remember original on first change\r
448             }\r
449             if(buf[0] == NULLCHAR) continue;\r
450             info.dwTypeData = T_(buf);\r
451             info.cch = strlen(buf)+1;\r
452             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
453           }\r
454     return subMenu;\r
455 }\r
456 \r
457 void\r
458 TranslateMenus(int addLanguage)\r
459 {\r
460     int i;\r
461     WIN32_FIND_DATA fileData;\r
462     HANDLE hFind;\r
463 #define IDM_English 1970\r
464     if(1) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
467           HMENU subMenu = GetSubMenu(mainMenu, i);\r
468           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
469                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
470           TranslateOneMenu(i, subMenu);\r
471         }\r
472         DrawMenuBar(hwndMain);\r
473     }\r
474 \r
475     if(!addLanguage) return;\r
476     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
477         HMENU mainMenu = GetMenu(hwndMain);\r
478         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
479         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
480         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
481         i = 0; lastChecked = IDM_English;\r
482         do {\r
483             char *p, *q = fileData.cFileName;\r
484             int checkFlag = MF_UNCHECKED;\r
485             languageFile[i] = strdup(q);\r
486             if(barbaric && !strcmp(oldLanguage, q)) {\r
487                 checkFlag = MF_CHECKED;\r
488                 lastChecked = IDM_English + i + 1;\r
489                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
490             }\r
491             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
492             p = strstr(fileData.cFileName, ".lng");\r
493             if(p) *p = 0;\r
494             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
495         } while(FindNextFile(hFind, &fileData));\r
496         FindClose(hFind);\r
497     }\r
498 }\r
499 \r
500 #endif\r
501 \r
502 #define IDM_RecentEngines 3000\r
503 \r
504 void\r
505 RecentEngineMenu (char *s)\r
506 {\r
507     if(appData.icsActive) return;\r
508     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
509         HMENU mainMenu = GetMenu(hwndMain);\r
510         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
511         int i=IDM_RecentEngines;\r
512         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
513         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
514         while(*s) {\r
515           char *p = strchr(s, '\n');\r
516           if(p == NULL) return; // malformed!\r
517           *p = NULLCHAR;\r
518           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
519           *p = '\n';\r
520           s = p+1;\r
521         }\r
522     }\r
523 }\r
524 \r
525 \r
526 typedef struct {\r
527   char *name;\r
528   int squareSize;\r
529   int lineGap;\r
530   int smallLayout;\r
531   int tinyLayout;\r
532   int cliWidth, cliHeight;\r
533 } SizeInfo;\r
534 \r
535 SizeInfo sizeInfo[] = \r
536 {\r
537   { "tiny",     21, 0, 1, 1, 0, 0 },\r
538   { "teeny",    25, 1, 1, 1, 0, 0 },\r
539   { "dinky",    29, 1, 1, 1, 0, 0 },\r
540   { "petite",   33, 1, 1, 1, 0, 0 },\r
541   { "slim",     37, 2, 1, 0, 0, 0 },\r
542   { "small",    40, 2, 1, 0, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
574   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
575   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
576   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
577   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
578   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
607 };\r
608 \r
609 \r
610 MySound sounds[(int)NSoundClasses];\r
611 MyTextAttribs textAttribs[(int)NColorClasses];\r
612 \r
613 MyColorizeAttribs colorizeAttribs[] = {\r
614   { (COLORREF)0, 0, N_("Shout Text") },\r
615   { (COLORREF)0, 0, N_("SShout/CShout") },\r
616   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
617   { (COLORREF)0, 0, N_("Channel Text") },\r
618   { (COLORREF)0, 0, N_("Kibitz Text") },\r
619   { (COLORREF)0, 0, N_("Tell Text") },\r
620   { (COLORREF)0, 0, N_("Challenge Text") },\r
621   { (COLORREF)0, 0, N_("Request Text") },\r
622   { (COLORREF)0, 0, N_("Seek Text") },\r
623   { (COLORREF)0, 0, N_("Normal Text") },\r
624   { (COLORREF)0, 0, N_("None") }\r
625 };\r
626 \r
627 \r
628 \r
629 static char *commentTitle;\r
630 static char *commentText;\r
631 static int commentIndex;\r
632 static Boolean editComment = FALSE;\r
633 \r
634 \r
635 char errorTitle[MSG_SIZ];\r
636 char errorMessage[2*MSG_SIZ];\r
637 HWND errorDialog = NULL;\r
638 BOOLEAN moveErrorMessageUp = FALSE;\r
639 BOOLEAN consoleEcho = TRUE;\r
640 CHARFORMAT consoleCF;\r
641 COLORREF consoleBackgroundColor;\r
642 \r
643 char *programVersion;\r
644 \r
645 #define CPReal 1\r
646 #define CPComm 2\r
647 #define CPSock 3\r
648 #define CPRcmd 4\r
649 typedef int CPKind;\r
650 \r
651 typedef struct {\r
652   CPKind kind;\r
653   HANDLE hProcess;\r
654   DWORD pid;\r
655   HANDLE hTo;\r
656   HANDLE hFrom;\r
657   SOCKET sock;\r
658   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
659 } ChildProc;\r
660 \r
661 #define INPUT_SOURCE_BUF_SIZE 4096\r
662 \r
663 typedef struct _InputSource {\r
664   CPKind kind;\r
665   HANDLE hFile;\r
666   SOCKET sock;\r
667   int lineByLine;\r
668   HANDLE hThread;\r
669   DWORD id;\r
670   char buf[INPUT_SOURCE_BUF_SIZE];\r
671   char *next;\r
672   DWORD count;\r
673   int error;\r
674   InputCallback func;\r
675   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
676   VOIDSTAR closure;\r
677 } InputSource;\r
678 \r
679 InputSource *consoleInputSource;\r
680 \r
681 DCB dcb;\r
682 \r
683 /* forward */\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
685 VOID ConsoleCreate();\r
686 LRESULT CALLBACK\r
687   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);\r
691 LRESULT CALLBACK\r
692   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
694 void ParseIcsTextMenu(char *icsTextMenuString);\r
695 VOID PopUpNameDialog(char firstchar);\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
697 \r
698 /* [AS] */\r
699 int NewGameFRC();\r
700 int GameListOptions();\r
701 \r
702 int dummy; // [HGM] for obsolete args\r
703 \r
704 HWND hwndMain = NULL;        /* root window*/\r
705 HWND hwndConsole = NULL;\r
706 HWND commentDialog = NULL;\r
707 HWND moveHistoryDialog = NULL;\r
708 HWND evalGraphDialog = NULL;\r
709 HWND engineOutputDialog = NULL;\r
710 HWND gameListDialog = NULL;\r
711 HWND editTagsDialog = NULL;\r
712 \r
713 int commentUp = FALSE;\r
714 \r
715 WindowPlacement wpMain;\r
716 WindowPlacement wpConsole;\r
717 WindowPlacement wpComment;\r
718 WindowPlacement wpMoveHistory;\r
719 WindowPlacement wpEvalGraph;\r
720 WindowPlacement wpEngineOutput;\r
721 WindowPlacement wpGameList;\r
722 WindowPlacement wpTags;\r
723 \r
724 VOID EngineOptionsPopup(); // [HGM] settings\r
725 \r
726 VOID GothicPopUp(char *title, VariantClass variant);\r
727 /*\r
728  * Setting "frozen" should disable all user input other than deleting\r
729  * the window.  We do this while engines are initializing themselves.\r
730  */\r
731 static int frozen = 0;\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];\r
733 void FreezeUI()\r
734 {\r
735   HMENU hmenu;\r
736   int i;\r
737 \r
738   if (frozen) return;\r
739   frozen = 1;\r
740   hmenu = GetMenu(hwndMain);\r
741   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
742     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
743   }\r
744   DrawMenuBar(hwndMain);\r
745 }\r
746 \r
747 /* Undo a FreezeUI */\r
748 void ThawUI()\r
749 {\r
750   HMENU hmenu;\r
751   int i;\r
752 \r
753   if (!frozen) return;\r
754   frozen = 0;\r
755   hmenu = GetMenu(hwndMain);\r
756   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
757     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
758   }\r
759   DrawMenuBar(hwndMain);\r
760 }\r
761 \r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
763 \r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
765 #ifdef JAWS\r
766 #include "jaws.c"\r
767 #else\r
768 #define JAWS_INIT\r
769 #define JAWS_ARGS\r
770 #define JAWS_ALT_INTERCEPT\r
771 #define JAWS_KBUP_NAVIGATION\r
772 #define JAWS_KBDOWN_NAVIGATION\r
773 #define JAWS_MENU_ITEMS\r
774 #define JAWS_SILENCE\r
775 #define JAWS_REPLAY\r
776 #define JAWS_ACCEL\r
777 #define JAWS_COPYRIGHT\r
778 #define JAWS_DELETE(X) X\r
779 #define SAYMACHINEMOVE()\r
780 #define SAY(X)\r
781 #endif\r
782 \r
783 /*---------------------------------------------------------------------------*\\r
784  *\r
785  * WinMain\r
786  *\r
787 \*---------------------------------------------------------------------------*/\r
788 \r
789 static void HandleMessage P((MSG *message));\r
790 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
791 \r
792 int APIENTRY\r
793 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
794         LPSTR lpCmdLine, int nCmdShow)\r
795 {\r
796   MSG msg;\r
797 //  INITCOMMONCONTROLSEX ex;\r
798 \r
799   debugFP = stderr;\r
800 \r
801   LoadLibrary("RICHED32.DLL");\r
802   consoleCF.cbSize = sizeof(CHARFORMAT);\r
803 \r
804   if (!InitApplication(hInstance)) {\r
805     return (FALSE);\r
806   }\r
807   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
808     return (FALSE);\r
809   }\r
810 \r
811   JAWS_INIT\r
812   TranslateMenus(1);\r
813 \r
814 //  InitCommonControlsEx(&ex);\r
815   InitCommonControls();\r
816 \r
817   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
818   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
819   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
820 \r
821   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
822 \r
823   while (GetMessage(&msg, /* message structure */\r
824                     NULL, /* handle of window receiving the message */\r
825                     0,    /* lowest message to examine */\r
826                     0))   /* highest message to examine */\r
827     {\r
828         HandleMessage(&msg);\r
829     }\r
830 \r
831 \r
832   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
833 }\r
834 \r
835 static void\r
836 HandleMessage (MSG *message)\r
837 {\r
838     MSG msg = *message;\r
839 \r
840       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
841         // [HGM] navigate: switch between all windows with tab\r
842         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
843         int i, currentElement = 0;\r
844 \r
845         // first determine what element of the chain we come from (if any)\r
846         if(appData.icsActive) {\r
847             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
848             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
849         }\r
850         if(engineOutputDialog && EngineOutputIsUp()) {\r
851             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
852             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
853         }\r
854         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
855             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
856         }\r
857         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
858         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
859         if(msg.hwnd == e1)                 currentElement = 2; else\r
860         if(msg.hwnd == e2)                 currentElement = 3; else\r
861         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
862         if(msg.hwnd == mh)                currentElement = 4; else\r
863         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
864         if(msg.hwnd == hText)  currentElement = 5; else\r
865         if(msg.hwnd == hInput) currentElement = 6; else\r
866         for (i = 0; i < N_BUTTONS; i++) {\r
867             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
868         }\r
869 \r
870         // determine where to go to\r
871         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
872           do {\r
873             currentElement = (currentElement + direction) % 7;\r
874             switch(currentElement) {\r
875                 case 0:\r
876                   h = hwndMain; break; // passing this case always makes the loop exit\r
877                 case 1:\r
878                   h = buttonDesc[0].hwnd; break; // could be NULL\r
879                 case 2:\r
880                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
881                   h = e1; break;\r
882                 case 3:\r
883                   if(!EngineOutputIsUp()) continue;\r
884                   h = e2; break;\r
885                 case 4:\r
886                   if(!MoveHistoryIsUp()) continue;\r
887                   h = mh; break;\r
888 //              case 6: // input to eval graph does not seem to get here!\r
889 //                if(!EvalGraphIsUp()) continue;\r
890 //                h = evalGraphDialog; break;\r
891                 case 5:\r
892                   if(!appData.icsActive) continue;\r
893                   SAY("display");\r
894                   h = hText; break;\r
895                 case 6:\r
896                   if(!appData.icsActive) continue;\r
897                   SAY("input");\r
898                   h = hInput; break;\r
899             }\r
900           } while(h == 0);\r
901 \r
902           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
903           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
904           SetFocus(h);\r
905 \r
906           return; // this message now has been processed\r
907         }\r
908       }\r
909 \r
910       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
911           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
912           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
913           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
914           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
915           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
916           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
917           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
918           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
919           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
920         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
921         for(i=0; i<MAX_CHAT; i++) \r
922             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
923                 done = 1; break;\r
924         }\r
925         if(done) return; // [HGM] chat: end patch\r
926         TranslateMessage(&msg); /* Translates virtual key codes */\r
927         DispatchMessage(&msg);  /* Dispatches message to window */\r
928       }\r
929 }\r
930 \r
931 void\r
932 DoEvents ()\r
933 { /* Dispatch pending messages */\r
934   MSG msg;\r
935   while (PeekMessage(&msg, /* message structure */\r
936                      NULL, /* handle of window receiving the message */\r
937                      0,    /* lowest message to examine */\r
938                      0,    /* highest message to examine */\r
939                      PM_REMOVE))\r
940     {\r
941         HandleMessage(&msg);\r
942     }\r
943 }\r
944 \r
945 /*---------------------------------------------------------------------------*\\r
946  *\r
947  * Initialization functions\r
948  *\r
949 \*---------------------------------------------------------------------------*/\r
950 \r
951 void\r
952 SetUserLogo()\r
953 {   // update user logo if necessary\r
954     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
955 \r
956     if(appData.autoLogo) {\r
957           curName = UserName();\r
958           if(strcmp(curName, oldUserName)) {\r
959                 GetCurrentDirectory(MSG_SIZ, dir);\r
960                 SetCurrentDirectory(installDir);\r
961                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
962                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
963                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
964                 if(userLogo == NULL)\r
965                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
966                 SetCurrentDirectory(dir); /* return to prev directory */\r
967           }\r
968     }\r
969 }\r
970 \r
971 BOOL\r
972 InitApplication(HINSTANCE hInstance)\r
973 {\r
974   WNDCLASS wc;\r
975 \r
976   /* Fill in window class structure with parameters that describe the */\r
977   /* main window. */\r
978 \r
979   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
980   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
981   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
982   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
983   wc.hInstance     = hInstance;         /* Owner of this class */\r
984   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
985   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
986   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
987   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
988   wc.lpszClassName = szAppName;                 /* Name to register as */\r
989 \r
990   /* Register the window class and return success/failure code. */\r
991   if (!RegisterClass(&wc)) return FALSE;\r
992 \r
993   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
994   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
995   wc.cbClsExtra    = 0;\r
996   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
997   wc.hInstance     = hInstance;\r
998   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
999   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1000   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1001   wc.lpszMenuName  = NULL;\r
1002   wc.lpszClassName = szConsoleName;\r
1003 \r
1004   if (!RegisterClass(&wc)) return FALSE;\r
1005   return TRUE;\r
1006 }\r
1007 \r
1008 \r
1009 /* Set by InitInstance, used by EnsureOnScreen */\r
1010 int screenHeight, screenWidth;\r
1011 RECT screenGeometry;\r
1012 \r
1013 void\r
1014 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1015 {\r
1016 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1017   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1018   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1019   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1020   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1021   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1022 }\r
1023 \r
1024 VOID\r
1025 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1026 {\r
1027   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1028   GetCurrentDirectory(MSG_SIZ, dir);\r
1029   SetCurrentDirectory(installDir);\r
1030   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1031       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1032 \r
1033       if (cps->programLogo == NULL && appData.debugMode) {\r
1034           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1035       }\r
1036   } else if(appData.autoLogo) {\r
1037       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1038         char *opponent = "";\r
1039         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1040         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1041         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1042         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1043             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1044             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1045         }\r
1046       } else\r
1047       if(appData.directory[n] && appData.directory[n][0]) {\r
1048         SetCurrentDirectory(appData.directory[n]);\r
1049         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1050       }\r
1051   }\r
1052   SetCurrentDirectory(dir); /* return to prev directory */\r
1053 }\r
1054 \r
1055 VOID\r
1056 InitTextures()\r
1057 {\r
1058   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1059   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1060   \r
1061   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1062       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1063       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1064       liteBackTextureMode = appData.liteBackTextureMode;\r
1065 \r
1066       if (liteBackTexture == NULL && appData.debugMode) {\r
1067           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1068       }\r
1069   }\r
1070   \r
1071   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1072       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1073       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1074       darkBackTextureMode = appData.darkBackTextureMode;\r
1075 \r
1076       if (darkBackTexture == NULL && appData.debugMode) {\r
1077           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1078       }\r
1079   }\r
1080 }\r
1081 \r
1082 #ifndef SM_CXVIRTUALSCREEN\r
1083 #define SM_CXVIRTUALSCREEN 78\r
1084 #endif\r
1085 #ifndef SM_CYVIRTUALSCREEN\r
1086 #define SM_CYVIRTUALSCREEN 79\r
1087 #endif\r
1088 #ifndef SM_XVIRTUALSCREEN \r
1089 #define SM_XVIRTUALSCREEN 76\r
1090 #endif\r
1091 #ifndef SM_YVIRTUALSCREEN \r
1092 #define SM_YVIRTUALSCREEN 77\r
1093 #endif\r
1094 \r
1095 VOID\r
1096 InitGeometry()\r
1097 {\r
1098   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1099   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1100   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1101   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1102   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1103   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1104   screenGeometry.right = screenGeometry.left + screenWidth;\r
1105   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1106 }\r
1107 \r
1108 BOOL\r
1109 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1110 {\r
1111   HWND hwnd; /* Main window handle. */\r
1112   int ibs;\r
1113   WINDOWPLACEMENT wp;\r
1114   char *filepart;\r
1115 \r
1116   hInst = hInstance;    /* Store instance handle in our global variable */\r
1117   programName = szAppName;\r
1118 \r
1119   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1120     *filepart = NULLCHAR;\r
1121     SetCurrentDirectory(installDir);\r
1122   } else {\r
1123     GetCurrentDirectory(MSG_SIZ, installDir);\r
1124   }\r
1125   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1126   InitGeometry();\r
1127   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1128   /* xboard, and older WinBoards, controlled the move sound with the\r
1129      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1130      always turn the option on (so that the backend will call us),\r
1131      then let the user turn the sound off by setting it to silence if\r
1132      desired.  To accommodate old winboard.ini files saved by old\r
1133      versions of WinBoard, we also turn off the sound if the option\r
1134      was initially set to false. [HGM] taken out of InitAppData */\r
1135   if (!appData.ringBellAfterMoves) {\r
1136     sounds[(int)SoundMove].name = strdup("");\r
1137     appData.ringBellAfterMoves = TRUE;\r
1138   }\r
1139   if (appData.debugMode) {\r
1140     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1141     setbuf(debugFP, NULL);\r
1142   }\r
1143 \r
1144   LoadLanguageFile(appData.language);\r
1145 \r
1146   InitBackEnd1();\r
1147 \r
1148 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1149 //  InitEngineUCI( installDir, &second );\r
1150 \r
1151   /* Create a main window for this application instance. */\r
1152   hwnd = CreateWindow(szAppName, szTitle,\r
1153                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1154                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1155                       NULL, NULL, hInstance, NULL);\r
1156   hwndMain = hwnd;\r
1157 \r
1158   /* If window could not be created, return "failure" */\r
1159   if (!hwnd) {\r
1160     return (FALSE);\r
1161   }\r
1162 \r
1163   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1164   LoadLogo(&first, 0, FALSE);\r
1165   LoadLogo(&second, 1, appData.icsActive);\r
1166 \r
1167   SetUserLogo();\r
1168 \r
1169   iconWhite = LoadIcon(hInstance, "icon_white");\r
1170   iconBlack = LoadIcon(hInstance, "icon_black");\r
1171   iconCurrent = iconWhite;\r
1172   InitDrawingColors();\r
1173 \r
1174   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1175   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1176     /* Compute window size for each board size, and use the largest\r
1177        size that fits on this screen as the default. */\r
1178     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1179     if (boardSize == (BoardSize)-1 &&\r
1180         winH <= screenHeight\r
1181            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1182         && winW <= screenWidth) {\r
1183       boardSize = (BoardSize)ibs;\r
1184     }\r
1185   }\r
1186 \r
1187   InitDrawingSizes(boardSize, 0);\r
1188   RecentEngineMenu(appData.recentEngineList);\r
1189   InitMenuChecks();\r
1190   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1191 \r
1192   /* [AS] Load textures if specified */\r
1193   InitTextures();\r
1194 \r
1195   mysrandom( (unsigned) time(NULL) );\r
1196 \r
1197   /* [AS] Restore layout */\r
1198   if( wpMoveHistory.visible ) {\r
1199       MoveHistoryPopUp();\r
1200   }\r
1201 \r
1202   if( wpEvalGraph.visible ) {\r
1203       EvalGraphPopUp();\r
1204   }\r
1205 \r
1206   if( wpEngineOutput.visible ) {\r
1207       EngineOutputPopUp();\r
1208   }\r
1209 \r
1210   /* Make the window visible; update its client area; and return "success" */\r
1211   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1212   wp.length = sizeof(WINDOWPLACEMENT);\r
1213   wp.flags = 0;\r
1214   wp.showCmd = nCmdShow;\r
1215   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1216   wp.rcNormalPosition.left = wpMain.x;\r
1217   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1218   wp.rcNormalPosition.top = wpMain.y;\r
1219   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1220   SetWindowPlacement(hwndMain, &wp);\r
1221 \r
1222   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1223 \r
1224   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1225                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1226 \r
1227   if (hwndConsole) {\r
1228 #if AOT_CONSOLE\r
1229     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1230                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1231 #endif\r
1232     ShowWindow(hwndConsole, nCmdShow);\r
1233     SetActiveWindow(hwndConsole);\r
1234   }\r
1235   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1236   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1237 \r
1238   return TRUE;\r
1239 \r
1240 }\r
1241 \r
1242 VOID\r
1243 InitMenuChecks()\r
1244 {\r
1245   HMENU hmenu = GetMenu(hwndMain);\r
1246 \r
1247   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1248                         MF_BYCOMMAND|((appData.icsActive &&\r
1249                                        *appData.icsCommPort != NULLCHAR) ?\r
1250                                       MF_ENABLED : MF_GRAYED));\r
1251   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1252                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1253                                      MF_CHECKED : MF_UNCHECKED));\r
1254   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1255 }\r
1256 \r
1257 //---------------------------------------------------------------------------------------------------------\r
1258 \r
1259 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1260 #define XBOARD FALSE\r
1261 \r
1262 #define OPTCHAR "/"\r
1263 #define SEPCHAR "="\r
1264 #define TOPLEVEL 0\r
1265 \r
1266 #include "args.h"\r
1267 \r
1268 // front-end part of option handling\r
1269 \r
1270 VOID\r
1271 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1272 {\r
1273   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1274   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1275   DeleteDC(hdc);\r
1276   lf->lfWidth = 0;\r
1277   lf->lfEscapement = 0;\r
1278   lf->lfOrientation = 0;\r
1279   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1280   lf->lfItalic = mfp->italic;\r
1281   lf->lfUnderline = mfp->underline;\r
1282   lf->lfStrikeOut = mfp->strikeout;\r
1283   lf->lfCharSet = mfp->charset;\r
1284   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1285   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1286   lf->lfQuality = DEFAULT_QUALITY;\r
1287   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1288     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1289 }\r
1290 \r
1291 void\r
1292 CreateFontInMF(MyFont *mf)\r
1293\r
1294   LFfromMFP(&mf->lf, &mf->mfp);\r
1295   if (mf->hf) DeleteObject(mf->hf);\r
1296   mf->hf = CreateFontIndirect(&mf->lf);\r
1297 }\r
1298 \r
1299 // [HGM] This platform-dependent table provides the location for storing the color info\r
1300 void *\r
1301 colorVariable[] = {\r
1302   &whitePieceColor, \r
1303   &blackPieceColor, \r
1304   &lightSquareColor,\r
1305   &darkSquareColor, \r
1306   &highlightSquareColor,\r
1307   &premoveHighlightColor,\r
1308   NULL,\r
1309   &consoleBackgroundColor,\r
1310   &appData.fontForeColorWhite,\r
1311   &appData.fontBackColorWhite,\r
1312   &appData.fontForeColorBlack,\r
1313   &appData.fontBackColorBlack,\r
1314   &appData.evalHistColorWhite,\r
1315   &appData.evalHistColorBlack,\r
1316   &appData.highlightArrowColor,\r
1317 };\r
1318 \r
1319 /* Command line font name parser.  NULL name means do nothing.\r
1320    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1321    For backward compatibility, syntax without the colon is also\r
1322    accepted, but font names with digits in them won't work in that case.\r
1323 */\r
1324 VOID\r
1325 ParseFontName(char *name, MyFontParams *mfp)\r
1326 {\r
1327   char *p, *q;\r
1328   if (name == NULL) return;\r
1329   p = name;\r
1330   q = strchr(p, ':');\r
1331   if (q) {\r
1332     if (q - p >= sizeof(mfp->faceName))\r
1333       ExitArgError(_("Font name too long:"), name, TRUE);\r
1334     memcpy(mfp->faceName, p, q - p);\r
1335     mfp->faceName[q - p] = NULLCHAR;\r
1336     p = q + 1;\r
1337   } else {\r
1338     q = mfp->faceName;\r
1339 \r
1340     while (*p && !isdigit(*p)) {\r
1341       *q++ = *p++;\r
1342       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1343         ExitArgError(_("Font name too long:"), name, TRUE);\r
1344     }\r
1345     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1346     *q = NULLCHAR;\r
1347   }\r
1348   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1349   mfp->pointSize = (float) atof(p);\r
1350   mfp->bold = (strchr(p, 'b') != NULL);\r
1351   mfp->italic = (strchr(p, 'i') != NULL);\r
1352   mfp->underline = (strchr(p, 'u') != NULL);\r
1353   mfp->strikeout = (strchr(p, 's') != NULL);\r
1354   mfp->charset = DEFAULT_CHARSET;\r
1355   q = strchr(p, 'c');\r
1356   if (q)\r
1357     mfp->charset = (BYTE) atoi(q+1);\r
1358 }\r
1359 \r
1360 void\r
1361 ParseFont(char *name, int number)\r
1362 { // wrapper to shield back-end from 'font'\r
1363   ParseFontName(name, &font[boardSize][number]->mfp);\r
1364 }\r
1365 \r
1366 void\r
1367 SetFontDefaults()\r
1368 { // in WB  we have a 2D array of fonts; this initializes their description\r
1369   int i, j;\r
1370   /* Point font array elements to structures and\r
1371      parse default font names */\r
1372   for (i=0; i<NUM_FONTS; i++) {\r
1373     for (j=0; j<NUM_SIZES; j++) {\r
1374       font[j][i] = &fontRec[j][i];\r
1375       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1376     }\r
1377   }\r
1378 }\r
1379 \r
1380 void\r
1381 CreateFonts()\r
1382 { // here we create the actual fonts from the selected descriptions\r
1383   int i, j;\r
1384   for (i=0; i<NUM_FONTS; i++) {\r
1385     for (j=0; j<NUM_SIZES; j++) {\r
1386       CreateFontInMF(font[j][i]);\r
1387     }\r
1388   }\r
1389 }\r
1390 /* Color name parser.\r
1391    X version accepts X color names, but this one\r
1392    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1393 COLORREF\r
1394 ParseColorName(char *name)\r
1395 {\r
1396   int red, green, blue, count;\r
1397   char buf[MSG_SIZ];\r
1398 \r
1399   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1400   if (count != 3) {\r
1401     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1402       &red, &green, &blue);\r
1403   }\r
1404   if (count != 3) {\r
1405     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1406     DisplayError(buf, 0);\r
1407     return RGB(0, 0, 0);\r
1408   }\r
1409   return PALETTERGB(red, green, blue);\r
1410 }\r
1411 \r
1412 void\r
1413 ParseColor(int n, char *name)\r
1414 { // for WinBoard the color is an int, which needs to be derived from the string\r
1415   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1416 }\r
1417 \r
1418 void\r
1419 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1420 {\r
1421   char *e = argValue;\r
1422   int eff = 0;\r
1423 \r
1424   while (*e) {\r
1425     if (*e == 'b')      eff |= CFE_BOLD;\r
1426     else if (*e == 'i') eff |= CFE_ITALIC;\r
1427     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1428     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1429     else if (*e == '#' || isdigit(*e)) break;\r
1430     e++;\r
1431   }\r
1432   *effects = eff;\r
1433   *color   = ParseColorName(e);\r
1434 }\r
1435 \r
1436 void\r
1437 ParseTextAttribs(ColorClass cc, char *s)\r
1438 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1439     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1440     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1441 }\r
1442 \r
1443 void\r
1444 ParseBoardSize(void *addr, char *name)\r
1445 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1446   BoardSize bs = SizeTiny;\r
1447   while (sizeInfo[bs].name != NULL) {\r
1448     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1449         *(BoardSize *)addr = bs;\r
1450         return;\r
1451     }\r
1452     bs++;\r
1453   }\r
1454   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1455 }\r
1456 \r
1457 void\r
1458 LoadAllSounds()\r
1459 { // [HGM] import name from appData first\r
1460   ColorClass cc;\r
1461   SoundClass sc;\r
1462   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1463     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1464     textAttribs[cc].sound.data = NULL;\r
1465     MyLoadSound(&textAttribs[cc].sound);\r
1466   }\r
1467   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1468     textAttribs[cc].sound.name = strdup("");\r
1469     textAttribs[cc].sound.data = NULL;\r
1470   }\r
1471   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1472     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1473     sounds[sc].data = NULL;\r
1474     MyLoadSound(&sounds[sc]);\r
1475   }\r
1476 }\r
1477 \r
1478 void\r
1479 SetCommPortDefaults()\r
1480 {\r
1481    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1482   dcb.DCBlength = sizeof(DCB);\r
1483   dcb.BaudRate = 9600;\r
1484   dcb.fBinary = TRUE;\r
1485   dcb.fParity = FALSE;\r
1486   dcb.fOutxCtsFlow = FALSE;\r
1487   dcb.fOutxDsrFlow = FALSE;\r
1488   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1489   dcb.fDsrSensitivity = FALSE;\r
1490   dcb.fTXContinueOnXoff = TRUE;\r
1491   dcb.fOutX = FALSE;\r
1492   dcb.fInX = FALSE;\r
1493   dcb.fNull = FALSE;\r
1494   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1495   dcb.fAbortOnError = FALSE;\r
1496   dcb.ByteSize = 7;\r
1497   dcb.Parity = SPACEPARITY;\r
1498   dcb.StopBits = ONESTOPBIT;\r
1499 }\r
1500 \r
1501 // [HGM] args: these three cases taken out to stay in front-end\r
1502 void\r
1503 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1504 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1505         // while the curent board size determines the element. This system should be ported to XBoard.\r
1506         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1507         int bs;\r
1508         for (bs=0; bs<NUM_SIZES; bs++) {\r
1509           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1510           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1511           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1512             ad->argName, mfp->faceName, mfp->pointSize,\r
1513             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1514             mfp->bold ? "b" : "",\r
1515             mfp->italic ? "i" : "",\r
1516             mfp->underline ? "u" : "",\r
1517             mfp->strikeout ? "s" : "",\r
1518             (int)mfp->charset);\r
1519         }\r
1520       }\r
1521 \r
1522 void\r
1523 ExportSounds()\r
1524 { // [HGM] copy the names from the internal WB variables to appData\r
1525   ColorClass cc;\r
1526   SoundClass sc;\r
1527   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1528     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1529   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1530     (&appData.soundMove)[sc] = sounds[sc].name;\r
1531 }\r
1532 \r
1533 void\r
1534 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1535 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1536         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1537         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1538           (ta->effects & CFE_BOLD) ? "b" : "",\r
1539           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1540           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1541           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1542           (ta->effects) ? " " : "",\r
1543           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1544       }\r
1545 \r
1546 void\r
1547 SaveColor(FILE *f, ArgDescriptor *ad)\r
1548 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1549         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1550         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1551           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1552 }\r
1553 \r
1554 void\r
1555 SaveBoardSize(FILE *f, char *name, void *addr)\r
1556 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1557   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1558 }\r
1559 \r
1560 void\r
1561 ParseCommPortSettings(char *s)\r
1562 { // wrapper to keep dcb from back-end\r
1563   ParseCommSettings(s, &dcb);\r
1564 }\r
1565 \r
1566 void\r
1567 GetWindowCoords()\r
1568 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1569   GetActualPlacement(hwndMain, &wpMain);\r
1570   GetActualPlacement(hwndConsole, &wpConsole);\r
1571   GetActualPlacement(commentDialog, &wpComment);\r
1572   GetActualPlacement(editTagsDialog, &wpTags);\r
1573   GetActualPlacement(gameListDialog, &wpGameList);\r
1574   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1575   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1576   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1577 }\r
1578 \r
1579 void\r
1580 PrintCommPortSettings(FILE *f, char *name)\r
1581 { // wrapper to shield back-end from DCB\r
1582       PrintCommSettings(f, name, &dcb);\r
1583 }\r
1584 \r
1585 int\r
1586 MySearchPath(char *installDir, char *name, char *fullname)\r
1587 {\r
1588   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1589   if(name[0]== '%') {\r
1590     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1591     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1592       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1593       *strchr(buf, '%') = 0;\r
1594       strcat(fullname, getenv(buf));\r
1595       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1596     }\r
1597     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1598     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1599     return (int) strlen(fullname);\r
1600   }\r
1601   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1602 }\r
1603 \r
1604 int\r
1605 MyGetFullPathName(char *name, char *fullname)\r
1606 {\r
1607   char *dummy;\r
1608   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1609 }\r
1610 \r
1611 int\r
1612 MainWindowUp()\r
1613 { // [HGM] args: allows testing if main window is realized from back-end\r
1614   return hwndMain != NULL;\r
1615 }\r
1616 \r
1617 void\r
1618 PopUpStartupDialog()\r
1619 {\r
1620     FARPROC lpProc;\r
1621     \r
1622     LoadLanguageFile(appData.language);\r
1623     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1624     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1625     FreeProcInstance(lpProc);\r
1626 }\r
1627 \r
1628 /*---------------------------------------------------------------------------*\\r
1629  *\r
1630  * GDI board drawing routines\r
1631  *\r
1632 \*---------------------------------------------------------------------------*/\r
1633 \r
1634 /* [AS] Draw square using background texture */\r
1635 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1636 {\r
1637     XFORM   x;\r
1638 \r
1639     if( mode == 0 ) {\r
1640         return; /* Should never happen! */\r
1641     }\r
1642 \r
1643     SetGraphicsMode( dst, GM_ADVANCED );\r
1644 \r
1645     switch( mode ) {\r
1646     case 1:\r
1647         /* Identity */\r
1648         break;\r
1649     case 2:\r
1650         /* X reflection */\r
1651         x.eM11 = -1.0;\r
1652         x.eM12 = 0;\r
1653         x.eM21 = 0;\r
1654         x.eM22 = 1.0;\r
1655         x.eDx = (FLOAT) dw + dx - 1;\r
1656         x.eDy = 0;\r
1657         dx = 0;\r
1658         SetWorldTransform( dst, &x );\r
1659         break;\r
1660     case 3:\r
1661         /* Y reflection */\r
1662         x.eM11 = 1.0;\r
1663         x.eM12 = 0;\r
1664         x.eM21 = 0;\r
1665         x.eM22 = -1.0;\r
1666         x.eDx = 0;\r
1667         x.eDy = (FLOAT) dh + dy - 1;\r
1668         dy = 0;\r
1669         SetWorldTransform( dst, &x );\r
1670         break;\r
1671     case 4:\r
1672         /* X/Y flip */\r
1673         x.eM11 = 0;\r
1674         x.eM12 = 1.0;\r
1675         x.eM21 = 1.0;\r
1676         x.eM22 = 0;\r
1677         x.eDx = (FLOAT) dx;\r
1678         x.eDy = (FLOAT) dy;\r
1679         dx = 0;\r
1680         dy = 0;\r
1681         SetWorldTransform( dst, &x );\r
1682         break;\r
1683     }\r
1684 \r
1685     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1686 \r
1687     x.eM11 = 1.0;\r
1688     x.eM12 = 0;\r
1689     x.eM21 = 0;\r
1690     x.eM22 = 1.0;\r
1691     x.eDx = 0;\r
1692     x.eDy = 0;\r
1693     SetWorldTransform( dst, &x );\r
1694 \r
1695     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1696 }\r
1697 \r
1698 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1699 enum {\r
1700     PM_WP = (int) WhitePawn, \r
1701     PM_WN = (int) WhiteKnight, \r
1702     PM_WB = (int) WhiteBishop, \r
1703     PM_WR = (int) WhiteRook, \r
1704     PM_WQ = (int) WhiteQueen, \r
1705     PM_WF = (int) WhiteFerz, \r
1706     PM_WW = (int) WhiteWazir, \r
1707     PM_WE = (int) WhiteAlfil, \r
1708     PM_WM = (int) WhiteMan, \r
1709     PM_WO = (int) WhiteCannon, \r
1710     PM_WU = (int) WhiteUnicorn, \r
1711     PM_WH = (int) WhiteNightrider, \r
1712     PM_WA = (int) WhiteAngel, \r
1713     PM_WC = (int) WhiteMarshall, \r
1714     PM_WAB = (int) WhiteCardinal, \r
1715     PM_WD = (int) WhiteDragon, \r
1716     PM_WL = (int) WhiteLance, \r
1717     PM_WS = (int) WhiteCobra, \r
1718     PM_WV = (int) WhiteFalcon, \r
1719     PM_WSG = (int) WhiteSilver, \r
1720     PM_WG = (int) WhiteGrasshopper, \r
1721     PM_WK = (int) WhiteKing,\r
1722     PM_BP = (int) BlackPawn, \r
1723     PM_BN = (int) BlackKnight, \r
1724     PM_BB = (int) BlackBishop, \r
1725     PM_BR = (int) BlackRook, \r
1726     PM_BQ = (int) BlackQueen, \r
1727     PM_BF = (int) BlackFerz, \r
1728     PM_BW = (int) BlackWazir, \r
1729     PM_BE = (int) BlackAlfil, \r
1730     PM_BM = (int) BlackMan,\r
1731     PM_BO = (int) BlackCannon, \r
1732     PM_BU = (int) BlackUnicorn, \r
1733     PM_BH = (int) BlackNightrider, \r
1734     PM_BA = (int) BlackAngel, \r
1735     PM_BC = (int) BlackMarshall, \r
1736     PM_BG = (int) BlackGrasshopper, \r
1737     PM_BAB = (int) BlackCardinal,\r
1738     PM_BD = (int) BlackDragon,\r
1739     PM_BL = (int) BlackLance,\r
1740     PM_BS = (int) BlackCobra,\r
1741     PM_BV = (int) BlackFalcon,\r
1742     PM_BSG = (int) BlackSilver,\r
1743     PM_BK = (int) BlackKing\r
1744 };\r
1745 \r
1746 static HFONT hPieceFont = NULL;\r
1747 static HBITMAP hPieceMask[(int) EmptySquare];\r
1748 static HBITMAP hPieceFace[(int) EmptySquare];\r
1749 static int fontBitmapSquareSize = 0;\r
1750 static char pieceToFontChar[(int) EmptySquare] =\r
1751                               { 'p', 'n', 'b', 'r', 'q', \r
1752                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1753                       'k', 'o', 'm', 'v', 't', 'w', \r
1754                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1755                                                               'l' };\r
1756 \r
1757 extern BOOL SetCharTable( char *table, const char * map );\r
1758 /* [HGM] moved to backend.c */\r
1759 \r
1760 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1761 {\r
1762     HBRUSH hbrush;\r
1763     BYTE r1 = GetRValue( color );\r
1764     BYTE g1 = GetGValue( color );\r
1765     BYTE b1 = GetBValue( color );\r
1766     BYTE r2 = r1 / 2;\r
1767     BYTE g2 = g1 / 2;\r
1768     BYTE b2 = b1 / 2;\r
1769     RECT rc;\r
1770 \r
1771     /* Create a uniform background first */\r
1772     hbrush = CreateSolidBrush( color );\r
1773     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1774     FillRect( hdc, &rc, hbrush );\r
1775     DeleteObject( hbrush );\r
1776     \r
1777     if( mode == 1 ) {\r
1778         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1779         int steps = squareSize / 2;\r
1780         int i;\r
1781 \r
1782         for( i=0; i<steps; i++ ) {\r
1783             BYTE r = r1 - (r1-r2) * i / steps;\r
1784             BYTE g = g1 - (g1-g2) * i / steps;\r
1785             BYTE b = b1 - (b1-b2) * i / steps;\r
1786 \r
1787             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1788             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1789             FillRect( hdc, &rc, hbrush );\r
1790             DeleteObject(hbrush);\r
1791         }\r
1792     }\r
1793     else if( mode == 2 ) {\r
1794         /* Diagonal gradient, good more or less for every piece */\r
1795         POINT triangle[3];\r
1796         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1797         HBRUSH hbrush_old;\r
1798         int steps = squareSize;\r
1799         int i;\r
1800 \r
1801         triangle[0].x = squareSize - steps;\r
1802         triangle[0].y = squareSize;\r
1803         triangle[1].x = squareSize;\r
1804         triangle[1].y = squareSize;\r
1805         triangle[2].x = squareSize;\r
1806         triangle[2].y = squareSize - steps;\r
1807 \r
1808         for( i=0; i<steps; i++ ) {\r
1809             BYTE r = r1 - (r1-r2) * i / steps;\r
1810             BYTE g = g1 - (g1-g2) * i / steps;\r
1811             BYTE b = b1 - (b1-b2) * i / steps;\r
1812 \r
1813             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1814             hbrush_old = SelectObject( hdc, hbrush );\r
1815             Polygon( hdc, triangle, 3 );\r
1816             SelectObject( hdc, hbrush_old );\r
1817             DeleteObject(hbrush);\r
1818             triangle[0].x++;\r
1819             triangle[2].y++;\r
1820         }\r
1821 \r
1822         SelectObject( hdc, hpen );\r
1823     }\r
1824 }\r
1825 \r
1826 /*\r
1827     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1828     seems to work ok. The main problem here is to find the "inside" of a chess\r
1829     piece: follow the steps as explained below.\r
1830 */\r
1831 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1832 {\r
1833     HBITMAP hbm;\r
1834     HBITMAP hbm_old;\r
1835     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1836     RECT rc;\r
1837     SIZE sz;\r
1838 \r
1839 \r
1840     POINT pt;\r
1841     int backColor = whitePieceColor; \r
1842     int foreColor = blackPieceColor;\r
1843     \r
1844     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1845         backColor = appData.fontBackColorWhite;\r
1846         foreColor = appData.fontForeColorWhite;\r
1847     }\r
1848     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1849         backColor = appData.fontBackColorBlack;\r
1850         foreColor = appData.fontForeColorBlack;\r
1851     }\r
1852 \r
1853     /* Mask */\r
1854     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1855 \r
1856     hbm_old = SelectObject( hdc, hbm );\r
1857 \r
1858     rc.left = 0;\r
1859     rc.top = 0;\r
1860     rc.right = squareSize;\r
1861     rc.bottom = squareSize;\r
1862 \r
1863     /* Step 1: background is now black */\r
1864     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1865 \r
1866     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1867 \r
1868     pt.x = (squareSize - sz.cx) / 2;\r
1869     pt.y = (squareSize - sz.cy) / 2;\r
1870 \r
1871     SetBkMode( hdc, TRANSPARENT );\r
1872     SetTextColor( hdc, chroma );\r
1873     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1874     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1875 \r
1876     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1877     /* Step 3: the area outside the piece is filled with white */\r
1878 //    FloodFill( hdc, 0, 0, chroma );\r
1879     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1880     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1881     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1882     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1883     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1884     /* \r
1885         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1886         but if the start point is not inside the piece we're lost!\r
1887         There should be a better way to do this... if we could create a region or path\r
1888         from the fill operation we would be fine for example.\r
1889     */\r
1890 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1891     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1892 \r
1893     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1894         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1895         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1896 \r
1897         SelectObject( dc2, bm2 );\r
1898         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1899         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1900         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1901         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1902         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1903 \r
1904         DeleteDC( dc2 );\r
1905         DeleteObject( bm2 );\r
1906     }\r
1907 \r
1908     SetTextColor( hdc, 0 );\r
1909     /* \r
1910         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1911         draw the piece again in black for safety.\r
1912     */\r
1913     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1914 \r
1915     SelectObject( hdc, hbm_old );\r
1916 \r
1917     if( hPieceMask[index] != NULL ) {\r
1918         DeleteObject( hPieceMask[index] );\r
1919     }\r
1920 \r
1921     hPieceMask[index] = hbm;\r
1922 \r
1923     /* Face */\r
1924     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1925 \r
1926     SelectObject( hdc, hbm );\r
1927 \r
1928     {\r
1929         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1930         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1931         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1932 \r
1933         SelectObject( dc1, hPieceMask[index] );\r
1934         SelectObject( dc2, bm2 );\r
1935         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1936         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1937         \r
1938         /* \r
1939             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1940             the piece background and deletes (makes transparent) the rest.\r
1941             Thanks to that mask, we are free to paint the background with the greates\r
1942             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1943             We use this, to make gradients and give the pieces a "roundish" look.\r
1944         */\r
1945         SetPieceBackground( hdc, backColor, 2 );\r
1946         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1947 \r
1948         DeleteDC( dc2 );\r
1949         DeleteDC( dc1 );\r
1950         DeleteObject( bm2 );\r
1951     }\r
1952 \r
1953     SetTextColor( hdc, foreColor );\r
1954     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1955 \r
1956     SelectObject( hdc, hbm_old );\r
1957 \r
1958     if( hPieceFace[index] != NULL ) {\r
1959         DeleteObject( hPieceFace[index] );\r
1960     }\r
1961 \r
1962     hPieceFace[index] = hbm;\r
1963 }\r
1964 \r
1965 static int TranslatePieceToFontPiece( int piece )\r
1966 {\r
1967     switch( piece ) {\r
1968     case BlackPawn:\r
1969         return PM_BP;\r
1970     case BlackKnight:\r
1971         return PM_BN;\r
1972     case BlackBishop:\r
1973         return PM_BB;\r
1974     case BlackRook:\r
1975         return PM_BR;\r
1976     case BlackQueen:\r
1977         return PM_BQ;\r
1978     case BlackKing:\r
1979         return PM_BK;\r
1980     case WhitePawn:\r
1981         return PM_WP;\r
1982     case WhiteKnight:\r
1983         return PM_WN;\r
1984     case WhiteBishop:\r
1985         return PM_WB;\r
1986     case WhiteRook:\r
1987         return PM_WR;\r
1988     case WhiteQueen:\r
1989         return PM_WQ;\r
1990     case WhiteKing:\r
1991         return PM_WK;\r
1992 \r
1993     case BlackAngel:\r
1994         return PM_BA;\r
1995     case BlackMarshall:\r
1996         return PM_BC;\r
1997     case BlackFerz:\r
1998         return PM_BF;\r
1999     case BlackNightrider:\r
2000         return PM_BH;\r
2001     case BlackAlfil:\r
2002         return PM_BE;\r
2003     case BlackWazir:\r
2004         return PM_BW;\r
2005     case BlackUnicorn:\r
2006         return PM_BU;\r
2007     case BlackCannon:\r
2008         return PM_BO;\r
2009     case BlackGrasshopper:\r
2010         return PM_BG;\r
2011     case BlackMan:\r
2012         return PM_BM;\r
2013     case BlackSilver:\r
2014         return PM_BSG;\r
2015     case BlackLance:\r
2016         return PM_BL;\r
2017     case BlackFalcon:\r
2018         return PM_BV;\r
2019     case BlackCobra:\r
2020         return PM_BS;\r
2021     case BlackCardinal:\r
2022         return PM_BAB;\r
2023     case BlackDragon:\r
2024         return PM_BD;\r
2025 \r
2026     case WhiteAngel:\r
2027         return PM_WA;\r
2028     case WhiteMarshall:\r
2029         return PM_WC;\r
2030     case WhiteFerz:\r
2031         return PM_WF;\r
2032     case WhiteNightrider:\r
2033         return PM_WH;\r
2034     case WhiteAlfil:\r
2035         return PM_WE;\r
2036     case WhiteWazir:\r
2037         return PM_WW;\r
2038     case WhiteUnicorn:\r
2039         return PM_WU;\r
2040     case WhiteCannon:\r
2041         return PM_WO;\r
2042     case WhiteGrasshopper:\r
2043         return PM_WG;\r
2044     case WhiteMan:\r
2045         return PM_WM;\r
2046     case WhiteSilver:\r
2047         return PM_WSG;\r
2048     case WhiteLance:\r
2049         return PM_WL;\r
2050     case WhiteFalcon:\r
2051         return PM_WV;\r
2052     case WhiteCobra:\r
2053         return PM_WS;\r
2054     case WhiteCardinal:\r
2055         return PM_WAB;\r
2056     case WhiteDragon:\r
2057         return PM_WD;\r
2058     }\r
2059 \r
2060     return 0;\r
2061 }\r
2062 \r
2063 void CreatePiecesFromFont()\r
2064 {\r
2065     LOGFONT lf;\r
2066     HDC hdc_window = NULL;\r
2067     HDC hdc = NULL;\r
2068     HFONT hfont_old;\r
2069     int fontHeight;\r
2070     int i;\r
2071 \r
2072     if( fontBitmapSquareSize < 0 ) {\r
2073         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2074         return;\r
2075     }\r
2076 \r
2077     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2078             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2079         fontBitmapSquareSize = -1;\r
2080         return;\r
2081     }\r
2082 \r
2083     if( fontBitmapSquareSize != squareSize ) {\r
2084         hdc_window = GetDC( hwndMain );\r
2085         hdc = CreateCompatibleDC( hdc_window );\r
2086 \r
2087         if( hPieceFont != NULL ) {\r
2088             DeleteObject( hPieceFont );\r
2089         }\r
2090         else {\r
2091             for( i=0; i<=(int)BlackKing; i++ ) {\r
2092                 hPieceMask[i] = NULL;\r
2093                 hPieceFace[i] = NULL;\r
2094             }\r
2095         }\r
2096 \r
2097         fontHeight = 75;\r
2098 \r
2099         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2100             fontHeight = appData.fontPieceSize;\r
2101         }\r
2102 \r
2103         fontHeight = (fontHeight * squareSize) / 100;\r
2104 \r
2105         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2106         lf.lfWidth = 0;\r
2107         lf.lfEscapement = 0;\r
2108         lf.lfOrientation = 0;\r
2109         lf.lfWeight = FW_NORMAL;\r
2110         lf.lfItalic = 0;\r
2111         lf.lfUnderline = 0;\r
2112         lf.lfStrikeOut = 0;\r
2113         lf.lfCharSet = DEFAULT_CHARSET;\r
2114         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2115         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2116         lf.lfQuality = PROOF_QUALITY;\r
2117         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2118         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2119         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2120 \r
2121         hPieceFont = CreateFontIndirect( &lf );\r
2122 \r
2123         if( hPieceFont == NULL ) {\r
2124             fontBitmapSquareSize = -2;\r
2125         }\r
2126         else {\r
2127             /* Setup font-to-piece character table */\r
2128             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2129                 /* No (or wrong) global settings, try to detect the font */\r
2130                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2131                     /* Alpha */\r
2132                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2133                 }\r
2134                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2135                     /* DiagramTT* family */\r
2136                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2137                 }\r
2138                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2139                     /* Fairy symbols */\r
2140                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2141                 }\r
2142                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2143                     /* Good Companion (Some characters get warped as literal :-( */\r
2144                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2145                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2146                     SetCharTable(pieceToFontChar, s);\r
2147                 }\r
2148                 else {\r
2149                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2150                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2151                 }\r
2152             }\r
2153 \r
2154             /* Create bitmaps */\r
2155             hfont_old = SelectObject( hdc, hPieceFont );\r
2156             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2157                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2158                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2159 \r
2160             SelectObject( hdc, hfont_old );\r
2161 \r
2162             fontBitmapSquareSize = squareSize;\r
2163         }\r
2164     }\r
2165 \r
2166     if( hdc != NULL ) {\r
2167         DeleteDC( hdc );\r
2168     }\r
2169 \r
2170     if( hdc_window != NULL ) {\r
2171         ReleaseDC( hwndMain, hdc_window );\r
2172     }\r
2173 }\r
2174 \r
2175 HBITMAP\r
2176 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2177 {\r
2178   char name[128], buf[MSG_SIZ];\r
2179 \r
2180     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2181   if(appData.pieceDirectory[0]) {\r
2182     HBITMAP res;\r
2183     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2184     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2185     if(res) return res;\r
2186   }\r
2187   if (gameInfo.event &&\r
2188       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2189       strcmp(name, "k80s") == 0) {\r
2190     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2191   }\r
2192   return LoadBitmap(hinst, name);\r
2193 }\r
2194 \r
2195 \r
2196 /* Insert a color into the program's logical palette\r
2197    structure.  This code assumes the given color is\r
2198    the result of the RGB or PALETTERGB macro, and it\r
2199    knows how those macros work (which is documented).\r
2200 */\r
2201 VOID\r
2202 InsertInPalette(COLORREF color)\r
2203 {\r
2204   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2205 \r
2206   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2207     DisplayFatalError(_("Too many colors"), 0, 1);\r
2208     pLogPal->palNumEntries--;\r
2209     return;\r
2210   }\r
2211 \r
2212   pe->peFlags = (char) 0;\r
2213   pe->peRed = (char) (0xFF & color);\r
2214   pe->peGreen = (char) (0xFF & (color >> 8));\r
2215   pe->peBlue = (char) (0xFF & (color >> 16));\r
2216   return;\r
2217 }\r
2218 \r
2219 \r
2220 VOID\r
2221 InitDrawingColors()\r
2222 {\r
2223   int i;\r
2224   if (pLogPal == NULL) {\r
2225     /* Allocate enough memory for a logical palette with\r
2226      * PALETTESIZE entries and set the size and version fields\r
2227      * of the logical palette structure.\r
2228      */\r
2229     pLogPal = (NPLOGPALETTE)\r
2230       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2231                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2232     pLogPal->palVersion    = 0x300;\r
2233   }\r
2234   pLogPal->palNumEntries = 0;\r
2235 \r
2236   InsertInPalette(lightSquareColor);\r
2237   InsertInPalette(darkSquareColor);\r
2238   InsertInPalette(whitePieceColor);\r
2239   InsertInPalette(blackPieceColor);\r
2240   InsertInPalette(highlightSquareColor);\r
2241   InsertInPalette(premoveHighlightColor);\r
2242 \r
2243   /*  create a logical color palette according the information\r
2244    *  in the LOGPALETTE structure.\r
2245    */\r
2246   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2247 \r
2248   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2249   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2250   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2251   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2252   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2253   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2254   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2255     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2256 \r
2257    /* [AS] Force rendering of the font-based pieces */\r
2258   if( fontBitmapSquareSize > 0 ) {\r
2259     fontBitmapSquareSize = 0;\r
2260   }\r
2261 }\r
2262 \r
2263 \r
2264 int\r
2265 BoardWidth(int boardSize, int n)\r
2266 { /* [HGM] argument n added to allow different width and height */\r
2267   int lineGap = sizeInfo[boardSize].lineGap;\r
2268 \r
2269   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2270       lineGap = appData.overrideLineGap;\r
2271   }\r
2272 \r
2273   return (n + 1) * lineGap +\r
2274           n * sizeInfo[boardSize].squareSize;\r
2275 }\r
2276 \r
2277 /* Respond to board resize by dragging edge */\r
2278 VOID\r
2279 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2280 {\r
2281   BoardSize newSize = NUM_SIZES - 1;\r
2282   static int recurse = 0;\r
2283   if (IsIconic(hwndMain)) return;\r
2284   if (recurse > 0) return;\r
2285   recurse++;\r
2286   while (newSize > 0) {\r
2287         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2288         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2289            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2290     newSize--;\r
2291   } \r
2292   boardSize = newSize;\r
2293   InitDrawingSizes(boardSize, flags);\r
2294   recurse--;\r
2295 }\r
2296 \r
2297 \r
2298 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2299 \r
2300 VOID\r
2301 InitDrawingSizes(BoardSize boardSize, int flags)\r
2302 {\r
2303   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2304   ChessSquare piece;\r
2305   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2306   HDC hdc;\r
2307   SIZE clockSize, messageSize;\r
2308   HFONT oldFont;\r
2309   char buf[MSG_SIZ];\r
2310   char *str;\r
2311   HMENU hmenu = GetMenu(hwndMain);\r
2312   RECT crect, wrect, oldRect;\r
2313   int offby;\r
2314   LOGBRUSH logbrush;\r
2315   VariantClass v = gameInfo.variant;\r
2316 \r
2317   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2318   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2319 \r
2320   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2321   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2322   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2323   oldBoardSize = boardSize;\r
2324 \r
2325   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2326   { // correct board size to one where built-in pieces exist\r
2327     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2328        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2329 \r
2330       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2331       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2332       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2333       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2334       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2335                                    boardSize = SizeMiddling;\r
2336     }\r
2337   }\r
2338   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2339 \r
2340   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2341   oldRect.top = wpMain.y;\r
2342   oldRect.right = wpMain.x + wpMain.width;\r
2343   oldRect.bottom = wpMain.y + wpMain.height;\r
2344 \r
2345   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2346   smallLayout = sizeInfo[boardSize].smallLayout;\r
2347   squareSize = sizeInfo[boardSize].squareSize;\r
2348   lineGap = sizeInfo[boardSize].lineGap;\r
2349   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2350   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2351 \r
2352   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2353       lineGap = appData.overrideLineGap;\r
2354   }\r
2355 \r
2356   if (tinyLayout != oldTinyLayout) {\r
2357     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2358     if (tinyLayout) {\r
2359       style &= ~WS_SYSMENU;\r
2360       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2361                  "&Minimize\tCtrl+F4");\r
2362     } else {\r
2363       style |= WS_SYSMENU;\r
2364       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2365     }\r
2366     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2367 \r
2368     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2369       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2370         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2371     }\r
2372     DrawMenuBar(hwndMain);\r
2373   }\r
2374 \r
2375   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2376   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2377 \r
2378   /* Get text area sizes */\r
2379   hdc = GetDC(hwndMain);\r
2380   if (appData.clockMode) {\r
2381     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2382   } else {\r
2383     snprintf(buf, MSG_SIZ, _("White"));\r
2384   }\r
2385   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2386   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2387   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2388   str = _("We only care about the height here");\r
2389   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2390   SelectObject(hdc, oldFont);\r
2391   ReleaseDC(hwndMain, hdc);\r
2392 \r
2393   /* Compute where everything goes */\r
2394   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2395         /* [HGM] logo: if either logo is on, reserve space for it */\r
2396         logoHeight =  2*clockSize.cy;\r
2397         leftLogoRect.left   = OUTER_MARGIN;\r
2398         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2399         leftLogoRect.top    = OUTER_MARGIN;\r
2400         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2401 \r
2402         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2403         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2404         rightLogoRect.top    = OUTER_MARGIN;\r
2405         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2406 \r
2407 \r
2408     whiteRect.left = leftLogoRect.right;\r
2409     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2410     whiteRect.top = OUTER_MARGIN;\r
2411     whiteRect.bottom = whiteRect.top + logoHeight;\r
2412 \r
2413     blackRect.right = rightLogoRect.left;\r
2414     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2415     blackRect.top = whiteRect.top;\r
2416     blackRect.bottom = whiteRect.bottom;\r
2417   } else {\r
2418     whiteRect.left = OUTER_MARGIN;\r
2419     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2420     whiteRect.top = OUTER_MARGIN;\r
2421     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2422 \r
2423     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2424     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2425     blackRect.top = whiteRect.top;\r
2426     blackRect.bottom = whiteRect.bottom;\r
2427 \r
2428     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2429   }\r
2430 \r
2431   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2432   if (appData.showButtonBar) {\r
2433     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2434       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2435   } else {\r
2436     messageRect.right = OUTER_MARGIN + boardWidth;\r
2437   }\r
2438   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2439   messageRect.bottom = messageRect.top + messageSize.cy;\r
2440 \r
2441   boardRect.left = OUTER_MARGIN;\r
2442   boardRect.right = boardRect.left + boardWidth;\r
2443   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2444   boardRect.bottom = boardRect.top + boardHeight;\r
2445 \r
2446   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2447   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2448   oldTinyLayout = tinyLayout;\r
2449   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2450   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2451     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2452   winW *= 1 + twoBoards;\r
2453   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2454   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2455   wpMain.height = winH; //       without disturbing window attachments\r
2456   GetWindowRect(hwndMain, &wrect);\r
2457   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2458                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2459 \r
2460   // [HGM] placement: let attached windows follow size change.\r
2461   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2462   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2463   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2464   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2465   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2466 \r
2467   /* compensate if menu bar wrapped */\r
2468   GetClientRect(hwndMain, &crect);\r
2469   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2470   wpMain.height += offby;\r
2471   switch (flags) {\r
2472   case WMSZ_TOPLEFT:\r
2473     SetWindowPos(hwndMain, NULL, \r
2474                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2475                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2476     break;\r
2477 \r
2478   case WMSZ_TOPRIGHT:\r
2479   case WMSZ_TOP:\r
2480     SetWindowPos(hwndMain, NULL, \r
2481                  wrect.left, wrect.bottom - wpMain.height, \r
2482                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2483     break;\r
2484 \r
2485   case WMSZ_BOTTOMLEFT:\r
2486   case WMSZ_LEFT:\r
2487     SetWindowPos(hwndMain, NULL, \r
2488                  wrect.right - wpMain.width, wrect.top, \r
2489                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2490     break;\r
2491 \r
2492   case WMSZ_BOTTOMRIGHT:\r
2493   case WMSZ_BOTTOM:\r
2494   case WMSZ_RIGHT:\r
2495   default:\r
2496     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2497                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2498     break;\r
2499   }\r
2500 \r
2501   hwndPause = NULL;\r
2502   for (i = 0; i < N_BUTTONS; i++) {\r
2503     if (buttonDesc[i].hwnd != NULL) {\r
2504       DestroyWindow(buttonDesc[i].hwnd);\r
2505       buttonDesc[i].hwnd = NULL;\r
2506     }\r
2507     if (appData.showButtonBar) {\r
2508       buttonDesc[i].hwnd =\r
2509         CreateWindow("BUTTON", buttonDesc[i].label,\r
2510                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2511                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2512                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2513                      (HMENU) buttonDesc[i].id,\r
2514                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2515       if (tinyLayout) {\r
2516         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2517                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2518                     MAKELPARAM(FALSE, 0));\r
2519       }\r
2520       if (buttonDesc[i].id == IDM_Pause)\r
2521         hwndPause = buttonDesc[i].hwnd;\r
2522       buttonDesc[i].wndproc = (WNDPROC)\r
2523         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2524     }\r
2525   }\r
2526   if (gridPen != NULL) DeleteObject(gridPen);\r
2527   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2528   if (premovePen != NULL) DeleteObject(premovePen);\r
2529   if (lineGap != 0) {\r
2530     logbrush.lbStyle = BS_SOLID;\r
2531     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2532     gridPen =\r
2533       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2534                    lineGap, &logbrush, 0, NULL);\r
2535     logbrush.lbColor = highlightSquareColor;\r
2536     highlightPen =\r
2537       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2538                    lineGap, &logbrush, 0, NULL);\r
2539 \r
2540     logbrush.lbColor = premoveHighlightColor; \r
2541     premovePen =\r
2542       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2543                    lineGap, &logbrush, 0, NULL);\r
2544 \r
2545     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2546     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2547       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2548       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2549         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2550       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2551         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2552       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2553     }\r
2554     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2555       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2556       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2557         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2558         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2559       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2560         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2561       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2562     }\r
2563   }\r
2564 \r
2565   /* [HGM] Licensing requirement */\r
2566 #ifdef GOTHIC\r
2567   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2568 #endif\r
2569 #ifdef FALCON\r
2570   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2571 #endif\r
2572   GothicPopUp( "", VariantNormal);\r
2573 \r
2574 \r
2575 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2576 \r
2577   /* Load piece bitmaps for this board size */\r
2578   for (i=0; i<=2; i++) {\r
2579     for (piece = WhitePawn;\r
2580          (int) piece < (int) BlackPawn;\r
2581          piece = (ChessSquare) ((int) piece + 1)) {\r
2582       if (pieceBitmap[i][piece] != NULL)\r
2583         DeleteObject(pieceBitmap[i][piece]);\r
2584     }\r
2585   }\r
2586 \r
2587   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2588   // Orthodox Chess pieces\r
2589   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2590   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2591   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2592   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2593   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2594   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2595   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2596   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2597   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2598   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2599   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2600   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2601   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2602   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2603   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2604   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2605     // in Shogi, Hijack the unused Queen for Lance\r
2606     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2607     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2608     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2609   } else {\r
2610     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2611     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2612     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2613   }\r
2614 \r
2615   if(squareSize <= 72 && squareSize >= 33) { \r
2616     /* A & C are available in most sizes now */\r
2617     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2618       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2619       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2620       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2621       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2622       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2623       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2624       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2625       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2626       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2627       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2628       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2629       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2630     } else { // Smirf-like\r
2631       if(gameInfo.variant == VariantSChess) {\r
2632         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2633         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2634         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2635       } else {\r
2636         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2637         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2638         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2639       }\r
2640     }\r
2641     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2642       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2643       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2644       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2645     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2646       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2647       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2648       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2649     } else { // WinBoard standard\r
2650       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2651       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2652       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2653     }\r
2654   }\r
2655 \r
2656 \r
2657   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2658     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2659     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2660     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2661     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2662     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2663     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2664     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2665     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2666     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2667     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2668     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2669     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2670     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2671     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2672     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2673     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2674     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2675     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2676     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2677     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2678     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2679     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2680     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2681     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2682     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2683     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2684     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2685     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2686     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2687     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2688     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2689     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2690     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2691 \r
2692     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2693       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2694       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2695       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2696       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2697       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2698       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2699       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2700       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2701       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2702       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2703       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2704       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2705     } else {\r
2706       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2707       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2708       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2709       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2710       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2711       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2712       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2713       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2714       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2715       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2716       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2717       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2718     }\r
2719 \r
2720   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2721     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2722     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2723     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2724     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2725     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2726     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2727     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2728     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2729     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2730     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2731     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2732     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2733     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2734     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2735   }\r
2736 \r
2737 \r
2738   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2739   /* special Shogi support in this size */\r
2740   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2741       for (piece = WhitePawn;\r
2742            (int) piece < (int) BlackPawn;\r
2743            piece = (ChessSquare) ((int) piece + 1)) {\r
2744         if (pieceBitmap[i][piece] != NULL)\r
2745           DeleteObject(pieceBitmap[i][piece]);\r
2746       }\r
2747     }\r
2748   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2749   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2750   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2751   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2752   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2753   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2754   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2755   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2756   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2757   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2758   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2759   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2760   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2761   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2762   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2763   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2764   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2765   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2766   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2767   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2768   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2769   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2770   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2771   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2772   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2773   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2774   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2775   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2776   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2777   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2778   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2779   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2780   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2781   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2782   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2783   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2784   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2785   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2786   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2787   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2788   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2789   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2790   minorSize = 0;\r
2791   }\r
2792 }\r
2793 \r
2794 HBITMAP\r
2795 PieceBitmap(ChessSquare p, int kind)\r
2796 {\r
2797   if ((int) p >= (int) BlackPawn)\r
2798     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2799 \r
2800   return pieceBitmap[kind][(int) p];\r
2801 }\r
2802 \r
2803 /***************************************************************/\r
2804 \r
2805 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2806 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2807 /*\r
2808 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2809 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2810 */\r
2811 \r
2812 VOID\r
2813 SquareToPos(int row, int column, int * x, int * y)\r
2814 {\r
2815   if (flipView) {\r
2816     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2817     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2818   } else {\r
2819     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2820     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2821   }\r
2822 }\r
2823 \r
2824 VOID\r
2825 DrawCoordsOnDC(HDC hdc)\r
2826 {\r
2827   static char files[] = "0123456789012345678901221098765432109876543210";\r
2828   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2829   char str[2] = { NULLCHAR, NULLCHAR };\r
2830   int oldMode, oldAlign, x, y, start, i;\r
2831   HFONT oldFont;\r
2832   HBRUSH oldBrush;\r
2833 \r
2834   if (!appData.showCoords)\r
2835     return;\r
2836 \r
2837   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2838 \r
2839   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2840   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2841   oldAlign = GetTextAlign(hdc);\r
2842   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2843 \r
2844   y = boardRect.top + lineGap;\r
2845   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2846 \r
2847   if(border) {\r
2848     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2849     x += border - lineGap - 4; y += squareSize - 6;\r
2850   } else\r
2851   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2852   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2853     str[0] = files[start + i];\r
2854     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2855     y += squareSize + lineGap;\r
2856   }\r
2857 \r
2858   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2859 \r
2860   if(border) {\r
2861     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2862     x += -border + 4; y += border - squareSize + 6;\r
2863   } else\r
2864   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2865   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2866     str[0] = ranks[start + i];\r
2867     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2868     x += squareSize + lineGap;\r
2869   }    \r
2870 \r
2871   SelectObject(hdc, oldBrush);\r
2872   SetBkMode(hdc, oldMode);\r
2873   SetTextAlign(hdc, oldAlign);\r
2874   SelectObject(hdc, oldFont);\r
2875 }\r
2876 \r
2877 VOID\r
2878 DrawGridOnDC(HDC hdc)\r
2879 {\r
2880   HPEN oldPen;\r
2881  \r
2882   if (lineGap != 0) {\r
2883     oldPen = SelectObject(hdc, gridPen);\r
2884     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2885     SelectObject(hdc, oldPen);\r
2886   }\r
2887 }\r
2888 \r
2889 #define HIGHLIGHT_PEN 0\r
2890 #define PREMOVE_PEN   1\r
2891 \r
2892 VOID\r
2893 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2894 {\r
2895   int x1, y1;\r
2896   HPEN oldPen, hPen;\r
2897   if (lineGap == 0) return;\r
2898   if (flipView) {\r
2899     x1 = boardRect.left +\r
2900       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2901     y1 = boardRect.top +\r
2902       lineGap/2 + y * (squareSize + lineGap) + border;\r
2903   } else {\r
2904     x1 = boardRect.left +\r
2905       lineGap/2 + x * (squareSize + lineGap) + border;\r
2906     y1 = boardRect.top +\r
2907       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2908   }\r
2909   hPen = pen ? premovePen : highlightPen;\r
2910   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2911   MoveToEx(hdc, x1, y1, NULL);\r
2912   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2913   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2914   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2915   LineTo(hdc, x1, y1);\r
2916   SelectObject(hdc, oldPen);\r
2917 }\r
2918 \r
2919 VOID\r
2920 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2921 {\r
2922   int i;\r
2923   for (i=0; i<2; i++) {\r
2924     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2925       DrawHighlightOnDC(hdc, TRUE,\r
2926                         h->sq[i].x, h->sq[i].y,\r
2927                         pen);\r
2928   }\r
2929 }\r
2930 \r
2931 /* Note: sqcolor is used only in monoMode */\r
2932 /* Note that this code is largely duplicated in woptions.c,\r
2933    function DrawSampleSquare, so that needs to be updated too */\r
2934 VOID\r
2935 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2936 {\r
2937   HBITMAP oldBitmap;\r
2938   HBRUSH oldBrush;\r
2939   int tmpSize;\r
2940 \r
2941   if (appData.blindfold) return;\r
2942 \r
2943   /* [AS] Use font-based pieces if needed */\r
2944   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2945     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2946     CreatePiecesFromFont();\r
2947 \r
2948     if( fontBitmapSquareSize == squareSize ) {\r
2949         int index = TranslatePieceToFontPiece(piece);\r
2950 \r
2951         SelectObject( tmphdc, hPieceMask[ index ] );\r
2952 \r
2953       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2954         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2955       else\r
2956         BitBlt( hdc,\r
2957             x, y,\r
2958             squareSize, squareSize,\r
2959             tmphdc,\r
2960             0, 0,\r
2961             SRCAND );\r
2962 \r
2963         SelectObject( tmphdc, hPieceFace[ index ] );\r
2964 \r
2965       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2966         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2967       else\r
2968         BitBlt( hdc,\r
2969             x, y,\r
2970             squareSize, squareSize,\r
2971             tmphdc,\r
2972             0, 0,\r
2973             SRCPAINT );\r
2974 \r
2975         return;\r
2976     }\r
2977   }\r
2978 \r
2979   if (appData.monoMode) {\r
2980     SelectObject(tmphdc, PieceBitmap(piece, \r
2981       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2982     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2983            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2984   } else {\r
2985     HBRUSH xBrush = whitePieceBrush;\r
2986     tmpSize = squareSize;\r
2987     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2988     if(minorSize &&\r
2989         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2990          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2991       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2992       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2993       x += (squareSize - minorSize)>>1;\r
2994       y += squareSize - minorSize - 2;\r
2995       tmpSize = minorSize;\r
2996     }\r
2997     if (color || appData.allWhite ) {\r
2998       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2999       if( color )\r
3000               oldBrush = SelectObject(hdc, xBrush);\r
3001       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3002       if(appData.upsideDown && color==flipView)\r
3003         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3004       else\r
3005         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3006       /* Use black for outline of white pieces */\r
3007       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3008       if(appData.upsideDown && color==flipView)\r
3009         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3010       else\r
3011         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3012     } else if(appData.pieceDirectory[0]) {\r
3013       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3014       oldBrush = SelectObject(hdc, xBrush);\r
3015       if(appData.upsideDown && color==flipView)\r
3016         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3017       else\r
3018         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3019       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3020       if(appData.upsideDown && color==flipView)\r
3021         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3022       else\r
3023         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3024     } else {\r
3025       /* Use square color for details of black pieces */\r
3026       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3027       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3028       if(appData.upsideDown && !flipView)\r
3029         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3030       else\r
3031         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3032     }\r
3033     SelectObject(hdc, oldBrush);\r
3034     SelectObject(tmphdc, oldBitmap);\r
3035   }\r
3036 }\r
3037 \r
3038 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3039 int GetBackTextureMode( int algo )\r
3040 {\r
3041     int result = BACK_TEXTURE_MODE_DISABLED;\r
3042 \r
3043     switch( algo ) \r
3044     {\r
3045         case BACK_TEXTURE_MODE_PLAIN:\r
3046             result = 1; /* Always use identity map */\r
3047             break;\r
3048         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3049             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3050             break;\r
3051     }\r
3052 \r
3053     return result;\r
3054 }\r
3055 \r
3056 /* \r
3057     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3058     to handle redraws cleanly (as random numbers would always be different).\r
3059 */\r
3060 VOID RebuildTextureSquareInfo()\r
3061 {\r
3062     BITMAP bi;\r
3063     int lite_w = 0;\r
3064     int lite_h = 0;\r
3065     int dark_w = 0;\r
3066     int dark_h = 0;\r
3067     int row;\r
3068     int col;\r
3069 \r
3070     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3071 \r
3072     if( liteBackTexture != NULL ) {\r
3073         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3074             lite_w = bi.bmWidth;\r
3075             lite_h = bi.bmHeight;\r
3076         }\r
3077     }\r
3078 \r
3079     if( darkBackTexture != NULL ) {\r
3080         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3081             dark_w = bi.bmWidth;\r
3082             dark_h = bi.bmHeight;\r
3083         }\r
3084     }\r
3085 \r
3086     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3087         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3088             if( (col + row) & 1 ) {\r
3089                 /* Lite square */\r
3090                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3091                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3092                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3093                   else\r
3094                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3095                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3096                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3097                   else\r
3098                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3099                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3100                 }\r
3101             }\r
3102             else {\r
3103                 /* Dark square */\r
3104                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3105                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3106                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3107                   else\r
3108                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3109                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3110                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3111                   else\r
3112                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3113                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3114                 }\r
3115             }\r
3116         }\r
3117     }\r
3118 }\r
3119 \r
3120 /* [AS] Arrow highlighting support */\r
3121 \r
3122 static double A_WIDTH = 5; /* Width of arrow body */\r
3123 \r
3124 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3125 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3126 \r
3127 static double Sqr( double x )\r
3128 {\r
3129     return x*x;\r
3130 }\r
3131 \r
3132 static int Round( double x )\r
3133 {\r
3134     return (int) (x + 0.5);\r
3135 }\r
3136 \r
3137 /* Draw an arrow between two points using current settings */\r
3138 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3139 {\r
3140     POINT arrow[7];\r
3141     double dx, dy, j, k, x, y;\r
3142 \r
3143     if( d_x == s_x ) {\r
3144         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3145 \r
3146         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3147         arrow[0].y = s_y;\r
3148 \r
3149         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3150         arrow[1].y = d_y - h;\r
3151 \r
3152         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3153         arrow[2].y = d_y - h;\r
3154 \r
3155         arrow[3].x = d_x;\r
3156         arrow[3].y = d_y;\r
3157 \r
3158         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3159         arrow[5].y = d_y - h;\r
3160 \r
3161         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3162         arrow[4].y = d_y - h;\r
3163 \r
3164         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3165         arrow[6].y = s_y;\r
3166     }\r
3167     else if( d_y == s_y ) {\r
3168         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3169 \r
3170         arrow[0].x = s_x;\r
3171         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3172 \r
3173         arrow[1].x = d_x - w;\r
3174         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3175 \r
3176         arrow[2].x = d_x - w;\r
3177         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3178 \r
3179         arrow[3].x = d_x;\r
3180         arrow[3].y = d_y;\r
3181 \r
3182         arrow[5].x = d_x - w;\r
3183         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3184 \r
3185         arrow[4].x = d_x - w;\r
3186         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3187 \r
3188         arrow[6].x = s_x;\r
3189         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3190     }\r
3191     else {\r
3192         /* [AS] Needed a lot of paper for this! :-) */\r
3193         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3194         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3195   \r
3196         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3197 \r
3198         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3199 \r
3200         x = s_x;\r
3201         y = s_y;\r
3202 \r
3203         arrow[0].x = Round(x - j);\r
3204         arrow[0].y = Round(y + j*dx);\r
3205 \r
3206         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3207         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3208 \r
3209         if( d_x > s_x ) {\r
3210             x = (double) d_x - k;\r
3211             y = (double) d_y - k*dy;\r
3212         }\r
3213         else {\r
3214             x = (double) d_x + k;\r
3215             y = (double) d_y + k*dy;\r
3216         }\r
3217 \r
3218         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3219 \r
3220         arrow[6].x = Round(x - j);\r
3221         arrow[6].y = Round(y + j*dx);\r
3222 \r
3223         arrow[2].x = Round(arrow[6].x + 2*j);\r
3224         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3225 \r
3226         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3227         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3228 \r
3229         arrow[4].x = d_x;\r
3230         arrow[4].y = d_y;\r
3231 \r
3232         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3233         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3234     }\r
3235 \r
3236     Polygon( hdc, arrow, 7 );\r
3237 }\r
3238 \r
3239 /* [AS] Draw an arrow between two squares */\r
3240 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3241 {\r
3242     int s_x, s_y, d_x, d_y;\r
3243     HPEN hpen;\r
3244     HPEN holdpen;\r
3245     HBRUSH hbrush;\r
3246     HBRUSH holdbrush;\r
3247     LOGBRUSH stLB;\r
3248 \r
3249     if( s_col == d_col && s_row == d_row ) {\r
3250         return;\r
3251     }\r
3252 \r
3253     /* Get source and destination points */\r
3254     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3255     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3256 \r
3257     if( d_y > s_y ) {\r
3258         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3259     }\r
3260     else if( d_y < s_y ) {\r
3261         d_y += squareSize / 2 + squareSize / 4;\r
3262     }\r
3263     else {\r
3264         d_y += squareSize / 2;\r
3265     }\r
3266 \r
3267     if( d_x > s_x ) {\r
3268         d_x += squareSize / 2 - squareSize / 4;\r
3269     }\r
3270     else if( d_x < s_x ) {\r
3271         d_x += squareSize / 2 + squareSize / 4;\r
3272     }\r
3273     else {\r
3274         d_x += squareSize / 2;\r
3275     }\r
3276 \r
3277     s_x += squareSize / 2;\r
3278     s_y += squareSize / 2;\r
3279 \r
3280     /* Adjust width */\r
3281     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3282 \r
3283     /* Draw */\r
3284     stLB.lbStyle = BS_SOLID;\r
3285     stLB.lbColor = appData.highlightArrowColor;\r
3286     stLB.lbHatch = 0;\r
3287 \r
3288     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3289     holdpen = SelectObject( hdc, hpen );\r
3290     hbrush = CreateBrushIndirect( &stLB );\r
3291     holdbrush = SelectObject( hdc, hbrush );\r
3292 \r
3293     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3294 \r
3295     SelectObject( hdc, holdpen );\r
3296     SelectObject( hdc, holdbrush );\r
3297     DeleteObject( hpen );\r
3298     DeleteObject( hbrush );\r
3299 }\r
3300 \r
3301 BOOL HasHighlightInfo()\r
3302 {\r
3303     BOOL result = FALSE;\r
3304 \r
3305     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3306         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3307     {\r
3308         result = TRUE;\r
3309     }\r
3310 \r
3311     return result;\r
3312 \r
3313 \r
3314 \r
3315 }\r
3316 \r
3317 BOOL IsDrawArrowEnabled()\r
3318 {\r
3319     BOOL result = FALSE;\r
3320 \r
3321     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3322         result = TRUE;\r
3323     }\r
3324 \r
3325     return result;\r
3326 }\r
3327 \r
3328 VOID DrawArrowHighlight( HDC hdc )\r
3329 {\r
3330     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3331         DrawArrowBetweenSquares( hdc,\r
3332             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3333             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3334     }\r
3335 }\r
3336 \r
3337 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3338 {\r
3339     HRGN result = NULL;\r
3340 \r
3341     if( HasHighlightInfo() ) {\r
3342         int x1, y1, x2, y2;\r
3343         int sx, sy, dx, dy;\r
3344 \r
3345         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3346         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3347 \r
3348         sx = MIN( x1, x2 );\r
3349         sy = MIN( y1, y2 );\r
3350         dx = MAX( x1, x2 ) + squareSize;\r
3351         dy = MAX( y1, y2 ) + squareSize;\r
3352 \r
3353         result = CreateRectRgn( sx, sy, dx, dy );\r
3354     }\r
3355 \r
3356     return result;\r
3357 }\r
3358 \r
3359 /*\r
3360     Warning: this function modifies the behavior of several other functions. \r
3361     \r
3362     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3363     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3364     repaint is scattered all over the place, which is not good for features such as\r
3365     "arrow highlighting" that require a full repaint of the board.\r
3366 \r
3367     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3368     user interaction, when speed is not so important) but especially to avoid errors\r
3369     in the displayed graphics.\r
3370 \r
3371     In such patched places, I always try refer to this function so there is a single\r
3372     place to maintain knowledge.\r
3373     \r
3374     To restore the original behavior, just return FALSE unconditionally.\r
3375 */\r
3376 BOOL IsFullRepaintPreferrable()\r
3377 {\r
3378     BOOL result = FALSE;\r
3379 \r
3380     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3381         /* Arrow may appear on the board */\r
3382         result = TRUE;\r
3383     }\r
3384 \r
3385     return result;\r
3386 }\r
3387 \r
3388 /* \r
3389     This function is called by DrawPosition to know whether a full repaint must\r
3390     be forced or not.\r
3391 \r
3392     Only DrawPosition may directly call this function, which makes use of \r
3393     some state information. Other function should call DrawPosition specifying \r
3394     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3395 */\r
3396 BOOL DrawPositionNeedsFullRepaint()\r
3397 {\r
3398     BOOL result = FALSE;\r
3399 \r
3400     /* \r
3401         Probably a slightly better policy would be to trigger a full repaint\r
3402         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3403         but animation is fast enough that it's difficult to notice.\r
3404     */\r
3405     if( animInfo.piece == EmptySquare ) {\r
3406         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3407             result = TRUE;\r
3408         }\r
3409     }\r
3410 \r
3411     return result;\r
3412 }\r
3413 \r
3414 static HBITMAP borderBitmap;\r
3415 \r
3416 VOID\r
3417 DrawBackgroundOnDC(HDC hdc)\r
3418 {\r
3419   \r
3420   BITMAP bi;\r
3421   HDC tmphdc;\r
3422   HBITMAP hbm;\r
3423   static char oldBorder[MSG_SIZ];\r
3424   int w = 600, h = 600, mode;\r
3425 \r
3426   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3427     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3428     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3429   }\r
3430   if(borderBitmap == NULL) { // loading failed, use white\r
3431     FillRect( hdc, &boardRect, whitePieceBrush );\r
3432     return;\r
3433   }\r
3434   tmphdc = CreateCompatibleDC(hdc);\r
3435   hbm = SelectObject(tmphdc, borderBitmap);\r
3436   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3437             w = bi.bmWidth;\r
3438             h = bi.bmHeight;\r
3439   }\r
3440   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3441   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3442                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3443   SetStretchBltMode(hdc, mode);\r
3444   SelectObject(tmphdc, hbm);\r
3445   DeleteDC(tmphdc);\r
3446 }\r
3447 \r
3448 VOID\r
3449 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3450 {\r
3451   int row, column, x, y, square_color, piece_color;\r
3452   ChessSquare piece;\r
3453   HBRUSH oldBrush;\r
3454   HDC texture_hdc = NULL;\r
3455 \r
3456   /* [AS] Initialize background textures if needed */\r
3457   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3458       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3459       if( backTextureSquareSize != squareSize \r
3460        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3461           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3462           backTextureSquareSize = squareSize;\r
3463           RebuildTextureSquareInfo();\r
3464       }\r
3465 \r
3466       texture_hdc = CreateCompatibleDC( hdc );\r
3467   }\r
3468 \r
3469   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3470     for (column = 0; column < BOARD_WIDTH; column++) {\r
3471   \r
3472       SquareToPos(row, column, &x, &y);\r
3473 \r
3474       piece = board[row][column];\r
3475 \r
3476       square_color = ((column + row) % 2) == 1;\r
3477       if( gameInfo.variant == VariantXiangqi ) {\r
3478           square_color = !InPalace(row, column);\r
3479           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3480           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3481       }\r
3482       piece_color = (int) piece < (int) BlackPawn;\r
3483 \r
3484 \r
3485       /* [HGM] holdings file: light square or black */\r
3486       if(column == BOARD_LEFT-2) {\r
3487             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3488                 square_color = 1;\r
3489             else {\r
3490                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3491                 continue;\r
3492             }\r
3493       } else\r
3494       if(column == BOARD_RGHT + 1 ) {\r
3495             if( row < gameInfo.holdingsSize )\r
3496                 square_color = 1;\r
3497             else {\r
3498                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3499                 continue;\r
3500             }\r
3501       }\r
3502       if(column == BOARD_LEFT-1 ) /* left align */\r
3503             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3504       else if( column == BOARD_RGHT) /* right align */\r
3505             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3506       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3507       else\r
3508       if (appData.monoMode) {\r
3509         if (piece == EmptySquare) {\r
3510           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3511                  square_color ? WHITENESS : BLACKNESS);\r
3512         } else {\r
3513           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3514         }\r
3515       } \r
3516       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3517           /* [AS] Draw the square using a texture bitmap */\r
3518           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3519           int r = row, c = column; // [HGM] do not flip board in flipView\r
3520           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3521 \r
3522           DrawTile( x, y, \r
3523               squareSize, squareSize, \r
3524               hdc, \r
3525               texture_hdc,\r
3526               backTextureSquareInfo[r][c].mode,\r
3527               backTextureSquareInfo[r][c].x,\r
3528               backTextureSquareInfo[r][c].y );\r
3529 \r
3530           SelectObject( texture_hdc, hbm );\r
3531 \r
3532           if (piece != EmptySquare) {\r
3533               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3534           }\r
3535       }\r
3536       else {\r
3537         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3538 \r
3539         oldBrush = SelectObject(hdc, brush );\r
3540         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3541         SelectObject(hdc, oldBrush);\r
3542         if (piece != EmptySquare)\r
3543           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3544       }\r
3545     }\r
3546   }\r
3547 \r
3548   if( texture_hdc != NULL ) {\r
3549     DeleteDC( texture_hdc );\r
3550   }\r
3551 }\r
3552 \r
3553 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3554 void fputDW(FILE *f, int x)\r
3555 {\r
3556         fputc(x     & 255, f);\r
3557         fputc(x>>8  & 255, f);\r
3558         fputc(x>>16 & 255, f);\r
3559         fputc(x>>24 & 255, f);\r
3560 }\r
3561 \r
3562 #define MAX_CLIPS 200   /* more than enough */\r
3563 \r
3564 VOID\r
3565 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3566 {\r
3567 //  HBITMAP bufferBitmap;\r
3568   BITMAP bi;\r
3569 //  RECT Rect;\r
3570   HDC tmphdc;\r
3571   HBITMAP hbm;\r
3572   int w = 100, h = 50;\r
3573 \r
3574   if(logo == NULL) {\r
3575     if(!logoHeight) return;\r
3576     FillRect( hdc, &logoRect, whitePieceBrush );\r
3577   }\r
3578 //  GetClientRect(hwndMain, &Rect);\r
3579 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3580 //                                      Rect.bottom-Rect.top+1);\r
3581   tmphdc = CreateCompatibleDC(hdc);\r
3582   hbm = SelectObject(tmphdc, logo);\r
3583   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3584             w = bi.bmWidth;\r
3585             h = bi.bmHeight;\r
3586   }\r
3587   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3588                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3589   SelectObject(tmphdc, hbm);\r
3590   DeleteDC(tmphdc);\r
3591 }\r
3592 \r
3593 VOID\r
3594 DisplayLogos()\r
3595 {\r
3596   if(logoHeight) {\r
3597         HDC hdc = GetDC(hwndMain);\r
3598         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3599         if(appData.autoLogo) {\r
3600           \r
3601           switch(gameMode) { // pick logos based on game mode\r
3602             case IcsObserving:\r
3603                 whiteLogo = second.programLogo; // ICS logo\r
3604                 blackLogo = second.programLogo;\r
3605             default:\r
3606                 break;\r
3607             case IcsPlayingWhite:\r
3608                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3609                 blackLogo = second.programLogo; // ICS logo\r
3610                 break;\r
3611             case IcsPlayingBlack:\r
3612                 whiteLogo = second.programLogo; // ICS logo\r
3613                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3614                 break;\r
3615             case TwoMachinesPlay:\r
3616                 if(first.twoMachinesColor[0] == 'b') {\r
3617                     whiteLogo = second.programLogo;\r
3618                     blackLogo = first.programLogo;\r
3619                 }\r
3620                 break;\r
3621             case MachinePlaysWhite:\r
3622                 blackLogo = userLogo;\r
3623                 break;\r
3624             case MachinePlaysBlack:\r
3625                 whiteLogo = userLogo;\r
3626                 blackLogo = first.programLogo;\r
3627           }\r
3628         }\r
3629         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3630         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3631         ReleaseDC(hwndMain, hdc);\r
3632   }\r
3633 }\r
3634 \r
3635 void\r
3636 UpdateLogos(int display)\r
3637 { // called after loading new engine(s), in tourney or from menu\r
3638   LoadLogo(&first, 0, FALSE);\r
3639   LoadLogo(&second, 1, appData.icsActive);\r
3640   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3641   if(display) DisplayLogos();\r
3642 }\r
3643 \r
3644 static HDC hdcSeek;\r
3645 \r
3646 // [HGM] seekgraph\r
3647 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3648 {\r
3649     POINT stPt;\r
3650     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3651     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3652     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3653     SelectObject( hdcSeek, hp );\r
3654 }\r
3655 \r
3656 // front-end wrapper for drawing functions to do rectangles\r
3657 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3658 {\r
3659     HPEN hp;\r
3660     RECT rc;\r
3661 \r
3662     if (hdcSeek == NULL) {\r
3663     hdcSeek = GetDC(hwndMain);\r
3664       if (!appData.monoMode) {\r
3665         SelectPalette(hdcSeek, hPal, FALSE);\r
3666         RealizePalette(hdcSeek);\r
3667       }\r
3668     }\r
3669     hp = SelectObject( hdcSeek, gridPen );\r
3670     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3671     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3672     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3673     SelectObject( hdcSeek, hp );\r
3674 }\r
3675 \r
3676 // front-end wrapper for putting text in graph\r
3677 void DrawSeekText(char *buf, int x, int y)\r
3678 {\r
3679         SIZE stSize;\r
3680         SetBkMode( hdcSeek, TRANSPARENT );\r
3681         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3682         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3683 }\r
3684 \r
3685 void DrawSeekDot(int x, int y, int color)\r
3686 {\r
3687         int square = color & 0x80;\r
3688         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3689                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3690         color &= 0x7F;\r
3691         if(square)\r
3692             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3693                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3694         else\r
3695             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3696                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3697             SelectObject(hdcSeek, oldBrush);\r
3698 }\r
3699 \r
3700 void DrawSeekOpen()\r
3701 {\r
3702 }\r
3703 \r
3704 void DrawSeekClose()\r
3705 {\r
3706 }\r
3707 \r
3708 VOID\r
3709 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3710 {\r
3711   static Board lastReq[2], lastDrawn[2];\r
3712   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3713   static int lastDrawnFlipView = 0;\r
3714   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3715   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3716   HDC tmphdc;\r
3717   HDC hdcmem;\r
3718   HBITMAP bufferBitmap;\r
3719   HBITMAP oldBitmap;\r
3720   RECT Rect;\r
3721   HRGN clips[MAX_CLIPS];\r
3722   ChessSquare dragged_piece = EmptySquare;\r
3723   int nr = twoBoards*partnerUp;\r
3724 \r
3725   /* I'm undecided on this - this function figures out whether a full\r
3726    * repaint is necessary on its own, so there's no real reason to have the\r
3727    * caller tell it that.  I think this can safely be set to FALSE - but\r
3728    * if we trust the callers not to request full repaints unnessesarily, then\r
3729    * we could skip some clipping work.  In other words, only request a full\r
3730    * redraw when the majority of pieces have changed positions (ie. flip, \r
3731    * gamestart and similar)  --Hawk\r
3732    */\r
3733   Boolean fullrepaint = repaint;\r
3734 \r
3735   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3736 \r
3737   if( DrawPositionNeedsFullRepaint() ) {\r
3738       fullrepaint = TRUE;\r
3739   }\r
3740 \r
3741   if (board == NULL) {\r
3742     if (!lastReqValid[nr]) {\r
3743       return;\r
3744     }\r
3745     board = lastReq[nr];\r
3746   } else {\r
3747     CopyBoard(lastReq[nr], board);\r
3748     lastReqValid[nr] = 1;\r
3749   }\r
3750 \r
3751   if (doingSizing) {\r
3752     return;\r
3753   }\r
3754 \r
3755   if (IsIconic(hwndMain)) {\r
3756     return;\r
3757   }\r
3758 \r
3759   if (hdc == NULL) {\r
3760     hdc = GetDC(hwndMain);\r
3761     if (!appData.monoMode) {\r
3762       SelectPalette(hdc, hPal, FALSE);\r
3763       RealizePalette(hdc);\r
3764     }\r
3765     releaseDC = TRUE;\r
3766   } else {\r
3767     releaseDC = FALSE;\r
3768   }\r
3769 \r
3770   /* Create some work-DCs */\r
3771   hdcmem = CreateCompatibleDC(hdc);\r
3772   tmphdc = CreateCompatibleDC(hdc);\r
3773 \r
3774   /* If dragging is in progress, we temporarely remove the piece */\r
3775   /* [HGM] or temporarily decrease count if stacked              */\r
3776   /*       !! Moved to before board compare !!                   */\r
3777   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3778     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3779     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3780             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3781         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3782     } else \r
3783     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3784             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3785         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3786     } else \r
3787         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3788   }\r
3789 \r
3790   /* Figure out which squares need updating by comparing the \r
3791    * newest board with the last drawn board and checking if\r
3792    * flipping has changed.\r
3793    */\r
3794   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3795     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3796       for (column = 0; column < BOARD_WIDTH; column++) {\r
3797         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3798           SquareToPos(row, column, &x, &y);\r
3799           clips[num_clips++] =\r
3800             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3801         }\r
3802       }\r
3803     }\r
3804    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3805     for (i=0; i<2; i++) {\r
3806       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3807           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3808         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3809             lastDrawnHighlight.sq[i].y >= 0) {\r
3810           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3811                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3812           clips[num_clips++] =\r
3813             CreateRectRgn(x - lineGap, y - lineGap, \r
3814                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3815         }\r
3816         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3817           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3818           clips[num_clips++] =\r
3819             CreateRectRgn(x - lineGap, y - lineGap, \r
3820                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3821         }\r
3822       }\r
3823     }\r
3824     for (i=0; i<2; i++) {\r
3825       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3826           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3827         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3828             lastDrawnPremove.sq[i].y >= 0) {\r
3829           SquareToPos(lastDrawnPremove.sq[i].y,\r
3830                       lastDrawnPremove.sq[i].x, &x, &y);\r
3831           clips[num_clips++] =\r
3832             CreateRectRgn(x - lineGap, y - lineGap, \r
3833                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3834         }\r
3835         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3836             premoveHighlightInfo.sq[i].y >= 0) {\r
3837           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3838                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3839           clips[num_clips++] =\r
3840             CreateRectRgn(x - lineGap, y - lineGap, \r
3841                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3842         }\r
3843       }\r
3844     }\r
3845    } else { // nr == 1\r
3846         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3847         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3848         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3849         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3850       for (i=0; i<2; i++) {\r
3851         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3852             partnerHighlightInfo.sq[i].y >= 0) {\r
3853           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3854                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3855           clips[num_clips++] =\r
3856             CreateRectRgn(x - lineGap, y - lineGap, \r
3857                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3858         }\r
3859         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3860             oldPartnerHighlight.sq[i].y >= 0) {\r
3861           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3862                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3863           clips[num_clips++] =\r
3864             CreateRectRgn(x - lineGap, y - lineGap, \r
3865                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3866         }\r
3867       }\r
3868    }\r
3869   } else {\r
3870     fullrepaint = TRUE;\r
3871   }\r
3872 \r
3873   /* Create a buffer bitmap - this is the actual bitmap\r
3874    * being written to.  When all the work is done, we can\r
3875    * copy it to the real DC (the screen).  This avoids\r
3876    * the problems with flickering.\r
3877    */\r
3878   GetClientRect(hwndMain, &Rect);\r
3879   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3880                                         Rect.bottom-Rect.top+1);\r
3881   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3882   if (!appData.monoMode) {\r
3883     SelectPalette(hdcmem, hPal, FALSE);\r
3884   }\r
3885 \r
3886   /* Create clips for dragging */\r
3887   if (!fullrepaint) {\r
3888     if (dragInfo.from.x >= 0) {\r
3889       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3890       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3891     }\r
3892     if (dragInfo.start.x >= 0) {\r
3893       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3894       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3895     }\r
3896     if (dragInfo.pos.x >= 0) {\r
3897       x = dragInfo.pos.x - squareSize / 2;\r
3898       y = dragInfo.pos.y - squareSize / 2;\r
3899       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3900     }\r
3901     if (dragInfo.lastpos.x >= 0) {\r
3902       x = dragInfo.lastpos.x - squareSize / 2;\r
3903       y = dragInfo.lastpos.y - squareSize / 2;\r
3904       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3905     }\r
3906   }\r
3907 \r
3908   /* Are we animating a move?  \r
3909    * If so, \r
3910    *   - remove the piece from the board (temporarely)\r
3911    *   - calculate the clipping region\r
3912    */\r
3913   if (!fullrepaint) {\r
3914     if (animInfo.piece != EmptySquare) {\r
3915       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3916       x = boardRect.left + animInfo.lastpos.x;\r
3917       y = boardRect.top + animInfo.lastpos.y;\r
3918       x2 = boardRect.left + animInfo.pos.x;\r
3919       y2 = boardRect.top + animInfo.pos.y;\r
3920       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3921       /* Slight kludge.  The real problem is that after AnimateMove is\r
3922          done, the position on the screen does not match lastDrawn.\r
3923          This currently causes trouble only on e.p. captures in\r
3924          atomic, where the piece moves to an empty square and then\r
3925          explodes.  The old and new positions both had an empty square\r
3926          at the destination, but animation has drawn a piece there and\r
3927          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3928       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3929     }\r
3930   }\r
3931 \r
3932   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3933   if (num_clips == 0)\r
3934     fullrepaint = TRUE;\r
3935 \r
3936   /* Set clipping on the memory DC */\r
3937   if (!fullrepaint) {\r
3938     SelectClipRgn(hdcmem, clips[0]);\r
3939     for (x = 1; x < num_clips; x++) {\r
3940       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3941         abort();  // this should never ever happen!\r
3942     }\r
3943   }\r
3944 \r
3945   /* Do all the drawing to the memory DC */\r
3946   if(explodeInfo.radius) { // [HGM] atomic\r
3947         HBRUSH oldBrush;\r
3948         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3949         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3950         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3951         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3952         x += squareSize/2;\r
3953         y += squareSize/2;\r
3954         if(!fullrepaint) {\r
3955           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3956           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3957         }\r
3958         DrawGridOnDC(hdcmem);\r
3959         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3960         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3961         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3962         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3963         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3964         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3965         SelectObject(hdcmem, oldBrush);\r
3966   } else {\r
3967     if(border) DrawBackgroundOnDC(hdcmem);\r
3968     DrawGridOnDC(hdcmem);\r
3969     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3970         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3971         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3972     } else {\r
3973         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3974         oldPartnerHighlight = partnerHighlightInfo;\r
3975     }\r
3976     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3977   }\r
3978   if(nr == 0) // [HGM] dual: markers only on left board\r
3979   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3980     for (column = 0; column < BOARD_WIDTH; column++) {\r
3981         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3982             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3983             SquareToPos(row, column, &x, &y);\r
3984             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3985                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3986             SelectObject(hdcmem, oldBrush);\r
3987         }\r
3988     }\r
3989   }\r
3990 \r
3991   if( appData.highlightMoveWithArrow ) {\r
3992     DrawArrowHighlight(hdcmem);\r
3993   }\r
3994 \r
3995   DrawCoordsOnDC(hdcmem);\r
3996 \r
3997   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3998                  /* to make sure lastDrawn contains what is actually drawn */\r
3999 \r
4000   /* Put the dragged piece back into place and draw it (out of place!) */\r
4001     if (dragged_piece != EmptySquare) {\r
4002     /* [HGM] or restack */\r
4003     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4004                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4005     else\r
4006     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4007                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4008 \r
4009     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4010     x = dragInfo.pos.x - squareSize / 2;\r
4011     y = dragInfo.pos.y - squareSize / 2;\r
4012     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4013                   ((int) dragInfo.piece < (int) BlackPawn), \r
4014                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4015   }   \r
4016   \r
4017   /* Put the animated piece back into place and draw it */\r
4018   if (animInfo.piece != EmptySquare) {\r
4019     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4020     x = boardRect.left + animInfo.pos.x;\r
4021     y = boardRect.top + animInfo.pos.y;\r
4022     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4023                   ((int) animInfo.piece < (int) BlackPawn),\r
4024                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4025   }\r
4026 \r
4027   /* Release the bufferBitmap by selecting in the old bitmap \r
4028    * and delete the memory DC\r
4029    */\r
4030   SelectObject(hdcmem, oldBitmap);\r
4031   DeleteDC(hdcmem);\r
4032 \r
4033   /* Set clipping on the target DC */\r
4034   if (!fullrepaint) {\r
4035     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4036         RECT rect;\r
4037         GetRgnBox(clips[x], &rect);\r
4038         DeleteObject(clips[x]);\r
4039         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4040                           rect.right + wpMain.width/2, rect.bottom);\r
4041     }\r
4042     SelectClipRgn(hdc, clips[0]);\r
4043     for (x = 1; x < num_clips; x++) {\r
4044       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4045         abort();   // this should never ever happen!\r
4046     } \r
4047   }\r
4048 \r
4049   /* Copy the new bitmap onto the screen in one go.\r
4050    * This way we avoid any flickering\r
4051    */\r
4052   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4053   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4054          boardRect.right - boardRect.left,\r
4055          boardRect.bottom - boardRect.top,\r
4056          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4057   if(saveDiagFlag) { \r
4058     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4059     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4060 \r
4061     GetObject(bufferBitmap, sizeof(b), &b);\r
4062     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4063         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4064         bih.biWidth = b.bmWidth;\r
4065         bih.biHeight = b.bmHeight;\r
4066         bih.biPlanes = 1;\r
4067         bih.biBitCount = b.bmBitsPixel;\r
4068         bih.biCompression = 0;\r
4069         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4070         bih.biXPelsPerMeter = 0;\r
4071         bih.biYPelsPerMeter = 0;\r
4072         bih.biClrUsed = 0;\r
4073         bih.biClrImportant = 0;\r
4074 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4075 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4076         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4077 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4078 \r
4079         wb = b.bmWidthBytes;\r
4080         // count colors\r
4081         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4082                 int k = ((int*) pData)[i];\r
4083                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4084                 if(j >= 16) break;\r
4085                 color[j] = k;\r
4086                 if(j >= nrColors) nrColors = j+1;\r
4087         }\r
4088         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4089                 INT p = 0;\r
4090                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4091                     for(w=0; w<(wb>>2); w+=2) {\r
4092                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4093                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4094                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4095                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4096                         pData[p++] = m | j<<4;\r
4097                     }\r
4098                     while(p&3) pData[p++] = 0;\r
4099                 }\r
4100                 fac = 3;\r
4101                 wb = ((wb+31)>>5)<<2;\r
4102         }\r
4103         // write BITMAPFILEHEADER\r
4104         fprintf(diagFile, "BM");\r
4105         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4106         fputDW(diagFile, 0);\r
4107         fputDW(diagFile, 0x36 + (fac?64:0));\r
4108         // write BITMAPINFOHEADER\r
4109         fputDW(diagFile, 40);\r
4110         fputDW(diagFile, b.bmWidth);\r
4111         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4112         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4113         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4114         fputDW(diagFile, 0);\r
4115         fputDW(diagFile, 0);\r
4116         fputDW(diagFile, 0);\r
4117         fputDW(diagFile, 0);\r
4118         fputDW(diagFile, 0);\r
4119         fputDW(diagFile, 0);\r
4120         // write color table\r
4121         if(fac)\r
4122         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4123         // write bitmap data\r
4124         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4125                 fputc(pData[i], diagFile);\r
4126         free(pData);\r
4127      }\r
4128   }\r
4129 \r
4130   SelectObject(tmphdc, oldBitmap);\r
4131 \r
4132   /* Massive cleanup */\r
4133   for (x = 0; x < num_clips; x++)\r
4134     DeleteObject(clips[x]);\r
4135 \r
4136   DeleteDC(tmphdc);\r
4137   DeleteObject(bufferBitmap);\r
4138 \r
4139   if (releaseDC) \r
4140     ReleaseDC(hwndMain, hdc);\r
4141   \r
4142   if (lastDrawnFlipView != flipView && nr == 0) {\r
4143     if (flipView)\r
4144       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4145     else\r
4146       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4147   }\r
4148 \r
4149 /*  CopyBoard(lastDrawn, board);*/\r
4150   lastDrawnHighlight = highlightInfo;\r
4151   lastDrawnPremove   = premoveHighlightInfo;\r
4152   lastDrawnFlipView = flipView;\r
4153   lastDrawnValid[nr] = 1;\r
4154 }\r
4155 \r
4156 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4157 int\r
4158 SaveDiagram(f)\r
4159      FILE *f;\r
4160 {\r
4161     saveDiagFlag = 1; diagFile = f;\r
4162     HDCDrawPosition(NULL, TRUE, NULL);\r
4163     saveDiagFlag = 0;\r
4164 \r
4165     fclose(f);\r
4166     return TRUE;\r
4167 }\r
4168 \r
4169 \r
4170 /*---------------------------------------------------------------------------*\\r
4171 | CLIENT PAINT PROCEDURE\r
4172 |   This is the main event-handler for the WM_PAINT message.\r
4173 |\r
4174 \*---------------------------------------------------------------------------*/\r
4175 VOID\r
4176 PaintProc(HWND hwnd)\r
4177 {\r
4178   HDC         hdc;\r
4179   PAINTSTRUCT ps;\r
4180   HFONT       oldFont;\r
4181 \r
4182   if((hdc = BeginPaint(hwnd, &ps))) {\r
4183     if (IsIconic(hwnd)) {\r
4184       DrawIcon(hdc, 2, 2, iconCurrent);\r
4185     } else {\r
4186       if (!appData.monoMode) {\r
4187         SelectPalette(hdc, hPal, FALSE);\r
4188         RealizePalette(hdc);\r
4189       }\r
4190       HDCDrawPosition(hdc, 1, NULL);\r
4191       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4192         flipView = !flipView; partnerUp = !partnerUp;\r
4193         HDCDrawPosition(hdc, 1, NULL);\r
4194         flipView = !flipView; partnerUp = !partnerUp;\r
4195       }\r
4196       oldFont =\r
4197         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4198       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4199                  ETO_CLIPPED|ETO_OPAQUE,\r
4200                  &messageRect, messageText, strlen(messageText), NULL);\r
4201       SelectObject(hdc, oldFont);\r
4202       DisplayBothClocks();\r
4203       DisplayLogos();\r
4204     }\r
4205     EndPaint(hwnd,&ps);\r
4206   }\r
4207 \r
4208   return;\r
4209 }\r
4210 \r
4211 \r
4212 /*\r
4213  * If the user selects on a border boundary, return -1; if off the board,\r
4214  *   return -2.  Otherwise map the event coordinate to the square.\r
4215  * The offset boardRect.left or boardRect.top must already have been\r
4216  *   subtracted from x.\r
4217  */\r
4218 int EventToSquare(x, limit)\r
4219      int x, limit;\r
4220 {\r
4221   if (x <= border)\r
4222     return -2;\r
4223   if (x < lineGap + border)\r
4224     return -1;\r
4225   x -= lineGap + border;\r
4226   if ((x % (squareSize + lineGap)) >= squareSize)\r
4227     return -1;\r
4228   x /= (squareSize + lineGap);\r
4229     if (x >= limit)\r
4230     return -2;\r
4231   return x;\r
4232 }\r
4233 \r
4234 typedef struct {\r
4235   char piece;\r
4236   int command;\r
4237   char* name;\r
4238 } DropEnable;\r
4239 \r
4240 DropEnable dropEnables[] = {\r
4241   { 'P', DP_Pawn, N_("Pawn") },\r
4242   { 'N', DP_Knight, N_("Knight") },\r
4243   { 'B', DP_Bishop, N_("Bishop") },\r
4244   { 'R', DP_Rook, N_("Rook") },\r
4245   { 'Q', DP_Queen, N_("Queen") },\r
4246 };\r
4247 \r
4248 VOID\r
4249 SetupDropMenu(HMENU hmenu)\r
4250 {\r
4251   int i, count, enable;\r
4252   char *p;\r
4253   extern char white_holding[], black_holding[];\r
4254   char item[MSG_SIZ];\r
4255 \r
4256   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4257     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4258                dropEnables[i].piece);\r
4259     count = 0;\r
4260     while (p && *p++ == dropEnables[i].piece) count++;\r
4261       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4262     enable = count > 0 || !appData.testLegality\r
4263       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4264                       && !appData.icsActive);\r
4265     ModifyMenu(hmenu, dropEnables[i].command,\r
4266                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4267                dropEnables[i].command, item);\r
4268   }\r
4269 }\r
4270 \r
4271 void DragPieceBegin(int x, int y, Boolean instantly)\r
4272 {\r
4273       dragInfo.lastpos.x = boardRect.left + x;\r
4274       dragInfo.lastpos.y = boardRect.top + y;\r
4275       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4276       dragInfo.from.x = fromX;\r
4277       dragInfo.from.y = fromY;\r
4278       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4279       dragInfo.start = dragInfo.from;\r
4280       SetCapture(hwndMain);\r
4281 }\r
4282 \r
4283 void DragPieceEnd(int x, int y)\r
4284 {\r
4285     ReleaseCapture();\r
4286     dragInfo.start.x = dragInfo.start.y = -1;\r
4287     dragInfo.from = dragInfo.start;\r
4288     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4289 }\r
4290 \r
4291 void ChangeDragPiece(ChessSquare piece)\r
4292 {\r
4293     dragInfo.piece = piece;\r
4294 }\r
4295 \r
4296 /* Event handler for mouse messages */\r
4297 VOID\r
4298 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4299 {\r
4300   int x, y, menuNr;\r
4301   POINT pt;\r
4302   static int recursive = 0;\r
4303   HMENU hmenu;\r
4304   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4305 \r
4306   if (recursive) {\r
4307     if (message == WM_MBUTTONUP) {\r
4308       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4309          to the middle button: we simulate pressing the left button too!\r
4310          */\r
4311       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4312       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4313     }\r
4314     return;\r
4315   }\r
4316   recursive++;\r
4317   \r
4318   pt.x = LOWORD(lParam);\r
4319   pt.y = HIWORD(lParam);\r
4320   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4321   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4322   if (!flipView && y >= 0) {\r
4323     y = BOARD_HEIGHT - 1 - y;\r
4324   }\r
4325   if (flipView && x >= 0) {\r
4326     x = BOARD_WIDTH - 1 - x;\r
4327   }\r
4328 \r
4329   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4330   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4331 \r
4332   switch (message) {\r
4333   case WM_LBUTTONDOWN:\r
4334       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4335         ClockClick(flipClock); break;\r
4336       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4337         ClockClick(!flipClock); break;\r
4338       }\r
4339     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4340       dragInfo.start.x = dragInfo.start.y = -1;\r
4341       dragInfo.from = dragInfo.start;\r
4342     }\r
4343     if(fromX == -1 && frozen) { // not sure where this is for\r
4344                 fromX = fromY = -1; \r
4345       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4346       break;\r
4347     }\r
4348       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4349       DrawPosition(TRUE, NULL);\r
4350     break;\r
4351 \r
4352   case WM_LBUTTONUP:\r
4353       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4354       DrawPosition(TRUE, NULL);\r
4355     break;\r
4356 \r
4357   case WM_MOUSEMOVE:\r
4358     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4359     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4360     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4361     if ((appData.animateDragging || appData.highlightDragging)\r
4362         && (wParam & MK_LBUTTON || dragging == 2)\r
4363         && dragInfo.from.x >= 0) \r
4364     {\r
4365       BOOL full_repaint = FALSE;\r
4366 \r
4367       if (appData.animateDragging) {\r
4368         dragInfo.pos = pt;\r
4369       }\r
4370       if (appData.highlightDragging) {\r
4371         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4372         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4373             full_repaint = TRUE;\r
4374         }\r
4375       }\r
4376       \r
4377       DrawPosition( full_repaint, NULL);\r
4378       \r
4379       dragInfo.lastpos = dragInfo.pos;\r
4380     }\r
4381     break;\r
4382 \r
4383   case WM_MOUSEWHEEL: // [DM]\r
4384     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4385        /* Mouse Wheel is being rolled forward\r
4386         * Play moves forward\r
4387         */\r
4388        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4389                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4390        /* Mouse Wheel is being rolled backward\r
4391         * Play moves backward\r
4392         */\r
4393        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4394                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4395     }\r
4396     break;\r
4397 \r
4398   case WM_MBUTTONUP:\r
4399   case WM_RBUTTONUP:\r
4400     ReleaseCapture();\r
4401     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4402     break;\r
4403  \r
4404   case WM_MBUTTONDOWN:\r
4405   case WM_RBUTTONDOWN:\r
4406     ErrorPopDown();\r
4407     ReleaseCapture();\r
4408     fromX = fromY = -1;\r
4409     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4410     dragInfo.start.x = dragInfo.start.y = -1;\r
4411     dragInfo.from = dragInfo.start;\r
4412     dragInfo.lastpos = dragInfo.pos;\r
4413     if (appData.highlightDragging) {\r
4414       ClearHighlights();\r
4415     }\r
4416     if(y == -2) {\r
4417       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4418       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4419           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4420       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4421           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4422       }\r
4423       break;\r
4424     }\r
4425     DrawPosition(TRUE, NULL);\r
4426 \r
4427     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4428     switch (menuNr) {\r
4429     case 0:\r
4430       if (message == WM_MBUTTONDOWN) {\r
4431         buttonCount = 3;  /* even if system didn't think so */\r
4432         if (wParam & MK_SHIFT) \r
4433           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4434         else\r
4435           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4436       } else { /* message == WM_RBUTTONDOWN */\r
4437         /* Just have one menu, on the right button.  Windows users don't\r
4438            think to try the middle one, and sometimes other software steals\r
4439            it, or it doesn't really exist. */\r
4440         if(gameInfo.variant != VariantShogi)\r
4441             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4442         else\r
4443             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4444       }\r
4445       break;\r
4446     case 2:\r
4447       SetCapture(hwndMain);\r
4448       break;\r
4449     case 1:\r
4450       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4451       SetupDropMenu(hmenu);\r
4452       MenuPopup(hwnd, pt, hmenu, -1);\r
4453     default:\r
4454       break;\r
4455     }\r
4456     break;\r
4457   }\r
4458 \r
4459   recursive--;\r
4460 }\r
4461 \r
4462 /* Preprocess messages for buttons in main window */\r
4463 LRESULT CALLBACK\r
4464 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4465 {\r
4466   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4467   int i, dir;\r
4468 \r
4469   for (i=0; i<N_BUTTONS; i++) {\r
4470     if (buttonDesc[i].id == id) break;\r
4471   }\r
4472   if (i == N_BUTTONS) return 0;\r
4473   switch (message) {\r
4474   case WM_KEYDOWN:\r
4475     switch (wParam) {\r
4476     case VK_LEFT:\r
4477     case VK_RIGHT:\r
4478       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4479       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4480       return TRUE;\r
4481     }\r
4482     break;\r
4483   case WM_CHAR:\r
4484     switch (wParam) {\r
4485     case '\r':\r
4486       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4487       return TRUE;\r
4488     default:\r
4489       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4490         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4491         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4492         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4493         SetFocus(h);\r
4494         SendMessage(h, WM_CHAR, wParam, lParam);\r
4495         return TRUE;\r
4496       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4497         TypeInEvent((char)wParam);\r
4498       }\r
4499       break;\r
4500     }\r
4501     break;\r
4502   }\r
4503   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4504 }\r
4505 \r
4506 static int promoStyle;\r
4507 \r
4508 /* Process messages for Promotion dialog box */\r
4509 LRESULT CALLBACK\r
4510 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4511 {\r
4512   char promoChar;\r
4513 \r
4514   switch (message) {\r
4515 \r
4516   case WM_INITDIALOG: /* message: initialize dialog box */\r
4517     /* Center the dialog over the application window */\r
4518     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4519     Translate(hDlg, DLG_PromotionKing);\r
4520     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4521       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4522        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4523        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4524                SW_SHOW : SW_HIDE);\r
4525     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4526     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4527        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4528          PieceToChar(WhiteAngel) != '~') ||\r
4529         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4530          PieceToChar(BlackAngel) != '~')   ) ?\r
4531                SW_SHOW : SW_HIDE);\r
4532     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4533        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4534          PieceToChar(WhiteMarshall) != '~') ||\r
4535         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4536          PieceToChar(BlackMarshall) != '~')   ) ?\r
4537                SW_SHOW : SW_HIDE);\r
4538     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4539     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4540     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4541     if(promoStyle) {\r
4542         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4543         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4544         SetWindowText(hDlg, "Promote?");\r
4545     }\r
4546     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4547        gameInfo.variant == VariantSuper ?\r
4548                SW_SHOW : SW_HIDE);\r
4549     return TRUE;\r
4550 \r
4551   case WM_COMMAND: /* message: received a command */\r
4552     switch (LOWORD(wParam)) {\r
4553     case IDCANCEL:\r
4554       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4555       ClearHighlights();\r
4556       DrawPosition(FALSE, NULL);\r
4557       return TRUE;\r
4558     case PB_King:\r
4559       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4560       break;\r
4561     case PB_Queen:\r
4562       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4563       break;\r
4564     case PB_Rook:\r
4565       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4566       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4567       break;\r
4568     case PB_Bishop:\r
4569       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4570       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4571       break;\r
4572     case PB_Chancellor:\r
4573       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4574       break;\r
4575     case PB_Archbishop:\r
4576       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4577       break;\r
4578     case PB_Knight:\r
4579       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4580                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4581       break;\r
4582     default:\r
4583       return FALSE;\r
4584     }\r
4585     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4586     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4587     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4588     fromX = fromY = -1;\r
4589     if (!appData.highlightLastMove) {\r
4590       ClearHighlights();\r
4591       DrawPosition(FALSE, NULL);\r
4592     }\r
4593     return TRUE;\r
4594   }\r
4595   return FALSE;\r
4596 }\r
4597 \r
4598 /* Pop up promotion dialog */\r
4599 VOID\r
4600 PromotionPopup(HWND hwnd)\r
4601 {\r
4602   FARPROC lpProc;\r
4603 \r
4604   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4605   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4606     hwnd, (DLGPROC)lpProc);\r
4607   FreeProcInstance(lpProc);\r
4608 }\r
4609 \r
4610 void\r
4611 PromotionPopUp(char choice)\r
4612 {\r
4613   promoStyle = (choice == '+');\r
4614   DrawPosition(TRUE, NULL);\r
4615   PromotionPopup(hwndMain);\r
4616 }\r
4617 \r
4618 VOID\r
4619 LoadGameDialog(HWND hwnd, char* title)\r
4620 {\r
4621   UINT number = 0;\r
4622   FILE *f;\r
4623   char fileTitle[MSG_SIZ];\r
4624   f = OpenFileDialog(hwnd, "rb", "",\r
4625                      appData.oldSaveStyle ? "gam" : "pgn",\r
4626                      GAME_FILT,\r
4627                      title, &number, fileTitle, NULL);\r
4628   if (f != NULL) {\r
4629     cmailMsgLoaded = FALSE;\r
4630     if (number == 0) {\r
4631       int error = GameListBuild(f);\r
4632       if (error) {\r
4633         DisplayError(_("Cannot build game list"), error);\r
4634       } else if (!ListEmpty(&gameList) &&\r
4635                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4636         GameListPopUp(f, fileTitle);\r
4637         return;\r
4638       }\r
4639       GameListDestroy();\r
4640       number = 1;\r
4641     }\r
4642     LoadGame(f, number, fileTitle, FALSE);\r
4643   }\r
4644 }\r
4645 \r
4646 int get_term_width()\r
4647 {\r
4648     HDC hdc;\r
4649     TEXTMETRIC tm;\r
4650     RECT rc;\r
4651     HFONT hfont, hold_font;\r
4652     LOGFONT lf;\r
4653     HWND hText;\r
4654 \r
4655     if (hwndConsole)\r
4656         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4657     else\r
4658         return 79;\r
4659 \r
4660     // get the text metrics\r
4661     hdc = GetDC(hText);\r
4662     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4663     if (consoleCF.dwEffects & CFE_BOLD)\r
4664         lf.lfWeight = FW_BOLD;\r
4665     if (consoleCF.dwEffects & CFE_ITALIC)\r
4666         lf.lfItalic = TRUE;\r
4667     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4668         lf.lfStrikeOut = TRUE;\r
4669     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4670         lf.lfUnderline = TRUE;\r
4671     hfont = CreateFontIndirect(&lf);\r
4672     hold_font = SelectObject(hdc, hfont);\r
4673     GetTextMetrics(hdc, &tm);\r
4674     SelectObject(hdc, hold_font);\r
4675     DeleteObject(hfont);\r
4676     ReleaseDC(hText, hdc);\r
4677 \r
4678     // get the rectangle\r
4679     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4680 \r
4681     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4682 }\r
4683 \r
4684 void UpdateICSWidth(HWND hText)\r
4685 {\r
4686     LONG old_width, new_width;\r
4687 \r
4688     new_width = get_term_width(hText, FALSE);\r
4689     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4690     if (new_width != old_width)\r
4691     {\r
4692         ics_update_width(new_width);\r
4693         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4694     }\r
4695 }\r
4696 \r
4697 VOID\r
4698 ChangedConsoleFont()\r
4699 {\r
4700   CHARFORMAT cfmt;\r
4701   CHARRANGE tmpsel, sel;\r
4702   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4703   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4704   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4705   PARAFORMAT paraf;\r
4706 \r
4707   cfmt.cbSize = sizeof(CHARFORMAT);\r
4708   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4709     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4710                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4711   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4712    * size.  This was undocumented in the version of MSVC++ that I had\r
4713    * when I wrote the code, but is apparently documented now.\r
4714    */\r
4715   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4716   cfmt.bCharSet = f->lf.lfCharSet;\r
4717   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4718   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4719   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4720   /* Why are the following seemingly needed too? */\r
4721   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4722   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4723   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4724   tmpsel.cpMin = 0;\r
4725   tmpsel.cpMax = -1; /*999999?*/\r
4726   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4727   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4728   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4729    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4730    */\r
4731   paraf.cbSize = sizeof(paraf);\r
4732   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4733   paraf.dxStartIndent = 0;\r
4734   paraf.dxOffset = WRAP_INDENT;\r
4735   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4736   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4737   UpdateICSWidth(hText);\r
4738 }\r
4739 \r
4740 /*---------------------------------------------------------------------------*\\r
4741  *\r
4742  * Window Proc for main window\r
4743  *\r
4744 \*---------------------------------------------------------------------------*/\r
4745 \r
4746 /* Process messages for main window, etc. */\r
4747 LRESULT CALLBACK\r
4748 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4749 {\r
4750   FARPROC lpProc;\r
4751   int wmId;\r
4752   char *defName;\r
4753   FILE *f;\r
4754   UINT number;\r
4755   char fileTitle[MSG_SIZ];\r
4756   static SnapData sd;\r
4757   static int peek=0;\r
4758 \r
4759   switch (message) {\r
4760 \r
4761   case WM_PAINT: /* message: repaint portion of window */\r
4762     PaintProc(hwnd);\r
4763     break;\r
4764 \r
4765   case WM_ERASEBKGND:\r
4766     if (IsIconic(hwnd)) {\r
4767       /* Cheat; change the message */\r
4768       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4769     } else {\r
4770       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4771     }\r
4772     break;\r
4773 \r
4774   case WM_LBUTTONDOWN:\r
4775   case WM_MBUTTONDOWN:\r
4776   case WM_RBUTTONDOWN:\r
4777   case WM_LBUTTONUP:\r
4778   case WM_MBUTTONUP:\r
4779   case WM_RBUTTONUP:\r
4780   case WM_MOUSEMOVE:\r
4781   case WM_MOUSEWHEEL:\r
4782     MouseEvent(hwnd, message, wParam, lParam);\r
4783     break;\r
4784 \r
4785   case WM_KEYUP:\r
4786     if((char)wParam == '\b') {\r
4787       ForwardEvent(); peek = 0;\r
4788     }\r
4789 \r
4790     JAWS_KBUP_NAVIGATION\r
4791 \r
4792     break;\r
4793 \r
4794   case WM_KEYDOWN:\r
4795     if((char)wParam == '\b') {\r
4796       if(!peek) BackwardEvent(), peek = 1;\r
4797     }\r
4798 \r
4799     JAWS_KBDOWN_NAVIGATION\r
4800 \r
4801     break;\r
4802 \r
4803   case WM_CHAR:\r
4804     \r
4805     JAWS_ALT_INTERCEPT\r
4806 \r
4807     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4808         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4809         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4810         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4811         SetFocus(h);\r
4812         SendMessage(h, message, wParam, lParam);\r
4813     } else if(lParam != KF_REPEAT) {\r
4814         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4815                 TypeInEvent((char)wParam);\r
4816         } else if((char)wParam == 003) CopyGameToClipboard();\r
4817          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4818     }\r
4819 \r
4820     break;\r
4821 \r
4822   case WM_PALETTECHANGED:\r
4823     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4824       int nnew;\r
4825       HDC hdc = GetDC(hwndMain);\r
4826       SelectPalette(hdc, hPal, TRUE);\r
4827       nnew = RealizePalette(hdc);\r
4828       if (nnew > 0) {\r
4829         paletteChanged = TRUE;\r
4830 \r
4831         InvalidateRect(hwnd, &boardRect, FALSE);\r
4832       }\r
4833       ReleaseDC(hwnd, hdc);\r
4834     }\r
4835     break;\r
4836 \r
4837   case WM_QUERYNEWPALETTE:\r
4838     if (!appData.monoMode /*&& paletteChanged*/) {\r
4839       int nnew;\r
4840       HDC hdc = GetDC(hwndMain);\r
4841       paletteChanged = FALSE;\r
4842       SelectPalette(hdc, hPal, FALSE);\r
4843       nnew = RealizePalette(hdc);\r
4844       if (nnew > 0) {\r
4845         InvalidateRect(hwnd, &boardRect, FALSE);\r
4846       }\r
4847       ReleaseDC(hwnd, hdc);\r
4848       return TRUE;\r
4849     }\r
4850     return FALSE;\r
4851 \r
4852   case WM_COMMAND: /* message: command from application menu */\r
4853     wmId    = LOWORD(wParam);\r
4854 \r
4855     switch (wmId) {\r
4856     case IDM_NewGame:\r
4857       ResetGameEvent();\r
4858       SAY("new game enter a move to play against the computer with white");\r
4859       break;\r
4860 \r
4861     case IDM_NewGameFRC:\r
4862       if( NewGameFRC() == 0 ) {\r
4863         ResetGameEvent();\r
4864       }\r
4865       break;\r
4866 \r
4867     case IDM_NewVariant:\r
4868       NewVariantPopup(hwnd);\r
4869       break;\r
4870 \r
4871     case IDM_LoadGame:\r
4872       LoadGameDialog(hwnd, _("Load Game from File"));\r
4873       break;\r
4874 \r
4875     case IDM_LoadNextGame:\r
4876       ReloadGame(1);\r
4877       break;\r
4878 \r
4879     case IDM_LoadPrevGame:\r
4880       ReloadGame(-1);\r
4881       break;\r
4882 \r
4883     case IDM_ReloadGame:\r
4884       ReloadGame(0);\r
4885       break;\r
4886 \r
4887     case IDM_LoadPosition:\r
4888       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4889         Reset(FALSE, TRUE);\r
4890       }\r
4891       number = 1;\r
4892       f = OpenFileDialog(hwnd, "rb", "",\r
4893                          appData.oldSaveStyle ? "pos" : "fen",\r
4894                          POSITION_FILT,\r
4895                          _("Load Position from File"), &number, fileTitle, NULL);\r
4896       if (f != NULL) {\r
4897         LoadPosition(f, number, fileTitle);\r
4898       }\r
4899       break;\r
4900 \r
4901     case IDM_LoadNextPosition:\r
4902       ReloadPosition(1);\r
4903       break;\r
4904 \r
4905     case IDM_LoadPrevPosition:\r
4906       ReloadPosition(-1);\r
4907       break;\r
4908 \r
4909     case IDM_ReloadPosition:\r
4910       ReloadPosition(0);\r
4911       break;\r
4912 \r
4913     case IDM_SaveGame:\r
4914       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4915       f = OpenFileDialog(hwnd, "a", defName,\r
4916                          appData.oldSaveStyle ? "gam" : "pgn",\r
4917                          GAME_FILT,\r
4918                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4919       if (f != NULL) {\r
4920         SaveGame(f, 0, "");\r
4921       }\r
4922       break;\r
4923 \r
4924     case IDM_SavePosition:\r
4925       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4926       f = OpenFileDialog(hwnd, "a", defName,\r
4927                          appData.oldSaveStyle ? "pos" : "fen",\r
4928                          POSITION_FILT,\r
4929                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4930       if (f != NULL) {\r
4931         SavePosition(f, 0, "");\r
4932       }\r
4933       break;\r
4934 \r
4935     case IDM_SaveDiagram:\r
4936       defName = "diagram";\r
4937       f = OpenFileDialog(hwnd, "wb", defName,\r
4938                          "bmp",\r
4939                          DIAGRAM_FILT,\r
4940                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4941       if (f != NULL) {\r
4942         SaveDiagram(f);\r
4943       }\r
4944       break;\r
4945 \r
4946     case IDM_SaveSelected:\r
4947       f = OpenFileDialog(hwnd, "a", "",\r
4948                          "pgn",\r
4949                          GAME_FILT,\r
4950                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4951       if (f != NULL) {\r
4952         SaveSelected(f, 0, "");\r
4953       }\r
4954       break;\r
4955 \r
4956     case IDM_CreateBook:\r
4957       CreateBookEvent();\r
4958       break;\r
4959 \r
4960     case IDM_CopyGame:\r
4961       CopyGameToClipboard();\r
4962       break;\r
4963 \r
4964     case IDM_PasteGame:\r
4965       PasteGameFromClipboard();\r
4966       break;\r
4967 \r
4968     case IDM_CopyGameListToClipboard:\r
4969       CopyGameListToClipboard();\r
4970       break;\r
4971 \r
4972     /* [AS] Autodetect FEN or PGN data */\r
4973     case IDM_PasteAny:\r
4974       PasteGameOrFENFromClipboard();\r
4975       break;\r
4976 \r
4977     /* [AS] Move history */\r
4978     case IDM_ShowMoveHistory:\r
4979         if( MoveHistoryIsUp() ) {\r
4980             MoveHistoryPopDown();\r
4981         }\r
4982         else {\r
4983             MoveHistoryPopUp();\r
4984         }\r
4985         break;\r
4986 \r
4987     /* [AS] Eval graph */\r
4988     case IDM_ShowEvalGraph:\r
4989         if( EvalGraphIsUp() ) {\r
4990             EvalGraphPopDown();\r
4991         }\r
4992         else {\r
4993             EvalGraphPopUp();\r
4994             SetFocus(hwndMain);\r
4995         }\r
4996         break;\r
4997 \r
4998     /* [AS] Engine output */\r
4999     case IDM_ShowEngineOutput:\r
5000         if( EngineOutputIsUp() ) {\r
5001             EngineOutputPopDown();\r
5002         }\r
5003         else {\r
5004             EngineOutputPopUp();\r
5005         }\r
5006         break;\r
5007 \r
5008     /* [AS] User adjudication */\r
5009     case IDM_UserAdjudication_White:\r
5010         UserAdjudicationEvent( +1 );\r
5011         break;\r
5012 \r
5013     case IDM_UserAdjudication_Black:\r
5014         UserAdjudicationEvent( -1 );\r
5015         break;\r
5016 \r
5017     case IDM_UserAdjudication_Draw:\r
5018         UserAdjudicationEvent( 0 );\r
5019         break;\r
5020 \r
5021     /* [AS] Game list options dialog */\r
5022     case IDM_GameListOptions:\r
5023       GameListOptions();\r
5024       break;\r
5025 \r
5026     case IDM_NewChat:\r
5027       ChatPopUp(NULL);\r
5028       break;\r
5029 \r
5030     case IDM_CopyPosition:\r
5031       CopyFENToClipboard();\r
5032       break;\r
5033 \r
5034     case IDM_PastePosition:\r
5035       PasteFENFromClipboard();\r
5036       break;\r
5037 \r
5038     case IDM_MailMove:\r
5039       MailMoveEvent();\r
5040       break;\r
5041 \r
5042     case IDM_ReloadCMailMsg:\r
5043       Reset(TRUE, TRUE);\r
5044       ReloadCmailMsgEvent(FALSE);\r
5045       break;\r
5046 \r
5047     case IDM_Minimize:\r
5048       ShowWindow(hwnd, SW_MINIMIZE);\r
5049       break;\r
5050 \r
5051     case IDM_Exit:\r
5052       ExitEvent(0);\r
5053       break;\r
5054 \r
5055     case IDM_MachineWhite:\r
5056       MachineWhiteEvent();\r
5057       /*\r
5058        * refresh the tags dialog only if it's visible\r
5059        */\r
5060       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5061           char *tags;\r
5062           tags = PGNTags(&gameInfo);\r
5063           TagsPopUp(tags, CmailMsg());\r
5064           free(tags);\r
5065       }\r
5066       SAY("computer starts playing white");\r
5067       break;\r
5068 \r
5069     case IDM_MachineBlack:\r
5070       MachineBlackEvent();\r
5071       /*\r
5072        * refresh the tags dialog only if it's visible\r
5073        */\r
5074       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5075           char *tags;\r
5076           tags = PGNTags(&gameInfo);\r
5077           TagsPopUp(tags, CmailMsg());\r
5078           free(tags);\r
5079       }\r
5080       SAY("computer starts playing black");\r
5081       break;\r
5082 \r
5083     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5084       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5085       break;\r
5086 \r
5087     case IDM_TwoMachines:\r
5088       TwoMachinesEvent();\r
5089       /*\r
5090        * refresh the tags dialog only if it's visible\r
5091        */\r
5092       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5093           char *tags;\r
5094           tags = PGNTags(&gameInfo);\r
5095           TagsPopUp(tags, CmailMsg());\r
5096           free(tags);\r
5097       }\r
5098       SAY("computer starts playing both sides");\r
5099       break;\r
5100 \r
5101     case IDM_AnalysisMode:\r
5102       if(AnalyzeModeEvent()) {\r
5103         SAY("analyzing current position");\r
5104       }\r
5105       break;\r
5106 \r
5107     case IDM_AnalyzeFile:\r
5108       AnalyzeFileEvent();\r
5109       break;\r
5110 \r
5111     case IDM_IcsClient:\r
5112       IcsClientEvent();\r
5113       break;\r
5114 \r
5115     case IDM_EditGame:\r
5116     case IDM_EditGame2:\r
5117       EditGameEvent();\r
5118       SAY("edit game");\r
5119       break;\r
5120 \r
5121     case IDM_EditPosition:\r
5122     case IDM_EditPosition2:\r
5123       EditPositionEvent();\r
5124       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5125       break;\r
5126 \r
5127     case IDM_Training:\r
5128       TrainingEvent();\r
5129       break;\r
5130 \r
5131     case IDM_ShowGameList:\r
5132       ShowGameListProc();\r
5133       break;\r
5134 \r
5135     case IDM_EditProgs1:\r
5136       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5137       break;\r
5138 \r
5139     case IDM_LoadProg1:\r
5140      LoadEnginePopUp(hwndMain, 0);\r
5141       break;\r
5142 \r
5143     case IDM_LoadProg2:\r
5144      LoadEnginePopUp(hwndMain, 1);\r
5145       break;\r
5146 \r
5147     case IDM_EditServers:\r
5148       EditTagsPopUp(icsNames, &icsNames);\r
5149       break;\r
5150 \r
5151     case IDM_EditTags:\r
5152     case IDM_Tags:\r
5153       EditTagsProc();\r
5154       break;\r
5155 \r
5156     case IDM_EditBook:\r
5157       EditBookEvent();\r
5158       break;\r
5159 \r
5160     case IDM_EditComment:\r
5161     case IDM_Comment:\r
5162       if (commentUp && editComment) {\r
5163         CommentPopDown();\r
5164       } else {\r
5165         EditCommentEvent();\r
5166       }\r
5167       break;\r
5168 \r
5169     case IDM_Pause:\r
5170       PauseEvent();\r
5171       break;\r
5172 \r
5173     case IDM_Accept:\r
5174       AcceptEvent();\r
5175       break;\r
5176 \r
5177     case IDM_Decline:\r
5178       DeclineEvent();\r
5179       break;\r
5180 \r
5181     case IDM_Rematch:\r
5182 \r
5183       RematchEvent();\r
5184       break;\r
5185 \r
5186     case IDM_CallFlag:\r
5187       CallFlagEvent();\r
5188       break;\r
5189 \r
5190     case IDM_Draw:\r
5191       DrawEvent();\r
5192       break;\r
5193 \r
5194     case IDM_Adjourn:\r
5195       AdjournEvent();\r
5196       break;\r
5197 \r
5198     case IDM_Abort:\r
5199       AbortEvent();\r
5200       break;\r
5201 \r
5202     case IDM_Resign:\r
5203       ResignEvent();\r
5204       break;\r
5205 \r
5206     case IDM_StopObserving:\r
5207       StopObservingEvent();\r
5208       break;\r
5209 \r
5210     case IDM_StopExamining:\r
5211       StopExaminingEvent();\r
5212       break;\r
5213 \r
5214     case IDM_Upload:\r
5215       UploadGameEvent();\r
5216       break;\r
5217 \r
5218     case IDM_TypeInMove:\r
5219       TypeInEvent('\000');\r
5220       break;\r
5221 \r
5222     case IDM_TypeInName:\r
5223       PopUpNameDialog('\000');\r
5224       break;\r
5225 \r
5226     case IDM_Backward:\r
5227       BackwardEvent();\r
5228       SetFocus(hwndMain);\r
5229       break;\r
5230 \r
5231     JAWS_MENU_ITEMS\r
5232 \r
5233     case IDM_Forward:\r
5234       ForwardEvent();\r
5235       SetFocus(hwndMain);\r
5236       break;\r
5237 \r
5238     case IDM_ToStart:\r
5239       ToStartEvent();\r
5240       SetFocus(hwndMain);\r
5241       break;\r
5242 \r
5243     case IDM_ToEnd:\r
5244       ToEndEvent();\r
5245       SetFocus(hwndMain);\r
5246       break;\r
5247 \r
5248     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5249     case OPT_GameListPrev:\r
5250       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5251       break;\r
5252 \r
5253     case IDM_Revert:\r
5254       RevertEvent(FALSE);\r
5255       break;\r
5256 \r
5257     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5258       RevertEvent(TRUE);\r
5259       break;\r
5260 \r
5261     case IDM_TruncateGame:\r
5262       TruncateGameEvent();\r
5263       break;\r
5264 \r
5265     case IDM_MoveNow:\r
5266       MoveNowEvent();\r
5267       break;\r
5268 \r
5269     case IDM_RetractMove:\r
5270       RetractMoveEvent();\r
5271       break;\r
5272 \r
5273     case IDM_FlipView:\r
5274       flipView = !flipView;\r
5275       DrawPosition(FALSE, NULL);\r
5276       break;\r
5277 \r
5278     case IDM_FlipClock:\r
5279       flipClock = !flipClock;\r
5280       DisplayBothClocks();\r
5281       DisplayLogos();\r
5282       break;\r
5283 \r
5284     case IDM_MuteSounds:\r
5285       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5286       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5287                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5288       break;\r
5289 \r
5290     case IDM_GeneralOptions:\r
5291       GeneralOptionsPopup(hwnd);\r
5292       DrawPosition(TRUE, NULL);\r
5293       break;\r
5294 \r
5295     case IDM_BoardOptions:\r
5296       BoardOptionsPopup(hwnd);\r
5297       break;\r
5298 \r
5299     case IDM_ThemeOptions:\r
5300       ThemeOptionsPopup(hwnd);\r
5301       break;\r
5302 \r
5303     case IDM_EnginePlayOptions:\r
5304       EnginePlayOptionsPopup(hwnd);\r
5305       break;\r
5306 \r
5307     case IDM_Engine1Options:\r
5308       EngineOptionsPopup(hwnd, &first);\r
5309       break;\r
5310 \r
5311     case IDM_Engine2Options:\r
5312       savedHwnd = hwnd;\r
5313       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5314       EngineOptionsPopup(hwnd, &second);\r
5315       break;\r
5316 \r
5317     case IDM_OptionsUCI:\r
5318       UciOptionsPopup(hwnd);\r
5319       break;\r
5320 \r
5321     case IDM_Tourney:\r
5322       TourneyPopup(hwnd);\r
5323       break;\r
5324 \r
5325     case IDM_IcsOptions:\r
5326       IcsOptionsPopup(hwnd);\r
5327       break;\r
5328 \r
5329     case IDM_Fonts:\r
5330       FontsOptionsPopup(hwnd);\r
5331       break;\r
5332 \r
5333     case IDM_Sounds:\r
5334       SoundOptionsPopup(hwnd);\r
5335       break;\r
5336 \r
5337     case IDM_CommPort:\r
5338       CommPortOptionsPopup(hwnd);\r
5339       break;\r
5340 \r
5341     case IDM_LoadOptions:\r
5342       LoadOptionsPopup(hwnd);\r
5343       break;\r
5344 \r
5345     case IDM_SaveOptions:\r
5346       SaveOptionsPopup(hwnd);\r
5347       break;\r
5348 \r
5349     case IDM_TimeControl:\r
5350       TimeControlOptionsPopup(hwnd);\r
5351       break;\r
5352 \r
5353     case IDM_SaveSettings:\r
5354       SaveSettings(settingsFileName);\r
5355       break;\r
5356 \r
5357     case IDM_SaveSettingsOnExit:\r
5358       saveSettingsOnExit = !saveSettingsOnExit;\r
5359       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5360                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5361                                          MF_CHECKED : MF_UNCHECKED));\r
5362       break;\r
5363 \r
5364     case IDM_Hint:\r
5365       HintEvent();\r
5366       break;\r
5367 \r
5368     case IDM_Book:\r
5369       BookEvent();\r
5370       break;\r
5371 \r
5372     case IDM_AboutGame:\r
5373       AboutGameEvent();\r
5374       break;\r
5375 \r
5376     case IDM_Debug:\r
5377       appData.debugMode = !appData.debugMode;\r
5378       if (appData.debugMode) {\r
5379         char dir[MSG_SIZ];\r
5380         GetCurrentDirectory(MSG_SIZ, dir);\r
5381         SetCurrentDirectory(installDir);\r
5382         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5383         SetCurrentDirectory(dir);\r
5384         setbuf(debugFP, NULL);\r
5385       } else {\r
5386         fclose(debugFP);\r
5387         debugFP = NULL;\r
5388       }\r
5389       break;\r
5390 \r
5391     case IDM_HELPCONTENTS:\r
5392       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5393           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5394           MessageBox (GetFocus(),\r
5395                     _("Unable to activate help"),\r
5396                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5397       }\r
5398       break;\r
5399 \r
5400     case IDM_HELPSEARCH:\r
5401         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5402             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5403         MessageBox (GetFocus(),\r
5404                     _("Unable to activate help"),\r
5405                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5406       }\r
5407       break;\r
5408 \r
5409     case IDM_HELPHELP:\r
5410       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5411         MessageBox (GetFocus(),\r
5412                     _("Unable to activate help"),\r
5413                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5414       }\r
5415       break;\r
5416 \r
5417     case IDM_ABOUT:\r
5418       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5419       DialogBox(hInst, \r
5420         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5421         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5422       FreeProcInstance(lpProc);\r
5423       break;\r
5424 \r
5425     case IDM_DirectCommand1:\r
5426       AskQuestionEvent(_("Direct Command"),\r
5427                        _("Send to chess program:"), "", "1");\r
5428       break;\r
5429     case IDM_DirectCommand2:\r
5430       AskQuestionEvent(_("Direct Command"),\r
5431                        _("Send to second chess program:"), "", "2");\r
5432       break;\r
5433 \r
5434     case EP_WhitePawn:\r
5435       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5436       fromX = fromY = -1;\r
5437       break;\r
5438 \r
5439     case EP_WhiteKnight:\r
5440       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5441       fromX = fromY = -1;\r
5442       break;\r
5443 \r
5444     case EP_WhiteBishop:\r
5445       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5446       fromX = fromY = -1;\r
5447       break;\r
5448 \r
5449     case EP_WhiteRook:\r
5450       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5451       fromX = fromY = -1;\r
5452       break;\r
5453 \r
5454     case EP_WhiteQueen:\r
5455       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5456       fromX = fromY = -1;\r
5457       break;\r
5458 \r
5459     case EP_WhiteFerz:\r
5460       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5461       fromX = fromY = -1;\r
5462       break;\r
5463 \r
5464     case EP_WhiteWazir:\r
5465       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5466       fromX = fromY = -1;\r
5467       break;\r
5468 \r
5469     case EP_WhiteAlfil:\r
5470       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5471       fromX = fromY = -1;\r
5472       break;\r
5473 \r
5474     case EP_WhiteCannon:\r
5475       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5476       fromX = fromY = -1;\r
5477       break;\r
5478 \r
5479     case EP_WhiteCardinal:\r
5480       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5481       fromX = fromY = -1;\r
5482       break;\r
5483 \r
5484     case EP_WhiteMarshall:\r
5485       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5486       fromX = fromY = -1;\r
5487       break;\r
5488 \r
5489     case EP_WhiteKing:\r
5490       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5491       fromX = fromY = -1;\r
5492       break;\r
5493 \r
5494     case EP_BlackPawn:\r
5495       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5496       fromX = fromY = -1;\r
5497       break;\r
5498 \r
5499     case EP_BlackKnight:\r
5500       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5501       fromX = fromY = -1;\r
5502       break;\r
5503 \r
5504     case EP_BlackBishop:\r
5505       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5506       fromX = fromY = -1;\r
5507       break;\r
5508 \r
5509     case EP_BlackRook:\r
5510       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5511       fromX = fromY = -1;\r
5512       break;\r
5513 \r
5514     case EP_BlackQueen:\r
5515       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5516       fromX = fromY = -1;\r
5517       break;\r
5518 \r
5519     case EP_BlackFerz:\r
5520       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5521       fromX = fromY = -1;\r
5522       break;\r
5523 \r
5524     case EP_BlackWazir:\r
5525       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5526       fromX = fromY = -1;\r
5527       break;\r
5528 \r
5529     case EP_BlackAlfil:\r
5530       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5531       fromX = fromY = -1;\r
5532       break;\r
5533 \r
5534     case EP_BlackCannon:\r
5535       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5536       fromX = fromY = -1;\r
5537       break;\r
5538 \r
5539     case EP_BlackCardinal:\r
5540       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5541       fromX = fromY = -1;\r
5542       break;\r
5543 \r
5544     case EP_BlackMarshall:\r
5545       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5546       fromX = fromY = -1;\r
5547       break;\r
5548 \r
5549     case EP_BlackKing:\r
5550       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5551       fromX = fromY = -1;\r
5552       break;\r
5553 \r
5554     case EP_EmptySquare:\r
5555       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5556       fromX = fromY = -1;\r
5557       break;\r
5558 \r
5559     case EP_ClearBoard:\r
5560       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5561       fromX = fromY = -1;\r
5562       break;\r
5563 \r
5564     case EP_White:\r
5565       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5566       fromX = fromY = -1;\r
5567       break;\r
5568 \r
5569     case EP_Black:\r
5570       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5571       fromX = fromY = -1;\r
5572       break;\r
5573 \r
5574     case EP_Promote:\r
5575       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5576       fromX = fromY = -1;\r
5577       break;\r
5578 \r
5579     case EP_Demote:\r
5580       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5581       fromX = fromY = -1;\r
5582       break;\r
5583 \r
5584     case DP_Pawn:\r
5585       DropMenuEvent(WhitePawn, fromX, fromY);\r
5586       fromX = fromY = -1;\r
5587       break;\r
5588 \r
5589     case DP_Knight:\r
5590       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5591       fromX = fromY = -1;\r
5592       break;\r
5593 \r
5594     case DP_Bishop:\r
5595       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5596       fromX = fromY = -1;\r
5597       break;\r
5598 \r
5599     case DP_Rook:\r
5600       DropMenuEvent(WhiteRook, fromX, fromY);\r
5601       fromX = fromY = -1;\r
5602       break;\r
5603 \r
5604     case DP_Queen:\r
5605       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5606       fromX = fromY = -1;\r
5607       break;\r
5608 \r
5609     case IDM_English:\r
5610       barbaric = 0; appData.language = "";\r
5611       TranslateMenus(0);\r
5612       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5613       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5614       lastChecked = wmId;\r
5615       break;\r
5616 \r
5617     default:\r
5618       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5619           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5620       else\r
5621       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5622           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5623           TranslateMenus(0);\r
5624           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5625           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5626           lastChecked = wmId;\r
5627           break;\r
5628       }\r
5629       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5630     }\r
5631     break;\r
5632 \r
5633   case WM_TIMER:\r
5634     switch (wParam) {\r
5635     case CLOCK_TIMER_ID:\r
5636       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5637       clockTimerEvent = 0;\r
5638       DecrementClocks(); /* call into back end */\r
5639       break;\r
5640     case LOAD_GAME_TIMER_ID:\r
5641       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5642       loadGameTimerEvent = 0;\r
5643       AutoPlayGameLoop(); /* call into back end */\r
5644       break;\r
5645     case ANALYSIS_TIMER_ID:\r
5646       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5647                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5648         AnalysisPeriodicEvent(0);\r
5649       } else {\r
5650         KillTimer(hwnd, analysisTimerEvent);\r
5651         analysisTimerEvent = 0;\r
5652       }\r
5653       break;\r
5654     case DELAYED_TIMER_ID:\r
5655       KillTimer(hwnd, delayedTimerEvent);\r
5656       delayedTimerEvent = 0;\r
5657       delayedTimerCallback();\r
5658       break;\r
5659     }\r
5660     break;\r
5661 \r
5662   case WM_USER_Input:\r
5663     InputEvent(hwnd, message, wParam, lParam);\r
5664     break;\r
5665 \r
5666   /* [AS] Also move "attached" child windows */\r
5667   case WM_WINDOWPOSCHANGING:\r
5668 \r
5669     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5670         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5671 \r
5672         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5673             /* Window is moving */\r
5674             RECT rcMain;\r
5675 \r
5676 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5677             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5678             rcMain.right  = wpMain.x + wpMain.width;\r
5679             rcMain.top    = wpMain.y;\r
5680             rcMain.bottom = wpMain.y + wpMain.height;\r
5681             \r
5682             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5683             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5684             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5685             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5686             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5687             wpMain.x = lpwp->x;\r
5688             wpMain.y = lpwp->y;\r
5689         }\r
5690     }\r
5691     break;\r
5692 \r
5693   /* [AS] Snapping */\r
5694   case WM_ENTERSIZEMOVE:\r
5695     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5696     if (hwnd == hwndMain) {\r
5697       doingSizing = TRUE;\r
5698       lastSizing = 0;\r
5699     }\r
5700     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5701     break;\r
5702 \r
5703   case WM_SIZING:\r
5704     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5705     if (hwnd == hwndMain) {\r
5706       lastSizing = wParam;\r
5707     }\r
5708     break;\r
5709 \r
5710   case WM_MOVING:\r
5711     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5712       return OnMoving( &sd, hwnd, wParam, lParam );\r
5713 \r
5714   case WM_EXITSIZEMOVE:\r
5715     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5716     if (hwnd == hwndMain) {\r
5717       RECT client;\r
5718       doingSizing = FALSE;\r
5719       InvalidateRect(hwnd, &boardRect, FALSE);\r
5720       GetClientRect(hwnd, &client);\r
5721       ResizeBoard(client.right, client.bottom, lastSizing);\r
5722       lastSizing = 0;\r
5723       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5724     }\r
5725     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5726     break;\r
5727 \r
5728   case WM_DESTROY: /* message: window being destroyed */\r
5729     PostQuitMessage(0);\r
5730     break;\r
5731 \r
5732   case WM_CLOSE:\r
5733     if (hwnd == hwndMain) {\r
5734       ExitEvent(0);\r
5735     }\r
5736     break;\r
5737 \r
5738   default:      /* Passes it on if unprocessed */\r
5739     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5740   }\r
5741 \r
5742   return 0;\r
5743 }\r
5744 \r
5745 /*---------------------------------------------------------------------------*\\r
5746  *\r
5747  * Misc utility routines\r
5748  *\r
5749 \*---------------------------------------------------------------------------*/\r
5750 \r
5751 /*\r
5752  * Decent random number generator, at least not as bad as Windows\r
5753  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5754  */\r
5755 unsigned int randstate;\r
5756 \r
5757 int\r
5758 myrandom(void)\r
5759 {\r
5760   randstate = randstate * 1664525 + 1013904223;\r
5761   return (int) randstate & 0x7fffffff;\r
5762 }\r
5763 \r
5764 void\r
5765 mysrandom(unsigned int seed)\r
5766 {\r
5767   randstate = seed;\r
5768 }\r
5769 \r
5770 \r
5771 /* \r
5772  * returns TRUE if user selects a different color, FALSE otherwise \r
5773  */\r
5774 \r
5775 BOOL\r
5776 ChangeColor(HWND hwnd, COLORREF *which)\r
5777 {\r
5778   static BOOL firstTime = TRUE;\r
5779   static DWORD customColors[16];\r
5780   CHOOSECOLOR cc;\r
5781   COLORREF newcolor;\r
5782   int i;\r
5783   ColorClass ccl;\r
5784 \r
5785   if (firstTime) {\r
5786     /* Make initial colors in use available as custom colors */\r
5787     /* Should we put the compiled-in defaults here instead? */\r
5788     i = 0;\r
5789     customColors[i++] = lightSquareColor & 0xffffff;\r
5790     customColors[i++] = darkSquareColor & 0xffffff;\r
5791     customColors[i++] = whitePieceColor & 0xffffff;\r
5792     customColors[i++] = blackPieceColor & 0xffffff;\r
5793     customColors[i++] = highlightSquareColor & 0xffffff;\r
5794     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5795 \r
5796     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5797       customColors[i++] = textAttribs[ccl].color;\r
5798     }\r
5799     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5800     firstTime = FALSE;\r
5801   }\r
5802 \r
5803   cc.lStructSize = sizeof(cc);\r
5804   cc.hwndOwner = hwnd;\r
5805   cc.hInstance = NULL;\r
5806   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5807   cc.lpCustColors = (LPDWORD) customColors;\r
5808   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5809 \r
5810   if (!ChooseColor(&cc)) return FALSE;\r
5811 \r
5812   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5813   if (newcolor == *which) return FALSE;\r
5814   *which = newcolor;\r
5815   return TRUE;\r
5816 \r
5817   /*\r
5818   InitDrawingColors();\r
5819   InvalidateRect(hwnd, &boardRect, FALSE);\r
5820   */\r
5821 }\r
5822 \r
5823 BOOLEAN\r
5824 MyLoadSound(MySound *ms)\r
5825 {\r
5826   BOOL ok = FALSE;\r
5827   struct stat st;\r
5828   FILE *f;\r
5829 \r
5830   if (ms->data && ms->flag) free(ms->data);\r
5831   ms->data = NULL;\r
5832 \r
5833   switch (ms->name[0]) {\r
5834   case NULLCHAR:\r
5835     /* Silence */\r
5836     ok = TRUE;\r
5837     break;\r
5838   case '$':\r
5839     /* System sound from Control Panel.  Don't preload here. */\r
5840     ok = TRUE;\r
5841     break;\r
5842   case '!':\r
5843     if (ms->name[1] == NULLCHAR) {\r
5844       /* "!" alone = silence */\r
5845       ok = TRUE;\r
5846     } else {\r
5847       /* Builtin wave resource.  Error if not found. */\r
5848       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5849       if (h == NULL) break;\r
5850       ms->data = (void *)LoadResource(hInst, h);\r
5851       ms->flag = 0; // not maloced, so cannot be freed!\r
5852       if (h == NULL) break;\r
5853       ok = TRUE;\r
5854     }\r
5855     break;\r
5856   default:\r
5857     /* .wav file.  Error if not found. */\r
5858     f = fopen(ms->name, "rb");\r
5859     if (f == NULL) break;\r
5860     if (fstat(fileno(f), &st) < 0) break;\r
5861     ms->data = malloc(st.st_size);\r
5862     ms->flag = 1;\r
5863     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5864     fclose(f);\r
5865     ok = TRUE;\r
5866     break;\r
5867   }\r
5868   if (!ok) {\r
5869     char buf[MSG_SIZ];\r
5870       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5871     DisplayError(buf, GetLastError());\r
5872   }\r
5873   return ok;\r
5874 }\r
5875 \r
5876 BOOLEAN\r
5877 MyPlaySound(MySound *ms)\r
5878 {\r
5879   BOOLEAN ok = FALSE;\r
5880 \r
5881   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5882   switch (ms->name[0]) {\r
5883   case NULLCHAR:\r
5884         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5885     /* Silence */\r
5886     ok = TRUE;\r
5887     break;\r
5888   case '$':\r
5889     /* System sound from Control Panel (deprecated feature).\r
5890        "$" alone or an unset sound name gets default beep (still in use). */\r
5891     if (ms->name[1]) {\r
5892       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5893     }\r
5894     if (!ok) ok = MessageBeep(MB_OK);\r
5895     break; \r
5896   case '!':\r
5897     /* Builtin wave resource, or "!" alone for silence */\r
5898     if (ms->name[1]) {\r
5899       if (ms->data == NULL) return FALSE;\r
5900       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5901     } else {\r
5902       ok = TRUE;\r
5903     }\r
5904     break;\r
5905   default:\r
5906     /* .wav file.  Error if not found. */\r
5907     if (ms->data == NULL) return FALSE;\r
5908     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5909     break;\r
5910   }\r
5911   /* Don't print an error: this can happen innocently if the sound driver\r
5912      is busy; for instance, if another instance of WinBoard is playing\r
5913      a sound at about the same time. */\r
5914   return ok;\r
5915 }\r
5916 \r
5917 \r
5918 LRESULT CALLBACK\r
5919 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5920 {\r
5921   BOOL ok;\r
5922   OPENFILENAME *ofn;\r
5923   static UINT *number; /* gross that this is static */\r
5924 \r
5925   switch (message) {\r
5926   case WM_INITDIALOG: /* message: initialize dialog box */\r
5927     /* Center the dialog over the application window */\r
5928     ofn = (OPENFILENAME *) lParam;\r
5929     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5930       number = (UINT *) ofn->lCustData;\r
5931       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5932     } else {\r
5933       number = NULL;\r
5934     }\r
5935     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5936     Translate(hDlg, 1536);\r
5937     return FALSE;  /* Allow for further processing */\r
5938 \r
5939   case WM_COMMAND:\r
5940     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5941       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5942     }\r
5943     return FALSE;  /* Allow for further processing */\r
5944   }\r
5945   return FALSE;\r
5946 }\r
5947 \r
5948 UINT APIENTRY\r
5949 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5950 {\r
5951   static UINT *number;\r
5952   OPENFILENAME *ofname;\r
5953   OFNOTIFY *ofnot;\r
5954   switch (uiMsg) {\r
5955   case WM_INITDIALOG:\r
5956     Translate(hdlg, DLG_IndexNumber);\r
5957     ofname = (OPENFILENAME *)lParam;\r
5958     number = (UINT *)(ofname->lCustData);\r
5959     break;\r
5960   case WM_NOTIFY:\r
5961     ofnot = (OFNOTIFY *)lParam;\r
5962     if (ofnot->hdr.code == CDN_FILEOK) {\r
5963       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5964     }\r
5965     break;\r
5966   }\r
5967   return 0;\r
5968 }\r
5969 \r
5970 \r
5971 FILE *\r
5972 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5973                char *nameFilt, char *dlgTitle, UINT *number,\r
5974                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5975 {\r
5976   OPENFILENAME openFileName;\r
5977   char buf1[MSG_SIZ];\r
5978   FILE *f;\r
5979 \r
5980   if (fileName == NULL) fileName = buf1;\r
5981   if (defName == NULL) {\r
5982     safeStrCpy(fileName, "*.", 3 );\r
5983     strcat(fileName, defExt);\r
5984   } else {\r
5985     safeStrCpy(fileName, defName, MSG_SIZ );\r
5986   }\r
5987     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5988   if (number) *number = 0;\r
5989 \r
5990   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5991   openFileName.hwndOwner         = hwnd;\r
5992   openFileName.hInstance         = (HANDLE) hInst;\r
5993   openFileName.lpstrFilter       = nameFilt;\r
5994   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5995   openFileName.nMaxCustFilter    = 0L;\r
5996   openFileName.nFilterIndex      = 1L;\r
5997   openFileName.lpstrFile         = fileName;\r
5998   openFileName.nMaxFile          = MSG_SIZ;\r
5999   openFileName.lpstrFileTitle    = fileTitle;\r
6000   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6001   openFileName.lpstrInitialDir   = NULL;\r
6002   openFileName.lpstrTitle        = dlgTitle;\r
6003   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6004     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6005     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6006     | (oldDialog ? 0 : OFN_EXPLORER);\r
6007   openFileName.nFileOffset       = 0;\r
6008   openFileName.nFileExtension    = 0;\r
6009   openFileName.lpstrDefExt       = defExt;\r
6010   openFileName.lCustData         = (LONG) number;\r
6011   openFileName.lpfnHook          = oldDialog ?\r
6012     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6013   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6014 \r
6015   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6016                         GetOpenFileName(&openFileName)) {\r
6017     /* open the file */\r
6018     f = fopen(openFileName.lpstrFile, write);\r
6019     if (f == NULL) {\r
6020       MessageBox(hwnd, _("File open failed"), NULL,\r
6021                  MB_OK|MB_ICONEXCLAMATION);\r
6022       return NULL;\r
6023     }\r
6024   } else {\r
6025     int err = CommDlgExtendedError();\r
6026     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6027     return FALSE;\r
6028   }\r
6029   return f;\r
6030 }\r
6031 \r
6032 \r
6033 \r
6034 VOID APIENTRY\r
6035 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6036 {\r
6037   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6038 \r
6039   /*\r
6040    * Get the first pop-up menu in the menu template. This is the\r
6041    * menu that TrackPopupMenu displays.\r
6042    */\r
6043   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6044   TranslateOneMenu(10, hmenuTrackPopup);\r
6045 \r
6046   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6047 \r
6048   /*\r
6049    * TrackPopup uses screen coordinates, so convert the\r
6050    * coordinates of the mouse click to screen coordinates.\r
6051    */\r
6052   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6053 \r
6054   /* Draw and track the floating pop-up menu. */\r
6055   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6056                  pt.x, pt.y, 0, hwnd, NULL);\r
6057 \r
6058   /* Destroy the menu.*/\r
6059   DestroyMenu(hmenu);\r
6060 }\r
6061    \r
6062 typedef struct {\r
6063   HWND hDlg, hText;\r
6064   int sizeX, sizeY, newSizeX, newSizeY;\r
6065   HDWP hdwp;\r
6066 } ResizeEditPlusButtonsClosure;\r
6067 \r
6068 BOOL CALLBACK\r
6069 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6070 {\r
6071   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6072   RECT rect;\r
6073   POINT pt;\r
6074 \r
6075   if (hChild == cl->hText) return TRUE;\r
6076   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6077   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6078   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6079   ScreenToClient(cl->hDlg, &pt);\r
6080   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6081     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6082   return TRUE;\r
6083 }\r
6084 \r
6085 /* Resize a dialog that has a (rich) edit field filling most of\r
6086    the top, with a row of buttons below */\r
6087 VOID\r
6088 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6089 {\r
6090   RECT rectText;\r
6091   int newTextHeight, newTextWidth;\r
6092   ResizeEditPlusButtonsClosure cl;\r
6093   \r
6094   /*if (IsIconic(hDlg)) return;*/\r
6095   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6096   \r
6097   cl.hdwp = BeginDeferWindowPos(8);\r
6098 \r
6099   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6100   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6101   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6102   if (newTextHeight < 0) {\r
6103     newSizeY += -newTextHeight;\r
6104     newTextHeight = 0;\r
6105   }\r
6106   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6107     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6108 \r
6109   cl.hDlg = hDlg;\r
6110   cl.hText = hText;\r
6111   cl.sizeX = sizeX;\r
6112   cl.sizeY = sizeY;\r
6113   cl.newSizeX = newSizeX;\r
6114   cl.newSizeY = newSizeY;\r
6115   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6116 \r
6117   EndDeferWindowPos(cl.hdwp);\r
6118 }\r
6119 \r
6120 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6121 {\r
6122     RECT    rChild, rParent;\r
6123     int     wChild, hChild, wParent, hParent;\r
6124     int     wScreen, hScreen, xNew, yNew;\r
6125     HDC     hdc;\r
6126 \r
6127     /* Get the Height and Width of the child window */\r
6128     GetWindowRect (hwndChild, &rChild);\r
6129     wChild = rChild.right - rChild.left;\r
6130     hChild = rChild.bottom - rChild.top;\r
6131 \r
6132     /* Get the Height and Width of the parent window */\r
6133     GetWindowRect (hwndParent, &rParent);\r
6134     wParent = rParent.right - rParent.left;\r
6135     hParent = rParent.bottom - rParent.top;\r
6136 \r
6137     /* Get the display limits */\r
6138     hdc = GetDC (hwndChild);\r
6139     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6140     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6141     ReleaseDC(hwndChild, hdc);\r
6142 \r
6143     /* Calculate new X position, then adjust for screen */\r
6144     xNew = rParent.left + ((wParent - wChild) /2);\r
6145     if (xNew < 0) {\r
6146         xNew = 0;\r
6147     } else if ((xNew+wChild) > wScreen) {\r
6148         xNew = wScreen - wChild;\r
6149     }\r
6150 \r
6151     /* Calculate new Y position, then adjust for screen */\r
6152     if( mode == 0 ) {\r
6153         yNew = rParent.top  + ((hParent - hChild) /2);\r
6154     }\r
6155     else {\r
6156         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6157     }\r
6158 \r
6159     if (yNew < 0) {\r
6160         yNew = 0;\r
6161     } else if ((yNew+hChild) > hScreen) {\r
6162         yNew = hScreen - hChild;\r
6163     }\r
6164 \r
6165     /* Set it, and return */\r
6166     return SetWindowPos (hwndChild, NULL,\r
6167                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6168 }\r
6169 \r
6170 /* Center one window over another */\r
6171 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6172 {\r
6173     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6174 }\r
6175 \r
6176 /*---------------------------------------------------------------------------*\\r
6177  *\r
6178  * Startup Dialog functions\r
6179  *\r
6180 \*---------------------------------------------------------------------------*/\r
6181 void\r
6182 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6183 {\r
6184   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6185 \r
6186   while (*cd != NULL) {\r
6187     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6188     cd++;\r
6189   }\r
6190 }\r
6191 \r
6192 void\r
6193 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6194 {\r
6195   char buf1[MAX_ARG_LEN];\r
6196   int len;\r
6197 \r
6198   if (str[0] == '@') {\r
6199     FILE* f = fopen(str + 1, "r");\r
6200     if (f == NULL) {\r
6201       DisplayFatalError(str + 1, errno, 2);\r
6202       return;\r
6203     }\r
6204     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6205     fclose(f);\r
6206     buf1[len] = NULLCHAR;\r
6207     str = buf1;\r
6208   }\r
6209 \r
6210   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6211 \r
6212   for (;;) {\r
6213     char buf[MSG_SIZ];\r
6214     char *end = strchr(str, '\n');\r
6215     if (end == NULL) return;\r
6216     memcpy(buf, str, end - str);\r
6217     buf[end - str] = NULLCHAR;\r
6218     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6219     str = end + 1;\r
6220   }\r
6221 }\r
6222 \r
6223 void\r
6224 SetStartupDialogEnables(HWND hDlg)\r
6225 {\r
6226   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6227     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6228     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6229   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6230     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6231   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6232     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6233   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6234     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6235   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6236     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6237     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6238     IsDlgButtonChecked(hDlg, OPT_View));\r
6239 }\r
6240 \r
6241 char *\r
6242 QuoteForFilename(char *filename)\r
6243 {\r
6244   int dquote, space;\r
6245   dquote = strchr(filename, '"') != NULL;\r
6246   space = strchr(filename, ' ') != NULL;\r
6247   if (dquote || space) {\r
6248     if (dquote) {\r
6249       return "'";\r
6250     } else {\r
6251       return "\"";\r
6252     }\r
6253   } else {\r
6254     return "";\r
6255   }\r
6256 }\r
6257 \r
6258 VOID\r
6259 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6260 {\r
6261   char buf[MSG_SIZ];\r
6262   char *q;\r
6263 \r
6264   InitComboStringsFromOption(hwndCombo, nthnames);\r
6265   q = QuoteForFilename(nthcp);\r
6266     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6267   if (*nthdir != NULLCHAR) {\r
6268     q = QuoteForFilename(nthdir);\r
6269       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6270   }\r
6271   if (*nthcp == NULLCHAR) {\r
6272     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6273   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6274     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6275     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6276   }\r
6277 }\r
6278 \r
6279 LRESULT CALLBACK\r
6280 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6281 {\r
6282   char buf[MSG_SIZ];\r
6283   HANDLE hwndCombo;\r
6284   char *p;\r
6285 \r
6286   switch (message) {\r
6287   case WM_INITDIALOG:\r
6288     /* Center the dialog */\r
6289     CenterWindow (hDlg, GetDesktopWindow());\r
6290     Translate(hDlg, DLG_Startup);\r
6291     /* Initialize the dialog items */\r
6292     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6293                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6294                   firstChessProgramNames);\r
6295     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6296                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6297                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6298     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6299     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6300       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6301     if (*appData.icsHelper != NULLCHAR) {\r
6302       char *q = QuoteForFilename(appData.icsHelper);\r
6303       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6304     }\r
6305     if (*appData.icsHost == NULLCHAR) {\r
6306       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6307       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6308     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6309       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6310       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6311     }\r
6312 \r
6313     if (appData.icsActive) {\r
6314       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6315     }\r
6316     else if (appData.noChessProgram) {\r
6317       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6318     }\r
6319     else {\r
6320       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6321     }\r
6322 \r
6323     SetStartupDialogEnables(hDlg);\r
6324     return TRUE;\r
6325 \r
6326   case WM_COMMAND:\r
6327     switch (LOWORD(wParam)) {\r
6328     case IDOK:\r
6329       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6330         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6331         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6332         p = buf;\r
6333         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6334         ParseArgs(StringGet, &p);\r
6335         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6336         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6337         p = buf;\r
6338         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6339         ParseArgs(StringGet, &p);\r
6340         SwapEngines(singleList); // ... and then make it 'second'\r
6341 \r
6342         appData.noChessProgram = FALSE;\r
6343         appData.icsActive = FALSE;\r
6344       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6345         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6346         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6347         p = buf;\r
6348         ParseArgs(StringGet, &p);\r
6349         if (appData.zippyPlay) {\r
6350           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6351           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6352           p = buf;\r
6353           ParseArgs(StringGet, &p);\r
6354         }\r
6355       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6356         appData.noChessProgram = TRUE;\r
6357         appData.icsActive = FALSE;\r
6358       } else {\r
6359         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6360                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6361         return TRUE;\r
6362       }\r
6363       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6364         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6365         p = buf;\r
6366         ParseArgs(StringGet, &p);\r
6367       }\r
6368       EndDialog(hDlg, TRUE);\r
6369       return TRUE;\r
6370 \r
6371     case IDCANCEL:\r
6372       ExitEvent(0);\r
6373       return TRUE;\r
6374 \r
6375     case IDM_HELPCONTENTS:\r
6376       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6377         MessageBox (GetFocus(),\r
6378                     _("Unable to activate help"),\r
6379                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6380       }\r
6381       break;\r
6382 \r
6383     default:\r
6384       SetStartupDialogEnables(hDlg);\r
6385       break;\r
6386     }\r
6387     break;\r
6388   }\r
6389   return FALSE;\r
6390 }\r
6391 \r
6392 /*---------------------------------------------------------------------------*\\r
6393  *\r
6394  * About box dialog functions\r
6395  *\r
6396 \*---------------------------------------------------------------------------*/\r
6397 \r
6398 /* Process messages for "About" dialog box */\r
6399 LRESULT CALLBACK\r
6400 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6401 {\r
6402   switch (message) {\r
6403   case WM_INITDIALOG: /* message: initialize dialog box */\r
6404     /* Center the dialog over the application window */\r
6405     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6406     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6407     Translate(hDlg, ABOUTBOX);\r
6408     JAWS_COPYRIGHT\r
6409     return (TRUE);\r
6410 \r
6411   case WM_COMMAND: /* message: received a command */\r
6412     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6413         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6414       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6415       return (TRUE);\r
6416     }\r
6417     break;\r
6418   }\r
6419   return (FALSE);\r
6420 }\r
6421 \r
6422 /*---------------------------------------------------------------------------*\\r
6423  *\r
6424  * Comment Dialog functions\r
6425  *\r
6426 \*---------------------------------------------------------------------------*/\r
6427 \r
6428 LRESULT CALLBACK\r
6429 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6430 {\r
6431   static HANDLE hwndText = NULL;\r
6432   int len, newSizeX, newSizeY;\r
6433   static int sizeX, sizeY;\r
6434   char *str;\r
6435   RECT rect;\r
6436   MINMAXINFO *mmi;\r
6437 \r
6438   switch (message) {\r
6439   case WM_INITDIALOG: /* message: initialize dialog box */\r
6440     /* Initialize the dialog items */\r
6441     Translate(hDlg, DLG_EditComment);\r
6442     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6443     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6444     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6445     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6446     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6447     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6448     SetWindowText(hDlg, commentTitle);\r
6449     if (editComment) {\r
6450       SetFocus(hwndText);\r
6451     } else {\r
6452       SetFocus(GetDlgItem(hDlg, IDOK));\r
6453     }\r
6454     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6455                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6456                 MAKELPARAM(FALSE, 0));\r
6457     /* Size and position the dialog */\r
6458     if (!commentDialog) {\r
6459       commentDialog = hDlg;\r
6460       GetClientRect(hDlg, &rect);\r
6461       sizeX = rect.right;\r
6462       sizeY = rect.bottom;\r
6463       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6464           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6465         WINDOWPLACEMENT wp;\r
6466         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6467         wp.length = sizeof(WINDOWPLACEMENT);\r
6468         wp.flags = 0;\r
6469         wp.showCmd = SW_SHOW;\r
6470         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6471         wp.rcNormalPosition.left = wpComment.x;\r
6472         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6473         wp.rcNormalPosition.top = wpComment.y;\r
6474         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6475         SetWindowPlacement(hDlg, &wp);\r
6476 \r
6477         GetClientRect(hDlg, &rect);\r
6478         newSizeX = rect.right;\r
6479         newSizeY = rect.bottom;\r
6480         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6481                               newSizeX, newSizeY);\r
6482         sizeX = newSizeX;\r
6483         sizeY = newSizeY;\r
6484       }\r
6485     }\r
6486     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6487     return FALSE;\r
6488 \r
6489   case WM_COMMAND: /* message: received a command */\r
6490     switch (LOWORD(wParam)) {\r
6491     case IDOK:\r
6492       if (editComment) {\r
6493         char *p, *q;\r
6494         /* Read changed options from the dialog box */\r
6495         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6496         len = GetWindowTextLength(hwndText);\r
6497         str = (char *) malloc(len + 1);\r
6498         GetWindowText(hwndText, str, len + 1);\r
6499         p = q = str;\r
6500         while (*q) {\r
6501           if (*q == '\r')\r
6502             q++;\r
6503           else\r
6504             *p++ = *q++;\r
6505         }\r
6506         *p = NULLCHAR;\r
6507         ReplaceComment(commentIndex, str);\r
6508         free(str);\r
6509       }\r
6510       CommentPopDown();\r
6511       return TRUE;\r
6512 \r
6513     case IDCANCEL:\r
6514     case OPT_CancelComment:\r
6515       CommentPopDown();\r
6516       return TRUE;\r
6517 \r
6518     case OPT_ClearComment:\r
6519       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6520       break;\r
6521 \r
6522     case OPT_EditComment:\r
6523       EditCommentEvent();\r
6524       return TRUE;\r
6525 \r
6526     default:\r
6527       break;\r
6528     }\r
6529     break;\r
6530 \r
6531   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6532         if( wParam == OPT_CommentText ) {\r
6533             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6534 \r
6535             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6536                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6537                 POINTL pt;\r
6538                 LRESULT index;\r
6539 \r
6540                 pt.x = LOWORD( lpMF->lParam );\r
6541                 pt.y = HIWORD( lpMF->lParam );\r
6542 \r
6543                 if(lpMF->msg == WM_CHAR) {\r
6544                         CHARRANGE sel;\r
6545                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6546                         index = sel.cpMin;\r
6547                 } else\r
6548                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6549 \r
6550                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6551                 len = GetWindowTextLength(hwndText);\r
6552                 str = (char *) malloc(len + 1);\r
6553                 GetWindowText(hwndText, str, len + 1);\r
6554                 ReplaceComment(commentIndex, str);\r
6555                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6556                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6557                 free(str);\r
6558 \r
6559                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6560                 lpMF->msg = WM_USER;\r
6561 \r
6562                 return TRUE;\r
6563             }\r
6564         }\r
6565         break;\r
6566 \r
6567   case WM_SIZE:\r
6568     newSizeX = LOWORD(lParam);\r
6569     newSizeY = HIWORD(lParam);\r
6570     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6571     sizeX = newSizeX;\r
6572     sizeY = newSizeY;\r
6573     break;\r
6574 \r
6575   case WM_GETMINMAXINFO:\r
6576     /* Prevent resizing window too small */\r
6577     mmi = (MINMAXINFO *) lParam;\r
6578     mmi->ptMinTrackSize.x = 100;\r
6579     mmi->ptMinTrackSize.y = 100;\r
6580     break;\r
6581   }\r
6582   return FALSE;\r
6583 }\r
6584 \r
6585 VOID\r
6586 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6587 {\r
6588   FARPROC lpProc;\r
6589   char *p, *q;\r
6590 \r
6591   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6592 \r
6593   if (str == NULL) str = "";\r
6594   p = (char *) malloc(2 * strlen(str) + 2);\r
6595   q = p;\r
6596   while (*str) {\r
6597     if (*str == '\n') *q++ = '\r';\r
6598     *q++ = *str++;\r
6599   }\r
6600   *q = NULLCHAR;\r
6601   if (commentText != NULL) free(commentText);\r
6602 \r
6603   commentIndex = index;\r
6604   commentTitle = title;\r
6605   commentText = p;\r
6606   editComment = edit;\r
6607 \r
6608   if (commentDialog) {\r
6609     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6610     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6611   } else {\r
6612     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6613     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6614                  hwndMain, (DLGPROC)lpProc);\r
6615     FreeProcInstance(lpProc);\r
6616   }\r
6617   commentUp = TRUE;\r
6618 }\r
6619 \r
6620 \r
6621 /*---------------------------------------------------------------------------*\\r
6622  *\r
6623  * Type-in move dialog functions\r
6624  * \r
6625 \*---------------------------------------------------------------------------*/\r
6626 \r
6627 LRESULT CALLBACK\r
6628 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6629 {\r
6630   char move[MSG_SIZ];\r
6631   HWND hInput;\r
6632 \r
6633   switch (message) {\r
6634   case WM_INITDIALOG:\r
6635     move[0] = (char) lParam;\r
6636     move[1] = NULLCHAR;\r
6637     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6638     Translate(hDlg, DLG_TypeInMove);\r
6639     hInput = GetDlgItem(hDlg, OPT_Move);\r
6640     SetWindowText(hInput, move);\r
6641     SetFocus(hInput);\r
6642     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6643     return FALSE;\r
6644 \r
6645   case WM_COMMAND:\r
6646     switch (LOWORD(wParam)) {\r
6647     case IDOK:\r
6648 \r
6649       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6650       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6651       TypeInDoneEvent(move);\r
6652       EndDialog(hDlg, TRUE);\r
6653       return TRUE;\r
6654     case IDCANCEL:\r
6655       EndDialog(hDlg, FALSE);\r
6656       return TRUE;\r
6657     default:\r
6658       break;\r
6659     }\r
6660     break;\r
6661   }\r
6662   return FALSE;\r
6663 }\r
6664 \r
6665 VOID\r
6666 PopUpMoveDialog(char firstchar)\r
6667 {\r
6668     FARPROC lpProc;\r
6669 \r
6670       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6671       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6672         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6673       FreeProcInstance(lpProc);\r
6674 }\r
6675 \r
6676 /*---------------------------------------------------------------------------*\\r
6677  *\r
6678  * Type-in name dialog functions\r
6679  * \r
6680 \*---------------------------------------------------------------------------*/\r
6681 \r
6682 LRESULT CALLBACK\r
6683 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6684 {\r
6685   char move[MSG_SIZ];\r
6686   HWND hInput;\r
6687 \r
6688   switch (message) {\r
6689   case WM_INITDIALOG:\r
6690     move[0] = (char) lParam;\r
6691     move[1] = NULLCHAR;\r
6692     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6693     Translate(hDlg, DLG_TypeInName);\r
6694     hInput = GetDlgItem(hDlg, OPT_Name);\r
6695     SetWindowText(hInput, move);\r
6696     SetFocus(hInput);\r
6697     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6698     return FALSE;\r
6699 \r
6700   case WM_COMMAND:\r
6701     switch (LOWORD(wParam)) {\r
6702     case IDOK:\r
6703       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6704       appData.userName = strdup(move);\r
6705       SetUserLogo();\r
6706       SetGameInfo();\r
6707       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6708         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6709         DisplayTitle(move);\r
6710       }\r
6711 \r
6712 \r
6713       EndDialog(hDlg, TRUE);\r
6714       return TRUE;\r
6715     case IDCANCEL:\r
6716       EndDialog(hDlg, FALSE);\r
6717       return TRUE;\r
6718     default:\r
6719       break;\r
6720     }\r
6721     break;\r
6722   }\r
6723   return FALSE;\r
6724 }\r
6725 \r
6726 VOID\r
6727 PopUpNameDialog(char firstchar)\r
6728 {\r
6729     FARPROC lpProc;\r
6730     \r
6731       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6732       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6733         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6734       FreeProcInstance(lpProc);\r
6735 }\r
6736 \r
6737 /*---------------------------------------------------------------------------*\\r
6738  *\r
6739  *  Error dialogs\r
6740  * \r
6741 \*---------------------------------------------------------------------------*/\r
6742 \r
6743 /* Nonmodal error box */\r
6744 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6745                              WPARAM wParam, LPARAM lParam);\r
6746 \r
6747 VOID\r
6748 ErrorPopUp(char *title, char *content)\r
6749 {\r
6750   FARPROC lpProc;\r
6751   char *p, *q;\r
6752   BOOLEAN modal = hwndMain == NULL;\r
6753 \r
6754   p = content;\r
6755   q = errorMessage;\r
6756   while (*p) {\r
6757     if (*p == '\n') {\r
6758       if (modal) {\r
6759         *q++ = ' ';\r
6760         p++;\r
6761       } else {\r
6762         *q++ = '\r';\r
6763         *q++ = *p++;\r
6764       }\r
6765     } else {\r
6766       *q++ = *p++;\r
6767     }\r
6768   }\r
6769   *q = NULLCHAR;\r
6770   strncpy(errorTitle, title, sizeof(errorTitle));\r
6771   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6772   \r
6773   if (modal) {\r
6774     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6775   } else {\r
6776     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6777     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6778                  hwndMain, (DLGPROC)lpProc);\r
6779     FreeProcInstance(lpProc);\r
6780   }\r
6781 }\r
6782 \r
6783 VOID\r
6784 ErrorPopDown()\r
6785 {\r
6786   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6787   if (errorDialog == NULL) return;\r
6788   DestroyWindow(errorDialog);\r
6789   errorDialog = NULL;\r
6790   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6791 }\r
6792 \r
6793 LRESULT CALLBACK\r
6794 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6795 {\r
6796   RECT rChild;\r
6797 \r
6798   switch (message) {\r
6799   case WM_INITDIALOG:\r
6800     GetWindowRect(hDlg, &rChild);\r
6801 \r
6802     /*\r
6803     SetWindowPos(hDlg, NULL, rChild.left,\r
6804       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6805       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6806     */\r
6807 \r
6808     /* \r
6809         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6810         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6811         and it doesn't work when you resize the dialog.\r
6812         For now, just give it a default position.\r
6813     */\r
6814     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6815     Translate(hDlg, DLG_Error);\r
6816 \r
6817     errorDialog = hDlg;\r
6818     SetWindowText(hDlg, errorTitle);\r
6819     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6820     return FALSE;\r
6821 \r
6822   case WM_COMMAND:\r
6823     switch (LOWORD(wParam)) {\r
6824     case IDOK:\r
6825     case IDCANCEL:\r
6826       if (errorDialog == hDlg) errorDialog = NULL;\r
6827       DestroyWindow(hDlg);\r
6828       return TRUE;\r
6829 \r
6830     default:\r
6831       break;\r
6832     }\r
6833     break;\r
6834   }\r
6835   return FALSE;\r
6836 }\r
6837 \r
6838 #ifdef GOTHIC\r
6839 HWND gothicDialog = NULL;\r
6840 \r
6841 LRESULT CALLBACK\r
6842 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6843 {\r
6844   RECT rChild;\r
6845   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6846 \r
6847   switch (message) {\r
6848   case WM_INITDIALOG:\r
6849     GetWindowRect(hDlg, &rChild);\r
6850 \r
6851     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6852                                                              SWP_NOZORDER);\r
6853 \r
6854     /* \r
6855         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6856         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6857         and it doesn't work when you resize the dialog.\r
6858         For now, just give it a default position.\r
6859     */\r
6860     gothicDialog = hDlg;\r
6861     SetWindowText(hDlg, errorTitle);\r
6862     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6863     return FALSE;\r
6864 \r
6865   case WM_COMMAND:\r
6866     switch (LOWORD(wParam)) {\r
6867     case IDOK:\r
6868     case IDCANCEL:\r
6869       if (errorDialog == hDlg) errorDialog = NULL;\r
6870       DestroyWindow(hDlg);\r
6871       return TRUE;\r
6872 \r
6873     default:\r
6874       break;\r
6875     }\r
6876     break;\r
6877   }\r
6878   return FALSE;\r
6879 }\r
6880 \r
6881 VOID\r
6882 GothicPopUp(char *title, VariantClass variant)\r
6883 {\r
6884   FARPROC lpProc;\r
6885   static char *lastTitle;\r
6886 \r
6887   strncpy(errorTitle, title, sizeof(errorTitle));\r
6888   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6889 \r
6890   if(lastTitle != title && gothicDialog != NULL) {\r
6891     DestroyWindow(gothicDialog);\r
6892     gothicDialog = NULL;\r
6893   }\r
6894   if(variant != VariantNormal && gothicDialog == NULL) {\r
6895     title = lastTitle;\r
6896     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6897     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6898                  hwndMain, (DLGPROC)lpProc);\r
6899     FreeProcInstance(lpProc);\r
6900   }\r
6901 }\r
6902 #endif\r
6903 \r
6904 /*---------------------------------------------------------------------------*\\r
6905  *\r
6906  *  Ics Interaction console functions\r
6907  *\r
6908 \*---------------------------------------------------------------------------*/\r
6909 \r
6910 #define HISTORY_SIZE 64\r
6911 static char *history[HISTORY_SIZE];\r
6912 int histIn = 0, histP = 0;\r
6913 \r
6914 \r
6915 VOID\r
6916 SaveInHistory(char *cmd)\r
6917 {\r
6918   if (history[histIn] != NULL) {\r
6919     free(history[histIn]);\r
6920     history[histIn] = NULL;\r
6921   }\r
6922   if (*cmd == NULLCHAR) return;\r
6923   history[histIn] = StrSave(cmd);\r
6924   histIn = (histIn + 1) % HISTORY_SIZE;\r
6925   if (history[histIn] != NULL) {\r
6926     free(history[histIn]);\r
6927 \r
6928     history[histIn] = NULL;\r
6929   }\r
6930   histP = histIn;\r
6931 }\r
6932 \r
6933 char *\r
6934 PrevInHistory(char *cmd)\r
6935 {\r
6936   int newhp;\r
6937   if (histP == histIn) {\r
6938     if (history[histIn] != NULL) free(history[histIn]);\r
6939     history[histIn] = StrSave(cmd);\r
6940   }\r
6941   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6942   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6943   histP = newhp;\r
6944   return history[histP];\r
6945 }\r
6946 \r
6947 char *\r
6948 NextInHistory()\r
6949 {\r
6950   if (histP == histIn) return NULL;\r
6951   histP = (histP + 1) % HISTORY_SIZE;\r
6952   return history[histP];   \r
6953 }\r
6954 \r
6955 HMENU\r
6956 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6957 {\r
6958   HMENU hmenu, h;\r
6959   int i = 0;\r
6960   hmenu = LoadMenu(hInst, "TextMenu");\r
6961   h = GetSubMenu(hmenu, 0);\r
6962   while (e->item) {\r
6963     if (strcmp(e->item, "-") == 0) {\r
6964       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6965     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6966       int flags = MF_STRING, j = 0;\r
6967       if (e->item[0] == '|') {\r
6968         flags |= MF_MENUBARBREAK;\r
6969         j++;\r
6970       }\r
6971       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6972       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6973     }\r
6974     e++;\r
6975     i++;\r
6976   } \r
6977   return hmenu;\r
6978 }\r
6979 \r
6980 WNDPROC consoleTextWindowProc;\r
6981 \r
6982 void\r
6983 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6984 {\r
6985   char buf[MSG_SIZ], name[MSG_SIZ];\r
6986   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6987   CHARRANGE sel;\r
6988 \r
6989   if (!getname) {\r
6990     SetWindowText(hInput, command);\r
6991     if (immediate) {\r
6992       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6993     } else {\r
6994       sel.cpMin = 999999;\r
6995       sel.cpMax = 999999;\r
6996       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6997       SetFocus(hInput);\r
6998     }\r
6999     return;\r
7000   }    \r
7001   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7002   if (sel.cpMin == sel.cpMax) {\r
7003     /* Expand to surrounding word */\r
7004     TEXTRANGE tr;\r
7005     do {\r
7006       tr.chrg.cpMax = sel.cpMin;\r
7007       tr.chrg.cpMin = --sel.cpMin;\r
7008       if (sel.cpMin < 0) break;\r
7009       tr.lpstrText = name;\r
7010       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7011     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7012     sel.cpMin++;\r
7013 \r
7014     do {\r
7015       tr.chrg.cpMin = sel.cpMax;\r
7016       tr.chrg.cpMax = ++sel.cpMax;\r
7017       tr.lpstrText = name;\r
7018       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7019     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7020     sel.cpMax--;\r
7021 \r
7022     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7023       MessageBeep(MB_ICONEXCLAMATION);\r
7024       return;\r
7025     }\r
7026     tr.chrg = sel;\r
7027     tr.lpstrText = name;\r
7028     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7029   } else {\r
7030     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7031       MessageBeep(MB_ICONEXCLAMATION);\r
7032       return;\r
7033     }\r
7034     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7035   }\r
7036   if (immediate) {\r
7037     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7038     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7039     SetWindowText(hInput, buf);\r
7040     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7041   } else {\r
7042     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7043       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7044     SetWindowText(hInput, buf);\r
7045     sel.cpMin = 999999;\r
7046     sel.cpMax = 999999;\r
7047     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7048     SetFocus(hInput);\r
7049   }\r
7050 }\r
7051 \r
7052 LRESULT CALLBACK \r
7053 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7054 {\r
7055   HWND hInput;\r
7056   CHARRANGE sel;\r
7057 \r
7058   switch (message) {\r
7059   case WM_KEYDOWN:\r
7060     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7061     if(wParam=='R') return 0;\r
7062     switch (wParam) {\r
7063     case VK_PRIOR:\r
7064       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7065       return 0;\r
7066     case VK_NEXT:\r
7067       sel.cpMin = 999999;\r
7068       sel.cpMax = 999999;\r
7069       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7070       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7071       return 0;\r
7072     }\r
7073     break;\r
7074   case WM_CHAR:\r
7075    if(wParam != '\022') {\r
7076     if (wParam == '\t') {\r
7077       if (GetKeyState(VK_SHIFT) < 0) {\r
7078         /* shifted */\r
7079         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7080         if (buttonDesc[0].hwnd) {\r
7081           SetFocus(buttonDesc[0].hwnd);\r
7082         } else {\r
7083           SetFocus(hwndMain);\r
7084         }\r
7085       } else {\r
7086         /* unshifted */\r
7087         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7088       }\r
7089     } else {\r
7090       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7091       JAWS_DELETE( SetFocus(hInput); )\r
7092       SendMessage(hInput, message, wParam, lParam);\r
7093     }\r
7094     return 0;\r
7095    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7096    lParam = -1;\r
7097   case WM_RBUTTONDOWN:\r
7098     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7099       /* Move selection here if it was empty */\r
7100       POINT pt;\r
7101       pt.x = LOWORD(lParam);\r
7102       pt.y = HIWORD(lParam);\r
7103       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7104       if (sel.cpMin == sel.cpMax) {\r
7105         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7106         sel.cpMax = sel.cpMin;\r
7107         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7108       }\r
7109       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7110 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7111       POINT pt;\r
7112       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7113       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7114       if (sel.cpMin == sel.cpMax) {\r
7115         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7116         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7117       }\r
7118       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7119         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7120       }\r
7121       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7122       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7123       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7124       MenuPopup(hwnd, pt, hmenu, -1);\r
7125 }\r
7126     }\r
7127     return 0;\r
7128   case WM_RBUTTONUP:\r
7129     if (GetKeyState(VK_SHIFT) & ~1) {\r
7130       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7131         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7132     }\r
7133     return 0;\r
7134   case WM_PASTE:\r
7135     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7136     SetFocus(hInput);\r
7137     return SendMessage(hInput, message, wParam, lParam);\r
7138   case WM_MBUTTONDOWN:\r
7139     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7140   case WM_COMMAND:\r
7141     switch (LOWORD(wParam)) {\r
7142     case IDM_QuickPaste:\r
7143       {\r
7144         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7145         if (sel.cpMin == sel.cpMax) {\r
7146           MessageBeep(MB_ICONEXCLAMATION);\r
7147           return 0;\r
7148         }\r
7149         SendMessage(hwnd, WM_COPY, 0, 0);\r
7150         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7151         SendMessage(hInput, WM_PASTE, 0, 0);\r
7152         SetFocus(hInput);\r
7153         return 0;\r
7154       }\r
7155     case IDM_Cut:\r
7156       SendMessage(hwnd, WM_CUT, 0, 0);\r
7157       return 0;\r
7158     case IDM_Paste:\r
7159       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7160       return 0;\r
7161     case IDM_Copy:\r
7162       SendMessage(hwnd, WM_COPY, 0, 0);\r
7163       return 0;\r
7164     default:\r
7165       {\r
7166         int i = LOWORD(wParam) - IDM_CommandX;\r
7167         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7168             icsTextMenuEntry[i].command != NULL) {\r
7169           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7170                    icsTextMenuEntry[i].getname,\r
7171                    icsTextMenuEntry[i].immediate);\r
7172           return 0;\r
7173         }\r
7174       }\r
7175       break;\r
7176     }\r
7177     break;\r
7178   }\r
7179   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7180 }\r
7181 \r
7182 WNDPROC consoleInputWindowProc;\r
7183 \r
7184 LRESULT CALLBACK\r
7185 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7186 {\r
7187   char buf[MSG_SIZ];\r
7188   char *p;\r
7189   static BOOL sendNextChar = FALSE;\r
7190   static BOOL quoteNextChar = FALSE;\r
7191   InputSource *is = consoleInputSource;\r
7192   CHARFORMAT cf;\r
7193   CHARRANGE sel;\r
7194 \r
7195   switch (message) {\r
7196   case WM_CHAR:\r
7197     if (!appData.localLineEditing || sendNextChar) {\r
7198       is->buf[0] = (CHAR) wParam;\r
7199       is->count = 1;\r
7200       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7201       sendNextChar = FALSE;\r
7202       return 0;\r
7203     }\r
7204     if (quoteNextChar) {\r
7205       buf[0] = (char) wParam;\r
7206       buf[1] = NULLCHAR;\r
7207       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7208       quoteNextChar = FALSE;\r
7209       return 0;\r
7210     }\r
7211     switch (wParam) {\r
7212     case '\r':   /* Enter key */\r
7213       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7214       if (consoleEcho) SaveInHistory(is->buf);\r
7215       is->buf[is->count++] = '\n';\r
7216       is->buf[is->count] = NULLCHAR;\r
7217       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7218       if (consoleEcho) {\r
7219         ConsoleOutput(is->buf, is->count, TRUE);\r
7220       } else if (appData.localLineEditing) {\r
7221         ConsoleOutput("\n", 1, TRUE);\r
7222       }\r
7223       /* fall thru */\r
7224     case '\033': /* Escape key */\r
7225       SetWindowText(hwnd, "");\r
7226       cf.cbSize = sizeof(CHARFORMAT);\r
7227       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7228       if (consoleEcho) {\r
7229         cf.crTextColor = textAttribs[ColorNormal].color;\r
7230       } else {\r
7231         cf.crTextColor = COLOR_ECHOOFF;\r
7232       }\r
7233       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7234       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7235       return 0;\r
7236     case '\t':   /* Tab key */\r
7237       if (GetKeyState(VK_SHIFT) < 0) {\r
7238         /* shifted */\r
7239         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7240       } else {\r
7241         /* unshifted */\r
7242         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7243         if (buttonDesc[0].hwnd) {\r
7244           SetFocus(buttonDesc[0].hwnd);\r
7245         } else {\r
7246           SetFocus(hwndMain);\r
7247         }\r
7248       }\r
7249       return 0;\r
7250     case '\023': /* Ctrl+S */\r
7251       sendNextChar = TRUE;\r
7252       return 0;\r
7253     case '\021': /* Ctrl+Q */\r
7254       quoteNextChar = TRUE;\r
7255       return 0;\r
7256     JAWS_REPLAY\r
7257     default:\r
7258       break;\r
7259     }\r
7260     break;\r
7261   case WM_KEYDOWN:\r
7262     switch (wParam) {\r
7263     case VK_UP:\r
7264       GetWindowText(hwnd, buf, MSG_SIZ);\r
7265       p = PrevInHistory(buf);\r
7266       if (p != NULL) {\r
7267         SetWindowText(hwnd, p);\r
7268         sel.cpMin = 999999;\r
7269         sel.cpMax = 999999;\r
7270         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7271         return 0;\r
7272       }\r
7273       break;\r
7274     case VK_DOWN:\r
7275       p = NextInHistory();\r
7276       if (p != NULL) {\r
7277         SetWindowText(hwnd, p);\r
7278         sel.cpMin = 999999;\r
7279         sel.cpMax = 999999;\r
7280         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7281         return 0;\r
7282       }\r
7283       break;\r
7284     case VK_HOME:\r
7285     case VK_END:\r
7286       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7287       /* fall thru */\r
7288     case VK_PRIOR:\r
7289     case VK_NEXT:\r
7290       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7291       return 0;\r
7292     }\r
7293     break;\r
7294   case WM_MBUTTONDOWN:\r
7295     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7296       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7297     break;\r
7298   case WM_RBUTTONUP:\r
7299     if (GetKeyState(VK_SHIFT) & ~1) {\r
7300       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7301         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7302     } else {\r
7303       POINT pt;\r
7304       HMENU hmenu;\r
7305       hmenu = LoadMenu(hInst, "InputMenu");\r
7306       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7307       if (sel.cpMin == sel.cpMax) {\r
7308         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7309         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7310       }\r
7311       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7312         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7313       }\r
7314       pt.x = LOWORD(lParam);\r
7315       pt.y = HIWORD(lParam);\r
7316       MenuPopup(hwnd, pt, hmenu, -1);\r
7317     }\r
7318     return 0;\r
7319   case WM_COMMAND:\r
7320     switch (LOWORD(wParam)) { \r
7321     case IDM_Undo:\r
7322       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7323       return 0;\r
7324     case IDM_SelectAll:\r
7325       sel.cpMin = 0;\r
7326       sel.cpMax = -1; /*999999?*/\r
7327       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7328       return 0;\r
7329     case IDM_Cut:\r
7330       SendMessage(hwnd, WM_CUT, 0, 0);\r
7331       return 0;\r
7332     case IDM_Paste:\r
7333       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7334       return 0;\r
7335     case IDM_Copy:\r
7336       SendMessage(hwnd, WM_COPY, 0, 0);\r
7337       return 0;\r
7338     }\r
7339     break;\r
7340   }\r
7341   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7342 }\r
7343 \r
7344 #define CO_MAX  100000\r
7345 #define CO_TRIM   1000\r
7346 \r
7347 LRESULT CALLBACK\r
7348 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7349 {\r
7350   static SnapData sd;\r
7351   HWND hText, hInput;\r
7352   RECT rect;\r
7353   static int sizeX, sizeY;\r
7354   int newSizeX, newSizeY;\r
7355   MINMAXINFO *mmi;\r
7356   WORD wMask;\r
7357 \r
7358   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7359   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7360 \r
7361   switch (message) {\r
7362   case WM_NOTIFY:\r
7363     if (((NMHDR*)lParam)->code == EN_LINK)\r
7364     {\r
7365       ENLINK *pLink = (ENLINK*)lParam;\r
7366       if (pLink->msg == WM_LBUTTONUP)\r
7367       {\r
7368         TEXTRANGE tr;\r
7369 \r
7370         tr.chrg = pLink->chrg;\r
7371         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7372         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7373         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7374         free(tr.lpstrText);\r
7375       }\r
7376     }\r
7377     break;\r
7378   case WM_INITDIALOG: /* message: initialize dialog box */\r
7379     hwndConsole = hDlg;\r
7380     SetFocus(hInput);\r
7381     consoleTextWindowProc = (WNDPROC)\r
7382       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7383     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7384     consoleInputWindowProc = (WNDPROC)\r
7385       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7386     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7387     Colorize(ColorNormal, TRUE);\r
7388     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7389     ChangedConsoleFont();\r
7390     GetClientRect(hDlg, &rect);\r
7391     sizeX = rect.right;\r
7392     sizeY = rect.bottom;\r
7393     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7394         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7395       WINDOWPLACEMENT wp;\r
7396       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7397       wp.length = sizeof(WINDOWPLACEMENT);\r
7398       wp.flags = 0;\r
7399       wp.showCmd = SW_SHOW;\r
7400       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7401       wp.rcNormalPosition.left = wpConsole.x;\r
7402       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7403       wp.rcNormalPosition.top = wpConsole.y;\r
7404       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7405       SetWindowPlacement(hDlg, &wp);\r
7406     }\r
7407 \r
7408    // [HGM] Chessknight's change 2004-07-13\r
7409    else { /* Determine Defaults */\r
7410        WINDOWPLACEMENT wp;\r
7411        wpConsole.x = wpMain.width + 1;\r
7412        wpConsole.y = wpMain.y;\r
7413        wpConsole.width = screenWidth -  wpMain.width;\r
7414        wpConsole.height = wpMain.height;\r
7415        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7416        wp.length = sizeof(WINDOWPLACEMENT);\r
7417        wp.flags = 0;\r
7418        wp.showCmd = SW_SHOW;\r
7419        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7420        wp.rcNormalPosition.left = wpConsole.x;\r
7421        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7422        wp.rcNormalPosition.top = wpConsole.y;\r
7423        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7424        SetWindowPlacement(hDlg, &wp);\r
7425     }\r
7426 \r
7427    // Allow hText to highlight URLs and send notifications on them\r
7428    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7429    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7430    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7431    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7432 \r
7433     return FALSE;\r
7434 \r
7435   case WM_SETFOCUS:\r
7436     SetFocus(hInput);\r
7437     return 0;\r
7438 \r
7439   case WM_CLOSE:\r
7440     ExitEvent(0);\r
7441     /* not reached */\r
7442     break;\r
7443 \r
7444   case WM_SIZE:\r
7445     if (IsIconic(hDlg)) break;\r
7446     newSizeX = LOWORD(lParam);\r
7447     newSizeY = HIWORD(lParam);\r
7448     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7449       RECT rectText, rectInput;\r
7450       POINT pt;\r
7451       int newTextHeight, newTextWidth;\r
7452       GetWindowRect(hText, &rectText);\r
7453       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7454       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7455       if (newTextHeight < 0) {\r
7456         newSizeY += -newTextHeight;\r
7457         newTextHeight = 0;\r
7458       }\r
7459       SetWindowPos(hText, NULL, 0, 0,\r
7460         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7461       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7462       pt.x = rectInput.left;\r
7463       pt.y = rectInput.top + newSizeY - sizeY;\r
7464       ScreenToClient(hDlg, &pt);\r
7465       SetWindowPos(hInput, NULL, \r
7466         pt.x, pt.y, /* needs client coords */   \r
7467         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7468         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7469     }\r
7470     sizeX = newSizeX;\r
7471     sizeY = newSizeY;\r
7472     break;\r
7473 \r
7474   case WM_GETMINMAXINFO:\r
7475     /* Prevent resizing window too small */\r
7476     mmi = (MINMAXINFO *) lParam;\r
7477     mmi->ptMinTrackSize.x = 100;\r
7478     mmi->ptMinTrackSize.y = 100;\r
7479     break;\r
7480 \r
7481   /* [AS] Snapping */\r
7482   case WM_ENTERSIZEMOVE:\r
7483     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7484 \r
7485   case WM_SIZING:\r
7486     return OnSizing( &sd, hDlg, wParam, lParam );\r
7487 \r
7488   case WM_MOVING:\r
7489     return OnMoving( &sd, hDlg, wParam, lParam );\r
7490 \r
7491   case WM_EXITSIZEMOVE:\r
7492         UpdateICSWidth(hText);\r
7493     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7494   }\r
7495 \r
7496   return DefWindowProc(hDlg, message, wParam, lParam);\r
7497 }\r
7498 \r
7499 \r
7500 VOID\r
7501 ConsoleCreate()\r
7502 {\r
7503   HWND hCons;\r
7504   if (hwndConsole) return;\r
7505   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7506   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7507 }\r
7508 \r
7509 \r
7510 VOID\r
7511 ConsoleOutput(char* data, int length, int forceVisible)\r
7512 {\r
7513   HWND hText;\r
7514   int trim, exlen;\r
7515   char *p, *q;\r
7516   char buf[CO_MAX+1];\r
7517   POINT pEnd;\r
7518   RECT rect;\r
7519   static int delayLF = 0;\r
7520   CHARRANGE savesel, sel;\r
7521 \r
7522   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7523   p = data;\r
7524   q = buf;\r
7525   if (delayLF) {\r
7526     *q++ = '\r';\r
7527     *q++ = '\n';\r
7528     delayLF = 0;\r
7529   }\r
7530   while (length--) {\r
7531     if (*p == '\n') {\r
7532       if (*++p) {\r
7533         *q++ = '\r';\r
7534         *q++ = '\n';\r
7535       } else {\r
7536         delayLF = 1;\r
7537       }\r
7538     } else if (*p == '\007') {\r
7539        MyPlaySound(&sounds[(int)SoundBell]);\r
7540        p++;\r
7541     } else {\r
7542       *q++ = *p++;\r
7543     }\r
7544   }\r
7545   *q = NULLCHAR;\r
7546   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7547   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7548   /* Save current selection */\r
7549   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7550   exlen = GetWindowTextLength(hText);\r
7551   /* Find out whether current end of text is visible */\r
7552   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7553   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7554   /* Trim existing text if it's too long */\r
7555   if (exlen + (q - buf) > CO_MAX) {\r
7556     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7557     sel.cpMin = 0;\r
7558     sel.cpMax = trim;\r
7559     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7560     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7561     exlen -= trim;\r
7562     savesel.cpMin -= trim;\r
7563     savesel.cpMax -= trim;\r
7564     if (exlen < 0) exlen = 0;\r
7565     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7566     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7567   }\r
7568   /* Append the new text */\r
7569   sel.cpMin = exlen;\r
7570   sel.cpMax = exlen;\r
7571   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7572   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7573   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7574   if (forceVisible || exlen == 0 ||\r
7575       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7576        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7577     /* Scroll to make new end of text visible if old end of text\r
7578        was visible or new text is an echo of user typein */\r
7579     sel.cpMin = 9999999;\r
7580     sel.cpMax = 9999999;\r
7581     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7582     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7583     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7584     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7585   }\r
7586   if (savesel.cpMax == exlen || forceVisible) {\r
7587     /* Move insert point to new end of text if it was at the old\r
7588        end of text or if the new text is an echo of user typein */\r
7589     sel.cpMin = 9999999;\r
7590     sel.cpMax = 9999999;\r
7591     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7592   } else {\r
7593     /* Restore previous selection */\r
7594     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7595   }\r
7596   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7597 }\r
7598 \r
7599 /*---------*/\r
7600 \r
7601 \r
7602 void\r
7603 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7604 {\r
7605   char buf[100];\r
7606   char *str;\r
7607   COLORREF oldFg, oldBg;\r
7608   HFONT oldFont;\r
7609   RECT rect;\r
7610 \r
7611   if(copyNumber > 1)\r
7612     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7613 \r
7614   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7615   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7616   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7617 \r
7618   rect.left = x;\r
7619   rect.right = x + squareSize;\r
7620   rect.top  = y;\r
7621   rect.bottom = y + squareSize;\r
7622   str = buf;\r
7623 \r
7624   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7625                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7626              y, ETO_CLIPPED|ETO_OPAQUE,\r
7627              &rect, str, strlen(str), NULL);\r
7628 \r
7629   (void) SetTextColor(hdc, oldFg);\r
7630   (void) SetBkColor(hdc, oldBg);\r
7631   (void) SelectObject(hdc, oldFont);\r
7632 }\r
7633 \r
7634 void\r
7635 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7636               RECT *rect, char *color, char *flagFell)\r
7637 {\r
7638   char buf[100];\r
7639   char *str;\r
7640   COLORREF oldFg, oldBg;\r
7641   HFONT oldFont;\r
7642 \r
7643   if (twoBoards && partnerUp) return;\r
7644   if (appData.clockMode) {\r
7645     if (tinyLayout)\r
7646       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7647     else\r
7648       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7649     str = buf;\r
7650   } else {\r
7651     str = color;\r
7652   }\r
7653 \r
7654   if (highlight) {\r
7655     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7656     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7657   } else {\r
7658     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7659     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7660   }\r
7661   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7662 \r
7663   JAWS_SILENCE\r
7664 \r
7665   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7666              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7667              rect, str, strlen(str), NULL);\r
7668   if(logoHeight > 0 && appData.clockMode) {\r
7669       RECT r;\r
7670       str += strlen(color)+2;\r
7671       r.top = rect->top + logoHeight/2;\r
7672       r.left = rect->left;\r
7673       r.right = rect->right;\r
7674       r.bottom = rect->bottom;\r
7675       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7676                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7677                  &r, str, strlen(str), NULL);\r
7678   }\r
7679   (void) SetTextColor(hdc, oldFg);\r
7680   (void) SetBkColor(hdc, oldBg);\r
7681   (void) SelectObject(hdc, oldFont);\r
7682 }\r
7683 \r
7684 \r
7685 int\r
7686 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7687            OVERLAPPED *ovl)\r
7688 {\r
7689   int ok, err;\r
7690 \r
7691   /* [AS]  */\r
7692   if( count <= 0 ) {\r
7693     if (appData.debugMode) {\r
7694       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7695     }\r
7696 \r
7697     return ERROR_INVALID_USER_BUFFER;\r
7698   }\r
7699 \r
7700   ResetEvent(ovl->hEvent);\r
7701   ovl->Offset = ovl->OffsetHigh = 0;\r
7702   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7703   if (ok) {\r
7704     err = NO_ERROR;\r
7705   } else {\r
7706     err = GetLastError();\r
7707     if (err == ERROR_IO_PENDING) {\r
7708       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7709       if (ok)\r
7710         err = NO_ERROR;\r
7711       else\r
7712         err = GetLastError();\r
7713     }\r
7714   }\r
7715   return err;\r
7716 }\r
7717 \r
7718 int\r
7719 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7720             OVERLAPPED *ovl)\r
7721 {\r
7722   int ok, err;\r
7723 \r
7724   ResetEvent(ovl->hEvent);\r
7725   ovl->Offset = ovl->OffsetHigh = 0;\r
7726   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7727   if (ok) {\r
7728     err = NO_ERROR;\r
7729   } else {\r
7730     err = GetLastError();\r
7731     if (err == ERROR_IO_PENDING) {\r
7732       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7733       if (ok)\r
7734         err = NO_ERROR;\r
7735       else\r
7736         err = GetLastError();\r
7737     }\r
7738 \r
7739   }\r
7740   return err;\r
7741 }\r
7742 \r
7743 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7744 void CheckForInputBufferFull( InputSource * is )\r
7745 {\r
7746     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7747         /* Look for end of line */\r
7748         char * p = is->buf;\r
7749         \r
7750         while( p < is->next && *p != '\n' ) {\r
7751             p++;\r
7752         }\r
7753 \r
7754         if( p >= is->next ) {\r
7755             if (appData.debugMode) {\r
7756                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7757             }\r
7758 \r
7759             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7760             is->count = (DWORD) -1;\r
7761             is->next = is->buf;\r
7762         }\r
7763     }\r
7764 }\r
7765 \r
7766 DWORD\r
7767 InputThread(LPVOID arg)\r
7768 {\r
7769   InputSource *is;\r
7770   OVERLAPPED ovl;\r
7771 \r
7772   is = (InputSource *) arg;\r
7773   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7774   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7775   while (is->hThread != NULL) {\r
7776     is->error = DoReadFile(is->hFile, is->next,\r
7777                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7778                            &is->count, &ovl);\r
7779     if (is->error == NO_ERROR) {\r
7780       is->next += is->count;\r
7781     } else {\r
7782       if (is->error == ERROR_BROKEN_PIPE) {\r
7783         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7784         is->count = 0;\r
7785       } else {\r
7786         is->count = (DWORD) -1;\r
7787         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7788         break; \r
7789       }\r
7790     }\r
7791 \r
7792     CheckForInputBufferFull( is );\r
7793 \r
7794     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7795 \r
7796     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7797 \r
7798     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7799   }\r
7800 \r
7801   CloseHandle(ovl.hEvent);\r
7802   CloseHandle(is->hFile);\r
7803 \r
7804   if (appData.debugMode) {\r
7805     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7806   }\r
7807 \r
7808   return 0;\r
7809 }\r
7810 \r
7811 \r
7812 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7813 DWORD\r
7814 NonOvlInputThread(LPVOID arg)\r
7815 {\r
7816   InputSource *is;\r
7817   char *p, *q;\r
7818   int i;\r
7819   char prev;\r
7820 \r
7821   is = (InputSource *) arg;\r
7822   while (is->hThread != NULL) {\r
7823     is->error = ReadFile(is->hFile, is->next,\r
7824                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7825                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7826     if (is->error == NO_ERROR) {\r
7827       /* Change CRLF to LF */\r
7828       if (is->next > is->buf) {\r
7829         p = is->next - 1;\r
7830         i = is->count + 1;\r
7831       } else {\r
7832         p = is->next;\r
7833         i = is->count;\r
7834       }\r
7835       q = p;\r
7836       prev = NULLCHAR;\r
7837       while (i > 0) {\r
7838         if (prev == '\r' && *p == '\n') {\r
7839           *(q-1) = '\n';\r
7840           is->count--;\r
7841         } else { \r
7842           *q++ = *p;\r
7843         }\r
7844         prev = *p++;\r
7845         i--;\r
7846       }\r
7847       *q = NULLCHAR;\r
7848       is->next = q;\r
7849     } else {\r
7850       if (is->error == ERROR_BROKEN_PIPE) {\r
7851         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7852         is->count = 0; \r
7853       } else {\r
7854         is->count = (DWORD) -1;\r
7855       }\r
7856     }\r
7857 \r
7858     CheckForInputBufferFull( is );\r
7859 \r
7860     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7861 \r
7862     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7863 \r
7864     if (is->count < 0) break;  /* Quit on error */\r
7865   }\r
7866   CloseHandle(is->hFile);\r
7867   return 0;\r
7868 }\r
7869 \r
7870 DWORD\r
7871 SocketInputThread(LPVOID arg)\r
7872 {\r
7873   InputSource *is;\r
7874 \r
7875   is = (InputSource *) arg;\r
7876   while (is->hThread != NULL) {\r
7877     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7878     if ((int)is->count == SOCKET_ERROR) {\r
7879       is->count = (DWORD) -1;\r
7880       is->error = WSAGetLastError();\r
7881     } else {\r
7882       is->error = NO_ERROR;\r
7883       is->next += is->count;\r
7884       if (is->count == 0 && is->second == is) {\r
7885         /* End of file on stderr; quit with no message */\r
7886         break;\r
7887       }\r
7888     }\r
7889     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7890 \r
7891     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7892 \r
7893     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7894   }\r
7895   return 0;\r
7896 }\r
7897 \r
7898 VOID\r
7899 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7900 {\r
7901   InputSource *is;\r
7902 \r
7903   is = (InputSource *) lParam;\r
7904   if (is->lineByLine) {\r
7905     /* Feed in lines one by one */\r
7906     char *p = is->buf;\r
7907     char *q = p;\r
7908     while (q < is->next) {\r
7909       if (*q++ == '\n') {\r
7910         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7911         p = q;\r
7912       }\r
7913     }\r
7914     \r
7915     /* Move any partial line to the start of the buffer */\r
7916     q = is->buf;\r
7917     while (p < is->next) {\r
7918       *q++ = *p++;\r
7919     }\r
7920     is->next = q;\r
7921 \r
7922     if (is->error != NO_ERROR || is->count == 0) {\r
7923       /* Notify backend of the error.  Note: If there was a partial\r
7924          line at the end, it is not flushed through. */\r
7925       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7926     }\r
7927   } else {\r
7928     /* Feed in the whole chunk of input at once */\r
7929     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7930     is->next = is->buf;\r
7931   }\r
7932 }\r
7933 \r
7934 /*---------------------------------------------------------------------------*\\r
7935  *\r
7936  *  Menu enables. Used when setting various modes.\r
7937  *\r
7938 \*---------------------------------------------------------------------------*/\r
7939 \r
7940 typedef struct {\r
7941   int item;\r
7942   int flags;\r
7943 } Enables;\r
7944 \r
7945 VOID\r
7946 GreyRevert(Boolean grey)\r
7947 { // [HGM] vari: for retracting variations in local mode\r
7948   HMENU hmenu = GetMenu(hwndMain);\r
7949   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7950   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7951 }\r
7952 \r
7953 VOID\r
7954 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7955 {\r
7956   while (enab->item > 0) {\r
7957     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7958     enab++;\r
7959   }\r
7960 }\r
7961 \r
7962 Enables gnuEnables[] = {\r
7963   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7976 \r
7977   // Needed to switch from ncp to GNU mode on Engine Load\r
7978   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7979   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7980   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7981   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7982   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7983   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7984   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7989   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7990   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7991   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7992   { -1, -1 }\r
7993 };\r
7994 \r
7995 Enables icsEnables[] = {\r
7996   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8001   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8002   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8003   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8004   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8005   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8012   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8016   { -1, -1 }\r
8017 };\r
8018 \r
8019 #if ZIPPY\r
8020 Enables zippyEnables[] = {\r
8021   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8022   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8023   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8024   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8025   { -1, -1 }\r
8026 };\r
8027 #endif\r
8028 \r
8029 Enables ncpEnables[] = {\r
8030   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8039   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8040   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8042   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8043   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8044   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8045   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8046   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8047   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8048   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8050   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8051   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8052   { -1, -1 }\r
8053 };\r
8054 \r
8055 Enables trainingOnEnables[] = {\r
8056   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8057   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8058   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8059   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8060   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8065   { -1, -1 }\r
8066 };\r
8067 \r
8068 Enables trainingOffEnables[] = {\r
8069   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8070   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8071   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8072   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8073   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8074   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8075   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8076   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8077   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8078   { -1, -1 }\r
8079 };\r
8080 \r
8081 /* These modify either ncpEnables or gnuEnables */\r
8082 Enables cmailEnables[] = {\r
8083   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8084   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8085   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8086   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8087   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8088   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8089   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8090   { -1, -1 }\r
8091 };\r
8092 \r
8093 Enables machineThinkingEnables[] = {\r
8094   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8095   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8096   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8104   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8105   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8107 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8108   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8109   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8110   { -1, -1 }\r
8111 };\r
8112 \r
8113 Enables userThinkingEnables[] = {\r
8114   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8115   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8116   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8117   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8118   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8119   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8120   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8121   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8122   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8123   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8124   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8125   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8126   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8127 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8128   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8129   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8130   { -1, -1 }\r
8131 };\r
8132 \r
8133 /*---------------------------------------------------------------------------*\\r
8134  *\r
8135  *  Front-end interface functions exported by XBoard.\r
8136  *  Functions appear in same order as prototypes in frontend.h.\r
8137  * \r
8138 \*---------------------------------------------------------------------------*/\r
8139 VOID\r
8140 CheckMark(UINT item, int state)\r
8141 {\r
8142     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8143 }\r
8144 \r
8145 VOID\r
8146 ModeHighlight()\r
8147 {\r
8148   static UINT prevChecked = 0;\r
8149   static int prevPausing = 0;\r
8150   UINT nowChecked;\r
8151 \r
8152   if (pausing != prevPausing) {\r
8153     prevPausing = pausing;\r
8154     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8155                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8156     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8157   }\r
8158 \r
8159   switch (gameMode) {\r
8160   case BeginningOfGame:\r
8161     if (appData.icsActive)\r
8162       nowChecked = IDM_IcsClient;\r
8163     else if (appData.noChessProgram)\r
8164       nowChecked = IDM_EditGame;\r
8165     else\r
8166       nowChecked = IDM_MachineBlack;\r
8167     break;\r
8168   case MachinePlaysBlack:\r
8169     nowChecked = IDM_MachineBlack;\r
8170     break;\r
8171   case MachinePlaysWhite:\r
8172     nowChecked = IDM_MachineWhite;\r
8173     break;\r
8174   case TwoMachinesPlay:\r
8175     nowChecked = IDM_TwoMachines;\r
8176     break;\r
8177   case AnalyzeMode:\r
8178     nowChecked = IDM_AnalysisMode;\r
8179     break;\r
8180   case AnalyzeFile:\r
8181     nowChecked = IDM_AnalyzeFile;\r
8182     break;\r
8183   case EditGame:\r
8184     nowChecked = IDM_EditGame;\r
8185     break;\r
8186   case PlayFromGameFile:\r
8187     nowChecked = IDM_LoadGame;\r
8188     break;\r
8189   case EditPosition:\r
8190     nowChecked = IDM_EditPosition;\r
8191     break;\r
8192   case Training:\r
8193     nowChecked = IDM_Training;\r
8194     break;\r
8195   case IcsPlayingWhite:\r
8196   case IcsPlayingBlack:\r
8197   case IcsObserving:\r
8198   case IcsIdle:\r
8199     nowChecked = IDM_IcsClient;\r
8200     break;\r
8201   default:\r
8202   case EndOfGame:\r
8203     nowChecked = 0;\r
8204     break;\r
8205   }\r
8206   CheckMark(prevChecked, MF_UNCHECKED);\r
8207   CheckMark(nowChecked, MF_CHECKED);\r
8208   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8209 \r
8210   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8211     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8212                           MF_BYCOMMAND|MF_ENABLED);\r
8213   } else {\r
8214     (void) EnableMenuItem(GetMenu(hwndMain), \r
8215                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8216   }\r
8217 \r
8218   prevChecked = nowChecked;\r
8219 \r
8220   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8221   if (appData.icsActive) {\r
8222        if (appData.icsEngineAnalyze) {\r
8223                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8224        } else {\r
8225                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8226        }\r
8227   }\r
8228   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8229 }\r
8230 \r
8231 VOID\r
8232 SetICSMode()\r
8233 {\r
8234   HMENU hmenu = GetMenu(hwndMain);\r
8235   SetMenuEnables(hmenu, icsEnables);\r
8236   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8237     MF_BYCOMMAND|MF_ENABLED);\r
8238 #if ZIPPY\r
8239   if (appData.zippyPlay) {\r
8240     SetMenuEnables(hmenu, zippyEnables);\r
8241     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8242          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8243           MF_BYCOMMAND|MF_ENABLED);\r
8244   }\r
8245 #endif\r
8246 }\r
8247 \r
8248 VOID\r
8249 SetGNUMode()\r
8250 {\r
8251   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8252 }\r
8253 \r
8254 VOID\r
8255 SetNCPMode()\r
8256 {\r
8257   HMENU hmenu = GetMenu(hwndMain);\r
8258   SetMenuEnables(hmenu, ncpEnables);\r
8259     DrawMenuBar(hwndMain);\r
8260 }\r
8261 \r
8262 VOID\r
8263 SetCmailMode()\r
8264 {\r
8265   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8266 }\r
8267 \r
8268 VOID \r
8269 SetTrainingModeOn()\r
8270 {\r
8271   int i;\r
8272   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8273   for (i = 0; i < N_BUTTONS; i++) {\r
8274     if (buttonDesc[i].hwnd != NULL)\r
8275       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8276   }\r
8277   CommentPopDown();\r
8278 }\r
8279 \r
8280 VOID SetTrainingModeOff()\r
8281 {\r
8282   int i;\r
8283   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8284   for (i = 0; i < N_BUTTONS; i++) {\r
8285     if (buttonDesc[i].hwnd != NULL)\r
8286       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8287   }\r
8288 }\r
8289 \r
8290 \r
8291 VOID\r
8292 SetUserThinkingEnables()\r
8293 {\r
8294   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8295 }\r
8296 \r
8297 VOID\r
8298 SetMachineThinkingEnables()\r
8299 {\r
8300   HMENU hMenu = GetMenu(hwndMain);\r
8301   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8302 \r
8303   SetMenuEnables(hMenu, machineThinkingEnables);\r
8304 \r
8305   if (gameMode == MachinePlaysBlack) {\r
8306     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8307   } else if (gameMode == MachinePlaysWhite) {\r
8308     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8309   } else if (gameMode == TwoMachinesPlay) {\r
8310     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8311   }\r
8312 }\r
8313 \r
8314 \r
8315 VOID\r
8316 DisplayTitle(char *str)\r
8317 {\r
8318   char title[MSG_SIZ], *host;\r
8319   if (str[0] != NULLCHAR) {\r
8320     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8321   } else if (appData.icsActive) {\r
8322     if (appData.icsCommPort[0] != NULLCHAR)\r
8323       host = "ICS";\r
8324     else \r
8325       host = appData.icsHost;\r
8326       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8327   } else if (appData.noChessProgram) {\r
8328     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8329   } else {\r
8330     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8331     strcat(title, ": ");\r
8332     strcat(title, first.tidy);\r
8333   }\r
8334   SetWindowText(hwndMain, title);\r
8335 }\r
8336 \r
8337 \r
8338 VOID\r
8339 DisplayMessage(char *str1, char *str2)\r
8340 {\r
8341   HDC hdc;\r
8342   HFONT oldFont;\r
8343   int remain = MESSAGE_TEXT_MAX - 1;\r
8344   int len;\r
8345 \r
8346   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8347   messageText[0] = NULLCHAR;\r
8348   if (*str1) {\r
8349     len = strlen(str1);\r
8350     if (len > remain) len = remain;\r
8351     strncpy(messageText, str1, len);\r
8352     messageText[len] = NULLCHAR;\r
8353     remain -= len;\r
8354   }\r
8355   if (*str2 && remain >= 2) {\r
8356     if (*str1) {\r
8357       strcat(messageText, "  ");\r
8358       remain -= 2;\r
8359     }\r
8360     len = strlen(str2);\r
8361     if (len > remain) len = remain;\r
8362     strncat(messageText, str2, len);\r
8363   }\r
8364   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8365   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8366 \r
8367   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8368 \r
8369   SAYMACHINEMOVE();\r
8370 \r
8371   hdc = GetDC(hwndMain);\r
8372   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8373   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8374              &messageRect, messageText, strlen(messageText), NULL);\r
8375   (void) SelectObject(hdc, oldFont);\r
8376   (void) ReleaseDC(hwndMain, hdc);\r
8377 }\r
8378 \r
8379 VOID\r
8380 DisplayError(char *str, int error)\r
8381 {\r
8382   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8383   int len;\r
8384 \r
8385   if (error == 0) {\r
8386     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8387   } else {\r
8388     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8389                         NULL, error, LANG_NEUTRAL,\r
8390                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8391     if (len > 0) {\r
8392       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8393     } else {\r
8394       ErrorMap *em = errmap;\r
8395       while (em->err != 0 && em->err != error) em++;\r
8396       if (em->err != 0) {\r
8397         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8398       } else {\r
8399         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8400       }\r
8401     }\r
8402   }\r
8403   \r
8404   ErrorPopUp(_("Error"), buf);\r
8405 }\r
8406 \r
8407 \r
8408 VOID\r
8409 DisplayMoveError(char *str)\r
8410 {\r
8411   fromX = fromY = -1;\r
8412   ClearHighlights();\r
8413   DrawPosition(FALSE, NULL);\r
8414   if (appData.popupMoveErrors) {\r
8415     ErrorPopUp(_("Error"), str);\r
8416   } else {\r
8417     DisplayMessage(str, "");\r
8418     moveErrorMessageUp = TRUE;\r
8419   }\r
8420 }\r
8421 \r
8422 VOID\r
8423 DisplayFatalError(char *str, int error, int exitStatus)\r
8424 {\r
8425   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8426   int len;\r
8427   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8428 \r
8429   if (error != 0) {\r
8430     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8431                         NULL, error, LANG_NEUTRAL,\r
8432                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8433     if (len > 0) {\r
8434       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8435     } else {\r
8436       ErrorMap *em = errmap;\r
8437       while (em->err != 0 && em->err != error) em++;\r
8438       if (em->err != 0) {\r
8439         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8440       } else {\r
8441         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8442       }\r
8443     }\r
8444     str = buf;\r
8445   }\r
8446   if (appData.debugMode) {\r
8447     fprintf(debugFP, "%s: %s\n", label, str);\r
8448   }\r
8449   if (appData.popupExitMessage) {\r
8450     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8451                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8452   }\r
8453   ExitEvent(exitStatus);\r
8454 }\r
8455 \r
8456 \r
8457 VOID\r
8458 DisplayInformation(char *str)\r
8459 {\r
8460   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8461 }\r
8462 \r
8463 \r
8464 VOID\r
8465 DisplayNote(char *str)\r
8466 {\r
8467   ErrorPopUp(_("Note"), str);\r
8468 }\r
8469 \r
8470 \r
8471 typedef struct {\r
8472   char *title, *question, *replyPrefix;\r
8473   ProcRef pr;\r
8474 } QuestionParams;\r
8475 \r
8476 LRESULT CALLBACK\r
8477 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8478 {\r
8479   static QuestionParams *qp;\r
8480   char reply[MSG_SIZ];\r
8481   int len, err;\r
8482 \r
8483   switch (message) {\r
8484   case WM_INITDIALOG:\r
8485     qp = (QuestionParams *) lParam;\r
8486     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8487     Translate(hDlg, DLG_Question);\r
8488     SetWindowText(hDlg, qp->title);\r
8489     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8490     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8491     return FALSE;\r
8492 \r
8493   case WM_COMMAND:\r
8494     switch (LOWORD(wParam)) {\r
8495     case IDOK:\r
8496       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8497       if (*reply) strcat(reply, " ");\r
8498       len = strlen(reply);\r
8499       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8500       strcat(reply, "\n");\r
8501       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8502       EndDialog(hDlg, TRUE);\r
8503       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8504       return TRUE;\r
8505     case IDCANCEL:\r
8506       EndDialog(hDlg, FALSE);\r
8507       return TRUE;\r
8508     default:\r
8509       break;\r
8510     }\r
8511     break;\r
8512   }\r
8513   return FALSE;\r
8514 }\r
8515 \r
8516 VOID\r
8517 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8518 {\r
8519     QuestionParams qp;\r
8520     FARPROC lpProc;\r
8521     \r
8522     qp.title = title;\r
8523     qp.question = question;\r
8524     qp.replyPrefix = replyPrefix;\r
8525     qp.pr = pr;\r
8526     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8527     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8528       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8529     FreeProcInstance(lpProc);\r
8530 }\r
8531 \r
8532 /* [AS] Pick FRC position */\r
8533 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8534 {\r
8535     static int * lpIndexFRC;\r
8536     BOOL index_is_ok;\r
8537     char buf[16];\r
8538 \r
8539     switch( message )\r
8540     {\r
8541     case WM_INITDIALOG:\r
8542         lpIndexFRC = (int *) lParam;\r
8543 \r
8544         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8545         Translate(hDlg, DLG_NewGameFRC);\r
8546 \r
8547         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8548         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8549         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8550         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8551 \r
8552         break;\r
8553 \r
8554     case WM_COMMAND:\r
8555         switch( LOWORD(wParam) ) {\r
8556         case IDOK:\r
8557             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8558             EndDialog( hDlg, 0 );\r
8559             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8560             return TRUE;\r
8561         case IDCANCEL:\r
8562             EndDialog( hDlg, 1 );   \r
8563             return TRUE;\r
8564         case IDC_NFG_Edit:\r
8565             if( HIWORD(wParam) == EN_CHANGE ) {\r
8566                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8567 \r
8568                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8569             }\r
8570             return TRUE;\r
8571         case IDC_NFG_Random:\r
8572           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8573             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8574             return TRUE;\r
8575         }\r
8576 \r
8577         break;\r
8578     }\r
8579 \r
8580     return FALSE;\r
8581 }\r
8582 \r
8583 int NewGameFRC()\r
8584 {\r
8585     int result;\r
8586     int index = appData.defaultFrcPosition;\r
8587     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8588 \r
8589     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8590 \r
8591     if( result == 0 ) {\r
8592         appData.defaultFrcPosition = index;\r
8593     }\r
8594 \r
8595     return result;\r
8596 }\r
8597 \r
8598 /* [AS] Game list options. Refactored by HGM */\r
8599 \r
8600 HWND gameListOptionsDialog;\r
8601 \r
8602 // low-level front-end: clear text edit / list widget\r
8603 void\r
8604 \r
8605 GLT_ClearList()\r
8606 {\r
8607     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8608 }\r
8609 \r
8610 // low-level front-end: clear text edit / list widget\r
8611 void\r
8612 GLT_DeSelectList()\r
8613 {\r
8614     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8615 }\r
8616 \r
8617 // low-level front-end: append line to text edit / list widget\r
8618 void\r
8619 GLT_AddToList( char *name )\r
8620 {\r
8621     if( name != 0 ) {\r
8622             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8623     }\r
8624 }\r
8625 \r
8626 // low-level front-end: get line from text edit / list widget\r
8627 Boolean\r
8628 GLT_GetFromList( int index, char *name )\r
8629 {\r
8630     if( name != 0 ) {\r
8631             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8632                 return TRUE;\r
8633     }\r
8634     return FALSE;\r
8635 }\r
8636 \r
8637 void GLT_MoveSelection( HWND hDlg, int delta )\r
8638 {\r
8639     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8640     int idx2 = idx1 + delta;\r
8641     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8642 \r
8643     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8644         char buf[128];\r
8645 \r
8646         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8647         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8648         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8649         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8650     }\r
8651 }\r
8652 \r
8653 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8654 {\r
8655     switch( message )\r
8656     {\r
8657     case WM_INITDIALOG:\r
8658         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8659         \r
8660         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8661         Translate(hDlg, DLG_GameListOptions);\r
8662 \r
8663         /* Initialize list */\r
8664         GLT_TagsToList( lpUserGLT );\r
8665 \r
8666         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8667 \r
8668         break;\r
8669 \r
8670     case WM_COMMAND:\r
8671         switch( LOWORD(wParam) ) {\r
8672         case IDOK:\r
8673             GLT_ParseList();\r
8674             EndDialog( hDlg, 0 );\r
8675             return TRUE;\r
8676         case IDCANCEL:\r
8677             EndDialog( hDlg, 1 );\r
8678             return TRUE;\r
8679 \r
8680         case IDC_GLT_Default:\r
8681             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8682             return TRUE;\r
8683 \r
8684         case IDC_GLT_Restore:\r
8685             GLT_TagsToList( appData.gameListTags );\r
8686             return TRUE;\r
8687 \r
8688         case IDC_GLT_Up:\r
8689             GLT_MoveSelection( hDlg, -1 );\r
8690             return TRUE;\r
8691 \r
8692         case IDC_GLT_Down:\r
8693             GLT_MoveSelection( hDlg, +1 );\r
8694             return TRUE;\r
8695         }\r
8696 \r
8697         break;\r
8698     }\r
8699 \r
8700     return FALSE;\r
8701 }\r
8702 \r
8703 int GameListOptions()\r
8704 {\r
8705     int result;\r
8706     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8707 \r
8708       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8709 \r
8710     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8711 \r
8712     if( result == 0 ) {\r
8713         /* [AS] Memory leak here! */\r
8714         appData.gameListTags = strdup( lpUserGLT ); \r
8715     }\r
8716 \r
8717     return result;\r
8718 }\r
8719 \r
8720 VOID\r
8721 DisplayIcsInteractionTitle(char *str)\r
8722 {\r
8723   char consoleTitle[MSG_SIZ];\r
8724 \r
8725     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8726     SetWindowText(hwndConsole, consoleTitle);\r
8727 \r
8728     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8729       char buf[MSG_SIZ], *p = buf, *q;\r
8730         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8731       do {\r
8732         q = strchr(p, ';');\r
8733         if(q) *q++ = 0;\r
8734         if(*p) ChatPopUp(p);\r
8735       } while(p=q);\r
8736     }\r
8737 \r
8738     SetActiveWindow(hwndMain);\r
8739 }\r
8740 \r
8741 void\r
8742 DrawPosition(int fullRedraw, Board board)\r
8743 {\r
8744   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8745 }\r
8746 \r
8747 void NotifyFrontendLogin()\r
8748 {\r
8749         if (hwndConsole)\r
8750                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8751 }\r
8752 \r
8753 VOID\r
8754 ResetFrontEnd()\r
8755 {\r
8756   fromX = fromY = -1;\r
8757   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8758     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8759     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8760     dragInfo.lastpos = dragInfo.pos;\r
8761     dragInfo.start.x = dragInfo.start.y = -1;\r
8762     dragInfo.from = dragInfo.start;\r
8763     ReleaseCapture();\r
8764     DrawPosition(TRUE, NULL);\r
8765   }\r
8766   TagsPopDown();\r
8767 }\r
8768 \r
8769 \r
8770 VOID\r
8771 CommentPopUp(char *title, char *str)\r
8772 {\r
8773   HWND hwnd = GetActiveWindow();\r
8774   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8775   SAY(str);\r
8776   SetActiveWindow(hwnd);\r
8777 }\r
8778 \r
8779 VOID\r
8780 CommentPopDown(void)\r
8781 {\r
8782   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8783   if (commentDialog) {\r
8784     ShowWindow(commentDialog, SW_HIDE);\r
8785   }\r
8786   commentUp = FALSE;\r
8787 }\r
8788 \r
8789 VOID\r
8790 EditCommentPopUp(int index, char *title, char *str)\r
8791 {\r
8792   EitherCommentPopUp(index, title, str, TRUE);\r
8793 }\r
8794 \r
8795 \r
8796 int\r
8797 Roar()\r
8798 {\r
8799   MyPlaySound(&sounds[(int)SoundRoar]);\r
8800   return 1;\r
8801 }\r
8802 \r
8803 VOID\r
8804 RingBell()\r
8805 {\r
8806   MyPlaySound(&sounds[(int)SoundMove]);\r
8807 }\r
8808 \r
8809 VOID PlayIcsWinSound()\r
8810 {\r
8811   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8812 }\r
8813 \r
8814 VOID PlayIcsLossSound()\r
8815 {\r
8816   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8817 }\r
8818 \r
8819 VOID PlayIcsDrawSound()\r
8820 {\r
8821   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8822 }\r
8823 \r
8824 VOID PlayIcsUnfinishedSound()\r
8825 {\r
8826   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8827 }\r
8828 \r
8829 VOID\r
8830 PlayAlarmSound()\r
8831 {\r
8832   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8833 }\r
8834 \r
8835 VOID\r
8836 PlayTellSound()\r
8837 {\r
8838   MyPlaySound(&textAttribs[ColorTell].sound);\r
8839 }\r
8840 \r
8841 \r
8842 VOID\r
8843 EchoOn()\r
8844 {\r
8845   HWND hInput;\r
8846   consoleEcho = TRUE;\r
8847   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8848   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8849   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8850 }\r
8851 \r
8852 \r
8853 VOID\r
8854 EchoOff()\r
8855 {\r
8856   CHARFORMAT cf;\r
8857   HWND hInput;\r
8858   consoleEcho = FALSE;\r
8859   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8860   /* This works OK: set text and background both to the same color */\r
8861   cf = consoleCF;\r
8862   cf.crTextColor = COLOR_ECHOOFF;\r
8863   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8864   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8865 }\r
8866 \r
8867 /* No Raw()...? */\r
8868 \r
8869 void Colorize(ColorClass cc, int continuation)\r
8870 {\r
8871   currentColorClass = cc;\r
8872   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8873   consoleCF.crTextColor = textAttribs[cc].color;\r
8874   consoleCF.dwEffects = textAttribs[cc].effects;\r
8875   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8876 }\r
8877 \r
8878 char *\r
8879 UserName()\r
8880 {\r
8881   static char buf[MSG_SIZ];\r
8882   DWORD bufsiz = MSG_SIZ;\r
8883 \r
8884   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8885         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8886   }\r
8887   if (!GetUserName(buf, &bufsiz)) {\r
8888     /*DisplayError("Error getting user name", GetLastError());*/\r
8889     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8890   }\r
8891   return buf;\r
8892 }\r
8893 \r
8894 char *\r
8895 HostName()\r
8896 {\r
8897   static char buf[MSG_SIZ];\r
8898   DWORD bufsiz = MSG_SIZ;\r
8899 \r
8900   if (!GetComputerName(buf, &bufsiz)) {\r
8901     /*DisplayError("Error getting host name", GetLastError());*/\r
8902     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8903   }\r
8904   return buf;\r
8905 }\r
8906 \r
8907 \r
8908 int\r
8909 ClockTimerRunning()\r
8910 {\r
8911   return clockTimerEvent != 0;\r
8912 }\r
8913 \r
8914 int\r
8915 StopClockTimer()\r
8916 {\r
8917   if (clockTimerEvent == 0) return FALSE;\r
8918   KillTimer(hwndMain, clockTimerEvent);\r
8919   clockTimerEvent = 0;\r
8920   return TRUE;\r
8921 }\r
8922 \r
8923 void\r
8924 StartClockTimer(long millisec)\r
8925 {\r
8926   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8927                              (UINT) millisec, NULL);\r
8928 }\r
8929 \r
8930 void\r
8931 DisplayWhiteClock(long timeRemaining, int highlight)\r
8932 {\r
8933   HDC hdc;\r
8934   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8935 \r
8936   if(appData.noGUI) return;\r
8937   hdc = GetDC(hwndMain);\r
8938   if (!IsIconic(hwndMain)) {\r
8939     DisplayAClock(hdc, timeRemaining, highlight, \r
8940                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8941   }\r
8942   if (highlight && iconCurrent == iconBlack) {\r
8943     iconCurrent = iconWhite;\r
8944     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8945     if (IsIconic(hwndMain)) {\r
8946       DrawIcon(hdc, 2, 2, iconCurrent);\r
8947     }\r
8948   }\r
8949   (void) ReleaseDC(hwndMain, hdc);\r
8950   if (hwndConsole)\r
8951     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8952 }\r
8953 \r
8954 void\r
8955 DisplayBlackClock(long timeRemaining, int highlight)\r
8956 {\r
8957   HDC hdc;\r
8958   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8959 \r
8960 \r
8961   if(appData.noGUI) return;\r
8962   hdc = GetDC(hwndMain);\r
8963   if (!IsIconic(hwndMain)) {\r
8964     DisplayAClock(hdc, timeRemaining, highlight, \r
8965                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8966   }\r
8967   if (highlight && iconCurrent == iconWhite) {\r
8968     iconCurrent = iconBlack;\r
8969     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8970     if (IsIconic(hwndMain)) {\r
8971       DrawIcon(hdc, 2, 2, iconCurrent);\r
8972     }\r
8973   }\r
8974   (void) ReleaseDC(hwndMain, hdc);\r
8975   if (hwndConsole)\r
8976     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8977 }\r
8978 \r
8979 \r
8980 int\r
8981 LoadGameTimerRunning()\r
8982 {\r
8983   return loadGameTimerEvent != 0;\r
8984 }\r
8985 \r
8986 int\r
8987 StopLoadGameTimer()\r
8988 {\r
8989   if (loadGameTimerEvent == 0) return FALSE;\r
8990   KillTimer(hwndMain, loadGameTimerEvent);\r
8991   loadGameTimerEvent = 0;\r
8992   return TRUE;\r
8993 }\r
8994 \r
8995 void\r
8996 StartLoadGameTimer(long millisec)\r
8997 {\r
8998   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8999                                 (UINT) millisec, NULL);\r
9000 }\r
9001 \r
9002 void\r
9003 AutoSaveGame()\r
9004 {\r
9005   char *defName;\r
9006   FILE *f;\r
9007   char fileTitle[MSG_SIZ];\r
9008 \r
9009   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9010   f = OpenFileDialog(hwndMain, "a", defName,\r
9011                      appData.oldSaveStyle ? "gam" : "pgn",\r
9012                      GAME_FILT, \r
9013                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9014   if (f != NULL) {\r
9015     SaveGame(f, 0, "");\r
9016     fclose(f);\r
9017   }\r
9018 }\r
9019 \r
9020 \r
9021 void\r
9022 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9023 {\r
9024   if (delayedTimerEvent != 0) {\r
9025     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9026       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9027     }\r
9028     KillTimer(hwndMain, delayedTimerEvent);\r
9029     delayedTimerEvent = 0;\r
9030     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9031     delayedTimerCallback();\r
9032   }\r
9033   delayedTimerCallback = cb;\r
9034   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9035                                 (UINT) millisec, NULL);\r
9036 }\r
9037 \r
9038 DelayedEventCallback\r
9039 GetDelayedEvent()\r
9040 {\r
9041   if (delayedTimerEvent) {\r
9042     return delayedTimerCallback;\r
9043   } else {\r
9044     return NULL;\r
9045   }\r
9046 }\r
9047 \r
9048 void\r
9049 CancelDelayedEvent()\r
9050 {\r
9051   if (delayedTimerEvent) {\r
9052     KillTimer(hwndMain, delayedTimerEvent);\r
9053     delayedTimerEvent = 0;\r
9054   }\r
9055 }\r
9056 \r
9057 DWORD GetWin32Priority(int nice)\r
9058 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9059 /*\r
9060 REALTIME_PRIORITY_CLASS     0x00000100\r
9061 HIGH_PRIORITY_CLASS         0x00000080\r
9062 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9063 NORMAL_PRIORITY_CLASS       0x00000020\r
9064 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9065 IDLE_PRIORITY_CLASS         0x00000040\r
9066 */\r
9067         if (nice < -15) return 0x00000080;\r
9068         if (nice < 0)   return 0x00008000;\r
9069         if (nice == 0)  return 0x00000020;\r
9070         if (nice < 15)  return 0x00004000;\r
9071         return 0x00000040;\r
9072 }\r
9073 \r
9074 void RunCommand(char *cmdLine)\r
9075 {\r
9076   /* Now create the child process. */\r
9077   STARTUPINFO siStartInfo;\r
9078   PROCESS_INFORMATION piProcInfo;\r
9079 \r
9080   siStartInfo.cb = sizeof(STARTUPINFO);\r
9081   siStartInfo.lpReserved = NULL;\r
9082   siStartInfo.lpDesktop = NULL;\r
9083   siStartInfo.lpTitle = NULL;\r
9084   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9085   siStartInfo.cbReserved2 = 0;\r
9086   siStartInfo.lpReserved2 = NULL;\r
9087   siStartInfo.hStdInput = NULL;\r
9088   siStartInfo.hStdOutput = NULL;\r
9089   siStartInfo.hStdError = NULL;\r
9090 \r
9091   CreateProcess(NULL,\r
9092                 cmdLine,           /* command line */\r
9093                 NULL,      /* process security attributes */\r
9094                 NULL,      /* primary thread security attrs */\r
9095                 TRUE,      /* handles are inherited */\r
9096                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9097                 NULL,      /* use parent's environment */\r
9098                 NULL,\r
9099                 &siStartInfo, /* STARTUPINFO pointer */\r
9100                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9101 \r
9102   CloseHandle(piProcInfo.hThread);\r
9103 }\r
9104 \r
9105 /* Start a child process running the given program.\r
9106    The process's standard output can be read from "from", and its\r
9107    standard input can be written to "to".\r
9108    Exit with fatal error if anything goes wrong.\r
9109    Returns an opaque pointer that can be used to destroy the process\r
9110    later.\r
9111 */\r
9112 int\r
9113 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9114 {\r
9115 #define BUFSIZE 4096\r
9116 \r
9117   HANDLE hChildStdinRd, hChildStdinWr,\r
9118     hChildStdoutRd, hChildStdoutWr;\r
9119   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9120   SECURITY_ATTRIBUTES saAttr;\r
9121   BOOL fSuccess;\r
9122   PROCESS_INFORMATION piProcInfo;\r
9123   STARTUPINFO siStartInfo;\r
9124   ChildProc *cp;\r
9125   char buf[MSG_SIZ];\r
9126   DWORD err;\r
9127 \r
9128   if (appData.debugMode) {\r
9129     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9130   }\r
9131 \r
9132   *pr = NoProc;\r
9133 \r
9134   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9135   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9136   saAttr.bInheritHandle = TRUE;\r
9137   saAttr.lpSecurityDescriptor = NULL;\r
9138 \r
9139   /*\r
9140    * The steps for redirecting child's STDOUT:\r
9141    *     1. Create anonymous pipe to be STDOUT for child.\r
9142    *     2. Create a noninheritable duplicate of read handle,\r
9143    *         and close the inheritable read handle.\r
9144    */\r
9145 \r
9146   /* Create a pipe for the child's STDOUT. */\r
9147   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9148     return GetLastError();\r
9149   }\r
9150 \r
9151   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9152   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9153                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9154                              FALSE,     /* not inherited */\r
9155                              DUPLICATE_SAME_ACCESS);\r
9156   if (! fSuccess) {\r
9157     return GetLastError();\r
9158   }\r
9159   CloseHandle(hChildStdoutRd);\r
9160 \r
9161   /*\r
9162    * The steps for redirecting child's STDIN:\r
9163    *     1. Create anonymous pipe to be STDIN for child.\r
9164    *     2. Create a noninheritable duplicate of write handle,\r
9165    *         and close the inheritable write handle.\r
9166    */\r
9167 \r
9168   /* Create a pipe for the child's STDIN. */\r
9169   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9170     return GetLastError();\r
9171   }\r
9172 \r
9173   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9174   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9175                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9176                              FALSE,     /* not inherited */\r
9177                              DUPLICATE_SAME_ACCESS);\r
9178   if (! fSuccess) {\r
9179     return GetLastError();\r
9180   }\r
9181   CloseHandle(hChildStdinWr);\r
9182 \r
9183   /* Arrange to (1) look in dir for the child .exe file, and\r
9184    * (2) have dir be the child's working directory.  Interpret\r
9185    * dir relative to the directory WinBoard loaded from. */\r
9186   GetCurrentDirectory(MSG_SIZ, buf);\r
9187   SetCurrentDirectory(installDir);\r
9188   SetCurrentDirectory(dir);\r
9189 \r
9190   /* Now create the child process. */\r
9191 \r
9192   siStartInfo.cb = sizeof(STARTUPINFO);\r
9193   siStartInfo.lpReserved = NULL;\r
9194   siStartInfo.lpDesktop = NULL;\r
9195   siStartInfo.lpTitle = NULL;\r
9196   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9197   siStartInfo.cbReserved2 = 0;\r
9198   siStartInfo.lpReserved2 = NULL;\r
9199   siStartInfo.hStdInput = hChildStdinRd;\r
9200   siStartInfo.hStdOutput = hChildStdoutWr;\r
9201   siStartInfo.hStdError = hChildStdoutWr;\r
9202 \r
9203   fSuccess = CreateProcess(NULL,\r
9204                            cmdLine,        /* command line */\r
9205                            NULL,           /* process security attributes */\r
9206                            NULL,           /* primary thread security attrs */\r
9207                            TRUE,           /* handles are inherited */\r
9208                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9209                            NULL,           /* use parent's environment */\r
9210                            NULL,\r
9211                            &siStartInfo, /* STARTUPINFO pointer */\r
9212                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9213 \r
9214   err = GetLastError();\r
9215   SetCurrentDirectory(buf); /* return to prev directory */\r
9216   if (! fSuccess) {\r
9217     return err;\r
9218   }\r
9219 \r
9220   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9221     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9222     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9223   }\r
9224 \r
9225   /* Close the handles we don't need in the parent */\r
9226   CloseHandle(piProcInfo.hThread);\r
9227   CloseHandle(hChildStdinRd);\r
9228   CloseHandle(hChildStdoutWr);\r
9229 \r
9230   /* Prepare return value */\r
9231   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9232   cp->kind = CPReal;\r
9233   cp->hProcess = piProcInfo.hProcess;\r
9234   cp->pid = piProcInfo.dwProcessId;\r
9235   cp->hFrom = hChildStdoutRdDup;\r
9236   cp->hTo = hChildStdinWrDup;\r
9237 \r
9238   *pr = (void *) cp;\r
9239 \r
9240   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9241      2000 where engines sometimes don't see the initial command(s)\r
9242      from WinBoard and hang.  I don't understand how that can happen,\r
9243      but the Sleep is harmless, so I've put it in.  Others have also\r
9244      reported what may be the same problem, so hopefully this will fix\r
9245      it for them too.  */\r
9246   Sleep(500);\r
9247 \r
9248   return NO_ERROR;\r
9249 }\r
9250 \r
9251 \r
9252 void\r
9253 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9254 {\r
9255   ChildProc *cp; int result;\r
9256 \r
9257   cp = (ChildProc *) pr;\r
9258   if (cp == NULL) return;\r
9259 \r
9260   switch (cp->kind) {\r
9261   case CPReal:\r
9262     /* TerminateProcess is considered harmful, so... */\r
9263     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9264     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9265     /* The following doesn't work because the chess program\r
9266        doesn't "have the same console" as WinBoard.  Maybe\r
9267        we could arrange for this even though neither WinBoard\r
9268        nor the chess program uses a console for stdio? */\r
9269     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9270 \r
9271     /* [AS] Special termination modes for misbehaving programs... */\r
9272     if( signal & 8 ) { \r
9273         result = TerminateProcess( cp->hProcess, 0 );\r
9274 \r
9275         if ( appData.debugMode) {\r
9276             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9277         }\r
9278     }\r
9279     else if( signal & 4 ) {\r
9280         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9281 \r
9282         if( dw != WAIT_OBJECT_0 ) {\r
9283             result = TerminateProcess( cp->hProcess, 0 );\r
9284 \r
9285             if ( appData.debugMode) {\r
9286                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9287             }\r
9288 \r
9289         }\r
9290     }\r
9291 \r
9292     CloseHandle(cp->hProcess);\r
9293     break;\r
9294 \r
9295   case CPComm:\r
9296     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9297     break;\r
9298 \r
9299   case CPSock:\r
9300     closesocket(cp->sock);\r
9301     WSACleanup();\r
9302     break;\r
9303 \r
9304   case CPRcmd:\r
9305     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9306     closesocket(cp->sock);\r
9307     closesocket(cp->sock2);\r
9308     WSACleanup();\r
9309     break;\r
9310   }\r
9311   free(cp);\r
9312 }\r
9313 \r
9314 void\r
9315 InterruptChildProcess(ProcRef pr)\r
9316 {\r
9317   ChildProc *cp;\r
9318 \r
9319   cp = (ChildProc *) pr;\r
9320   if (cp == NULL) return;\r
9321   switch (cp->kind) {\r
9322   case CPReal:\r
9323     /* The following doesn't work because the chess program\r
9324        doesn't "have the same console" as WinBoard.  Maybe\r
9325        we could arrange for this even though neither WinBoard\r
9326        nor the chess program uses a console for stdio */\r
9327     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9328     break;\r
9329 \r
9330   case CPComm:\r
9331   case CPSock:\r
9332     /* Can't interrupt */\r
9333     break;\r
9334 \r
9335   case CPRcmd:\r
9336     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9337     break;\r
9338   }\r
9339 }\r
9340 \r
9341 \r
9342 int\r
9343 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9344 {\r
9345   char cmdLine[MSG_SIZ];\r
9346 \r
9347   if (port[0] == NULLCHAR) {\r
9348     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9349   } else {\r
9350     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9351   }\r
9352   return StartChildProcess(cmdLine, "", pr);\r
9353 }\r
9354 \r
9355 \r
9356 /* Code to open TCP sockets */\r
9357 \r
9358 int\r
9359 OpenTCP(char *host, char *port, ProcRef *pr)\r
9360 {\r
9361   ChildProc *cp;\r
9362   int err;\r
9363   SOCKET s;\r
9364 \r
9365   struct sockaddr_in sa, mysa;\r
9366   struct hostent FAR *hp;\r
9367   unsigned short uport;\r
9368   WORD wVersionRequested;\r
9369   WSADATA wsaData;\r
9370 \r
9371   /* Initialize socket DLL */\r
9372   wVersionRequested = MAKEWORD(1, 1);\r
9373   err = WSAStartup(wVersionRequested, &wsaData);\r
9374   if (err != 0) return err;\r
9375 \r
9376   /* Make socket */\r
9377   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9378     err = WSAGetLastError();\r
9379     WSACleanup();\r
9380     return err;\r
9381   }\r
9382 \r
9383   /* Bind local address using (mostly) don't-care values.\r
9384    */\r
9385   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9386   mysa.sin_family = AF_INET;\r
9387   mysa.sin_addr.s_addr = INADDR_ANY;\r
9388   uport = (unsigned short) 0;\r
9389   mysa.sin_port = htons(uport);\r
9390   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9391       == SOCKET_ERROR) {\r
9392     err = WSAGetLastError();\r
9393     WSACleanup();\r
9394     return err;\r
9395   }\r
9396 \r
9397   /* Resolve remote host name */\r
9398   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9399   if (!(hp = gethostbyname(host))) {\r
9400     unsigned int b0, b1, b2, b3;\r
9401 \r
9402     err = WSAGetLastError();\r
9403 \r
9404     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9405       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9406       hp->h_addrtype = AF_INET;\r
9407       hp->h_length = 4;\r
9408       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9409       hp->h_addr_list[0] = (char *) malloc(4);\r
9410       hp->h_addr_list[0][0] = (char) b0;\r
9411       hp->h_addr_list[0][1] = (char) b1;\r
9412       hp->h_addr_list[0][2] = (char) b2;\r
9413       hp->h_addr_list[0][3] = (char) b3;\r
9414     } else {\r
9415       WSACleanup();\r
9416       return err;\r
9417     }\r
9418   }\r
9419   sa.sin_family = hp->h_addrtype;\r
9420   uport = (unsigned short) atoi(port);\r
9421   sa.sin_port = htons(uport);\r
9422   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9423 \r
9424   /* Make connection */\r
9425   if (connect(s, (struct sockaddr *) &sa,\r
9426               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9427     err = WSAGetLastError();\r
9428     WSACleanup();\r
9429     return err;\r
9430   }\r
9431 \r
9432   /* Prepare return value */\r
9433   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9434   cp->kind = CPSock;\r
9435   cp->sock = s;\r
9436   *pr = (ProcRef *) cp;\r
9437 \r
9438   return NO_ERROR;\r
9439 }\r
9440 \r
9441 int\r
9442 OpenCommPort(char *name, ProcRef *pr)\r
9443 {\r
9444   HANDLE h;\r
9445   COMMTIMEOUTS ct;\r
9446   ChildProc *cp;\r
9447   char fullname[MSG_SIZ];\r
9448 \r
9449   if (*name != '\\')\r
9450     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9451   else\r
9452     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9453 \r
9454   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9455                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9456   if (h == (HANDLE) -1) {\r
9457     return GetLastError();\r
9458   }\r
9459   hCommPort = h;\r
9460 \r
9461   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9462 \r
9463   /* Accumulate characters until a 100ms pause, then parse */\r
9464   ct.ReadIntervalTimeout = 100;\r
9465   ct.ReadTotalTimeoutMultiplier = 0;\r
9466   ct.ReadTotalTimeoutConstant = 0;\r
9467   ct.WriteTotalTimeoutMultiplier = 0;\r
9468   ct.WriteTotalTimeoutConstant = 0;\r
9469   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9470 \r
9471   /* Prepare return value */\r
9472   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9473   cp->kind = CPComm;\r
9474   cp->hFrom = h;\r
9475   cp->hTo = h;\r
9476   *pr = (ProcRef *) cp;\r
9477 \r
9478   return NO_ERROR;\r
9479 }\r
9480 \r
9481 int\r
9482 OpenLoopback(ProcRef *pr)\r
9483 {\r
9484   DisplayFatalError(_("Not implemented"), 0, 1);\r
9485   return NO_ERROR;\r
9486 }\r
9487 \r
9488 \r
9489 int\r
9490 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9491 {\r
9492   ChildProc *cp;\r
9493   int err;\r
9494   SOCKET s, s2, s3;\r
9495   struct sockaddr_in sa, mysa;\r
9496   struct hostent FAR *hp;\r
9497   unsigned short uport;\r
9498   WORD wVersionRequested;\r
9499   WSADATA wsaData;\r
9500   int fromPort;\r
9501   char stderrPortStr[MSG_SIZ];\r
9502 \r
9503   /* Initialize socket DLL */\r
9504   wVersionRequested = MAKEWORD(1, 1);\r
9505   err = WSAStartup(wVersionRequested, &wsaData);\r
9506   if (err != 0) return err;\r
9507 \r
9508   /* Resolve remote host name */\r
9509   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9510   if (!(hp = gethostbyname(host))) {\r
9511     unsigned int b0, b1, b2, b3;\r
9512 \r
9513     err = WSAGetLastError();\r
9514 \r
9515     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9516       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9517       hp->h_addrtype = AF_INET;\r
9518       hp->h_length = 4;\r
9519       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9520       hp->h_addr_list[0] = (char *) malloc(4);\r
9521       hp->h_addr_list[0][0] = (char) b0;\r
9522       hp->h_addr_list[0][1] = (char) b1;\r
9523       hp->h_addr_list[0][2] = (char) b2;\r
9524       hp->h_addr_list[0][3] = (char) b3;\r
9525     } else {\r
9526       WSACleanup();\r
9527       return err;\r
9528     }\r
9529   }\r
9530   sa.sin_family = hp->h_addrtype;\r
9531   uport = (unsigned short) 514;\r
9532   sa.sin_port = htons(uport);\r
9533   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9534 \r
9535   /* Bind local socket to unused "privileged" port address\r
9536    */\r
9537   s = INVALID_SOCKET;\r
9538   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9539   mysa.sin_family = AF_INET;\r
9540   mysa.sin_addr.s_addr = INADDR_ANY;\r
9541   for (fromPort = 1023;; fromPort--) {\r
9542     if (fromPort < 0) {\r
9543       WSACleanup();\r
9544       return WSAEADDRINUSE;\r
9545     }\r
9546     if (s == INVALID_SOCKET) {\r
9547       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9548         err = WSAGetLastError();\r
9549         WSACleanup();\r
9550         return err;\r
9551       }\r
9552     }\r
9553     uport = (unsigned short) fromPort;\r
9554     mysa.sin_port = htons(uport);\r
9555     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9556         == SOCKET_ERROR) {\r
9557       err = WSAGetLastError();\r
9558       if (err == WSAEADDRINUSE) continue;\r
9559       WSACleanup();\r
9560       return err;\r
9561     }\r
9562     if (connect(s, (struct sockaddr *) &sa,\r
9563       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9564       err = WSAGetLastError();\r
9565       if (err == WSAEADDRINUSE) {\r
9566         closesocket(s);\r
9567         s = -1;\r
9568         continue;\r
9569       }\r
9570       WSACleanup();\r
9571       return err;\r
9572     }\r
9573     break;\r
9574   }\r
9575 \r
9576   /* Bind stderr local socket to unused "privileged" port address\r
9577    */\r
9578   s2 = INVALID_SOCKET;\r
9579   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9580   mysa.sin_family = AF_INET;\r
9581   mysa.sin_addr.s_addr = INADDR_ANY;\r
9582   for (fromPort = 1023;; fromPort--) {\r
9583     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9584     if (fromPort < 0) {\r
9585       (void) closesocket(s);\r
9586       WSACleanup();\r
9587       return WSAEADDRINUSE;\r
9588     }\r
9589     if (s2 == INVALID_SOCKET) {\r
9590       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9591         err = WSAGetLastError();\r
9592         closesocket(s);\r
9593         WSACleanup();\r
9594         return err;\r
9595       }\r
9596     }\r
9597     uport = (unsigned short) fromPort;\r
9598     mysa.sin_port = htons(uport);\r
9599     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9600         == SOCKET_ERROR) {\r
9601       err = WSAGetLastError();\r
9602       if (err == WSAEADDRINUSE) continue;\r
9603       (void) closesocket(s);\r
9604       WSACleanup();\r
9605       return err;\r
9606     }\r
9607     if (listen(s2, 1) == SOCKET_ERROR) {\r
9608       err = WSAGetLastError();\r
9609       if (err == WSAEADDRINUSE) {\r
9610         closesocket(s2);\r
9611         s2 = INVALID_SOCKET;\r
9612         continue;\r
9613       }\r
9614       (void) closesocket(s);\r
9615       (void) closesocket(s2);\r
9616       WSACleanup();\r
9617       return err;\r
9618     }\r
9619     break;\r
9620   }\r
9621   prevStderrPort = fromPort; // remember port used\r
9622   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9623 \r
9624   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9625     err = WSAGetLastError();\r
9626     (void) closesocket(s);\r
9627     (void) closesocket(s2);\r
9628     WSACleanup();\r
9629     return err;\r
9630   }\r
9631 \r
9632   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9633     err = WSAGetLastError();\r
9634     (void) closesocket(s);\r
9635     (void) closesocket(s2);\r
9636     WSACleanup();\r
9637     return err;\r
9638   }\r
9639   if (*user == NULLCHAR) user = UserName();\r
9640   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9641     err = WSAGetLastError();\r
9642     (void) closesocket(s);\r
9643     (void) closesocket(s2);\r
9644     WSACleanup();\r
9645     return err;\r
9646   }\r
9647   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9648     err = WSAGetLastError();\r
9649     (void) closesocket(s);\r
9650     (void) closesocket(s2);\r
9651     WSACleanup();\r
9652     return err;\r
9653   }\r
9654 \r
9655   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9656     err = WSAGetLastError();\r
9657     (void) closesocket(s);\r
9658     (void) closesocket(s2);\r
9659     WSACleanup();\r
9660     return err;\r
9661   }\r
9662   (void) closesocket(s2);  /* Stop listening */\r
9663 \r
9664   /* Prepare return value */\r
9665   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9666   cp->kind = CPRcmd;\r
9667   cp->sock = s;\r
9668   cp->sock2 = s3;\r
9669   *pr = (ProcRef *) cp;\r
9670 \r
9671   return NO_ERROR;\r
9672 }\r
9673 \r
9674 \r
9675 InputSourceRef\r
9676 AddInputSource(ProcRef pr, int lineByLine,\r
9677                InputCallback func, VOIDSTAR closure)\r
9678 {\r
9679   InputSource *is, *is2 = NULL;\r
9680   ChildProc *cp = (ChildProc *) pr;\r
9681 \r
9682   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9683   is->lineByLine = lineByLine;\r
9684   is->func = func;\r
9685   is->closure = closure;\r
9686   is->second = NULL;\r
9687   is->next = is->buf;\r
9688   if (pr == NoProc) {\r
9689     is->kind = CPReal;\r
9690     consoleInputSource = is;\r
9691   } else {\r
9692     is->kind = cp->kind;\r
9693     /* \r
9694         [AS] Try to avoid a race condition if the thread is given control too early:\r
9695         we create all threads suspended so that the is->hThread variable can be\r
9696         safely assigned, then let the threads start with ResumeThread.\r
9697     */\r
9698     switch (cp->kind) {\r
9699     case CPReal:\r
9700       is->hFile = cp->hFrom;\r
9701       cp->hFrom = NULL; /* now owned by InputThread */\r
9702       is->hThread =\r
9703         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9704                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9705       break;\r
9706 \r
9707     case CPComm:\r
9708       is->hFile = cp->hFrom;\r
9709       cp->hFrom = NULL; /* now owned by InputThread */\r
9710       is->hThread =\r
9711         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9712                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9713       break;\r
9714 \r
9715     case CPSock:\r
9716       is->sock = cp->sock;\r
9717       is->hThread =\r
9718         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9719                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9720       break;\r
9721 \r
9722     case CPRcmd:\r
9723       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9724       *is2 = *is;\r
9725       is->sock = cp->sock;\r
9726       is->second = is2;\r
9727       is2->sock = cp->sock2;\r
9728       is2->second = is2;\r
9729       is->hThread =\r
9730         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9731                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9732       is2->hThread =\r
9733         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9734                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9735       break;\r
9736     }\r
9737 \r
9738     if( is->hThread != NULL ) {\r
9739         ResumeThread( is->hThread );\r
9740     }\r
9741 \r
9742     if( is2 != NULL && is2->hThread != NULL ) {\r
9743         ResumeThread( is2->hThread );\r
9744     }\r
9745   }\r
9746 \r
9747   return (InputSourceRef) is;\r
9748 }\r
9749 \r
9750 void\r
9751 RemoveInputSource(InputSourceRef isr)\r
9752 {\r
9753   InputSource *is;\r
9754 \r
9755   is = (InputSource *) isr;\r
9756   is->hThread = NULL;  /* tell thread to stop */\r
9757   CloseHandle(is->hThread);\r
9758   if (is->second != NULL) {\r
9759     is->second->hThread = NULL;\r
9760     CloseHandle(is->second->hThread);\r
9761   }\r
9762 }\r
9763 \r
9764 int no_wrap(char *message, int count)\r
9765 {\r
9766     ConsoleOutput(message, count, FALSE);\r
9767     return count;\r
9768 }\r
9769 \r
9770 int\r
9771 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9772 {\r
9773   DWORD dOutCount;\r
9774   int outCount = SOCKET_ERROR;\r
9775   ChildProc *cp = (ChildProc *) pr;\r
9776   static OVERLAPPED ovl;\r
9777   static int line = 0;\r
9778 \r
9779   if (pr == NoProc)\r
9780   {\r
9781     if (appData.noJoin || !appData.useInternalWrap)\r
9782       return no_wrap(message, count);\r
9783     else\r
9784     {\r
9785       int width = get_term_width();\r
9786       int len = wrap(NULL, message, count, width, &line);\r
9787       char *msg = malloc(len);\r
9788       int dbgchk;\r
9789 \r
9790       if (!msg)\r
9791         return no_wrap(message, count);\r
9792       else\r
9793       {\r
9794         dbgchk = wrap(msg, message, count, width, &line);\r
9795         if (dbgchk != len && appData.debugMode)\r
9796             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9797         ConsoleOutput(msg, len, FALSE);\r
9798         free(msg);\r
9799         return len;\r
9800       }\r
9801     }\r
9802   }\r
9803 \r
9804   if (ovl.hEvent == NULL) {\r
9805     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9806   }\r
9807   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9808 \r
9809   switch (cp->kind) {\r
9810   case CPSock:\r
9811   case CPRcmd:\r
9812     outCount = send(cp->sock, message, count, 0);\r
9813     if (outCount == SOCKET_ERROR) {\r
9814       *outError = WSAGetLastError();\r
9815     } else {\r
9816       *outError = NO_ERROR;\r
9817     }\r
9818     break;\r
9819 \r
9820   case CPReal:\r
9821     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9822                   &dOutCount, NULL)) {\r
9823       *outError = NO_ERROR;\r
9824       outCount = (int) dOutCount;\r
9825     } else {\r
9826       *outError = GetLastError();\r
9827     }\r
9828     break;\r
9829 \r
9830   case CPComm:\r
9831     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9832                             &dOutCount, &ovl);\r
9833     if (*outError == NO_ERROR) {\r
9834       outCount = (int) dOutCount;\r
9835     }\r
9836     break;\r
9837   }\r
9838   return outCount;\r
9839 }\r
9840 \r
9841 void\r
9842 DoSleep(int n)\r
9843 {\r
9844     if(n != 0) Sleep(n);\r
9845 }\r
9846 \r
9847 int\r
9848 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9849                        long msdelay)\r
9850 {\r
9851   /* Ignore delay, not implemented for WinBoard */\r
9852   return OutputToProcess(pr, message, count, outError);\r
9853 }\r
9854 \r
9855 \r
9856 void\r
9857 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9858                         char *buf, int count, int error)\r
9859 {\r
9860   DisplayFatalError(_("Not implemented"), 0, 1);\r
9861 }\r
9862 \r
9863 /* see wgamelist.c for Game List functions */\r
9864 /* see wedittags.c for Edit Tags functions */\r
9865 \r
9866 \r
9867 int\r
9868 ICSInitScript()\r
9869 {\r
9870   FILE *f;\r
9871   char buf[MSG_SIZ];\r
9872   char *dummy;\r
9873 \r
9874   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9875     f = fopen(buf, "r");\r
9876     if (f != NULL) {\r
9877       ProcessICSInitScript(f);\r
9878       fclose(f);\r
9879       return TRUE;\r
9880     }\r
9881   }\r
9882   return FALSE;\r
9883 }\r
9884 \r
9885 \r
9886 VOID\r
9887 StartAnalysisClock()\r
9888 {\r
9889   if (analysisTimerEvent) return;\r
9890   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9891                                         (UINT) 2000, NULL);\r
9892 }\r
9893 \r
9894 VOID\r
9895 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9896 {\r
9897   highlightInfo.sq[0].x = fromX;\r
9898   highlightInfo.sq[0].y = fromY;\r
9899   highlightInfo.sq[1].x = toX;\r
9900   highlightInfo.sq[1].y = toY;\r
9901 }\r
9902 \r
9903 VOID\r
9904 ClearHighlights()\r
9905 {\r
9906   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9907     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9908 }\r
9909 \r
9910 VOID\r
9911 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9912 {\r
9913   premoveHighlightInfo.sq[0].x = fromX;\r
9914   premoveHighlightInfo.sq[0].y = fromY;\r
9915   premoveHighlightInfo.sq[1].x = toX;\r
9916   premoveHighlightInfo.sq[1].y = toY;\r
9917 }\r
9918 \r
9919 VOID\r
9920 ClearPremoveHighlights()\r
9921 {\r
9922   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9923     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9924 }\r
9925 \r
9926 VOID\r
9927 ShutDownFrontEnd()\r
9928 {\r
9929   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9930   DeleteClipboardTempFiles();\r
9931 }\r
9932 \r
9933 void\r
9934 BoardToTop()\r
9935 {\r
9936     if (IsIconic(hwndMain))\r
9937       ShowWindow(hwndMain, SW_RESTORE);\r
9938 \r
9939     SetActiveWindow(hwndMain);\r
9940 }\r
9941 \r
9942 /*\r
9943  * Prototypes for animation support routines\r
9944  */\r
9945 static void ScreenSquare(int column, int row, POINT * pt);\r
9946 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9947      POINT frames[], int * nFrames);\r
9948 \r
9949 \r
9950 #define kFactor 4\r
9951 \r
9952 void\r
9953 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9954 {       // [HGM] atomic: animate blast wave\r
9955         int i;\r
9956 \r
9957         explodeInfo.fromX = fromX;\r
9958         explodeInfo.fromY = fromY;\r
9959         explodeInfo.toX = toX;\r
9960         explodeInfo.toY = toY;\r
9961         for(i=1; i<4*kFactor; i++) {\r
9962             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9963             DrawPosition(FALSE, board);\r
9964             Sleep(appData.animSpeed);\r
9965         }\r
9966         explodeInfo.radius = 0;\r
9967         DrawPosition(TRUE, board);\r
9968 }\r
9969 \r
9970 void\r
9971 AnimateMove(board, fromX, fromY, toX, toY)\r
9972      Board board;\r
9973      int fromX;\r
9974      int fromY;\r
9975      int toX;\r
9976      int toY;\r
9977 {\r
9978   ChessSquare piece;\r
9979   int x = toX, y = toY;\r
9980   POINT start, finish, mid;\r
9981   POINT frames[kFactor * 2 + 1];\r
9982   int nFrames, n;\r
9983 \r
9984   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9985 \r
9986   if (!appData.animate) return;\r
9987   if (doingSizing) return;\r
9988   if (fromY < 0 || fromX < 0) return;\r
9989   piece = board[fromY][fromX];\r
9990   if (piece >= EmptySquare) return;\r
9991 \r
9992   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
9993 \r
9994 again:\r
9995 \r
9996   ScreenSquare(fromX, fromY, &start);\r
9997   ScreenSquare(toX, toY, &finish);\r
9998 \r
9999   /* All moves except knight jumps move in straight line */\r
10000   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10001     mid.x = start.x + (finish.x - start.x) / 2;\r
10002     mid.y = start.y + (finish.y - start.y) / 2;\r
10003   } else {\r
10004     /* Knight: make straight movement then diagonal */\r
10005     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10006        mid.x = start.x + (finish.x - start.x) / 2;\r
10007        mid.y = start.y;\r
10008      } else {\r
10009        mid.x = start.x;\r
10010        mid.y = start.y + (finish.y - start.y) / 2;\r
10011      }\r
10012   }\r
10013   \r
10014   /* Don't use as many frames for very short moves */\r
10015   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10016     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10017   else\r
10018     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10019 \r
10020   animInfo.from.x = fromX;\r
10021   animInfo.from.y = fromY;\r
10022   animInfo.to.x = toX;\r
10023   animInfo.to.y = toY;\r
10024   animInfo.lastpos = start;\r
10025   animInfo.piece = piece;\r
10026   for (n = 0; n < nFrames; n++) {\r
10027     animInfo.pos = frames[n];\r
10028     DrawPosition(FALSE, NULL);\r
10029     animInfo.lastpos = animInfo.pos;\r
10030     Sleep(appData.animSpeed);\r
10031   }\r
10032   animInfo.pos = finish;\r
10033   DrawPosition(FALSE, NULL);\r
10034 \r
10035   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10036 \r
10037   animInfo.piece = EmptySquare;\r
10038   Explode(board, fromX, fromY, toX, toY);\r
10039 }\r
10040 \r
10041 /*      Convert board position to corner of screen rect and color       */\r
10042 \r
10043 static void\r
10044 ScreenSquare(column, row, pt)\r
10045      int column; int row; POINT * pt;\r
10046 {\r
10047   if (flipView) {\r
10048     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10049     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10050   } else {\r
10051     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10052     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10053   }\r
10054 }\r
10055 \r
10056 /*      Generate a series of frame coords from start->mid->finish.\r
10057         The movement rate doubles until the half way point is\r
10058         reached, then halves back down to the final destination,\r
10059         which gives a nice slow in/out effect. The algorithmn\r
10060         may seem to generate too many intermediates for short\r
10061         moves, but remember that the purpose is to attract the\r
10062         viewers attention to the piece about to be moved and\r
10063         then to where it ends up. Too few frames would be less\r
10064         noticeable.                                             */\r
10065 \r
10066 static void\r
10067 Tween(start, mid, finish, factor, frames, nFrames)\r
10068      POINT * start; POINT * mid;\r
10069      POINT * finish; int factor;\r
10070      POINT frames[]; int * nFrames;\r
10071 {\r
10072   int n, fraction = 1, count = 0;\r
10073 \r
10074   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10075   for (n = 0; n < factor; n++)\r
10076     fraction *= 2;\r
10077   for (n = 0; n < factor; n++) {\r
10078     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10079     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10080     count ++;\r
10081     fraction = fraction / 2;\r
10082   }\r
10083   \r
10084   /* Midpoint */\r
10085   frames[count] = *mid;\r
10086   count ++;\r
10087   \r
10088   /* Slow out, stepping 1/2, then 1/4, ... */\r
10089   fraction = 2;\r
10090   for (n = 0; n < factor; n++) {\r
10091     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10092     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10093     count ++;\r
10094     fraction = fraction * 2;\r
10095   }\r
10096   *nFrames = count;\r
10097 }\r
10098 \r
10099 void\r
10100 SettingsPopUp(ChessProgramState *cps)\r
10101 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10102       EngineOptionsPopup(savedHwnd, cps);\r
10103 }\r
10104 \r
10105 int flock(int fid, int code)\r
10106 {\r
10107     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10108     OVERLAPPED ov;\r
10109     ov.hEvent = NULL;\r
10110     ov.Offset = 0;\r
10111     ov.OffsetHigh = 0;\r
10112     switch(code) {\r
10113       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10114 \r
10115       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10116       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10117       default: return -1;\r
10118     }\r
10119     return 0;\r
10120 }\r
10121 \r
10122 char *\r
10123 Col2Text (int n)\r
10124 {\r
10125     static int i=0;\r
10126     static char col[8][20];\r
10127     COLORREF color = *(COLORREF *) colorVariable[n];\r
10128     i = i+1 & 7;\r
10129     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10130     return col[i];\r
10131 }\r
10132 \r
10133 void\r
10134 ActivateTheme (int new)\r
10135 {   // Redo initialization of features depending on options that can occur in themes\r
10136    InitTextures();\r
10137    if(new) InitDrawingColors();\r
10138    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10139    InitDrawingSizes(boardSize, 0);\r
10140    InvalidateRect(hwndMain, NULL, TRUE);\r
10141 }\r