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