Provide DoEvents function in front-ends
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts.\r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 #define SLASH '/'\r
96 #define DATADIR "~~"\r
97 \r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
99 \r
100   int myrandom(void);\r
101   void mysrandom(unsigned int seed);\r
102 \r
103 extern int whiteFlag, blackFlag;\r
104 Boolean flipClock = FALSE;\r
105 extern HANDLE chatHandle[];\r
106 extern enum ICS_TYPE ics_type;\r
107 \r
108 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
109 int  MyGetFullPathName P((char *name, char *fullname));\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
111 VOID NewVariantPopup(HWND hwnd);\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
113                    /*char*/int promoChar));\r
114 void DisplayMove P((int moveNumber));\r
115 void ChatPopUp P((char *s));\r
116 typedef struct {\r
117   ChessSquare piece;  \r
118   POINT pos;      /* window coordinates of current pos */\r
119   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
120   POINT from;     /* board coordinates of the piece's orig pos */\r
121   POINT to;       /* board coordinates of the piece's new pos */\r
122 } AnimInfo;\r
123 \r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
125 \r
126 typedef struct {\r
127   POINT start;    /* window coordinates of start pos */\r
128   POINT pos;      /* window coordinates of current pos */\r
129   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
130   POINT from;     /* board coordinates of the piece's orig pos */\r
131   ChessSquare piece;\r
132 } DragInfo;\r
133 \r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
135 \r
136 typedef struct {\r
137   POINT sq[2];    /* board coordinates of from, to squares */\r
138 } HighlightInfo;\r
139 \r
140 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
144 \r
145 typedef struct { // [HGM] atomic\r
146   int fromX, fromY, toX, toY, radius;\r
147 } ExplodeInfo;\r
148 \r
149 static ExplodeInfo explodeInfo;\r
150 \r
151 /* Window class names */\r
152 char szAppName[] = "WinBoard";\r
153 char szConsoleName[] = "WBConsole";\r
154 \r
155 /* Title bar text */\r
156 char szTitle[] = "WinBoard";\r
157 char szConsoleTitle[] = "I C S Interaction";\r
158 \r
159 char *programName;\r
160 char *settingsFileName;\r
161 Boolean saveSettingsOnExit;\r
162 char installDir[MSG_SIZ];\r
163 int errorExitStatus;\r
164 \r
165 BoardSize boardSize;\r
166 Boolean chessProgram;\r
167 //static int boardX, boardY;\r
168 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
169 int squareSize, lineGap, minorSize, border;\r
170 static int winW, winH;\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
172 static int logoHeight = 0;\r
173 static char messageText[MESSAGE_TEXT_MAX];\r
174 static int clockTimerEvent = 0;\r
175 static int loadGameTimerEvent = 0;\r
176 static int analysisTimerEvent = 0;\r
177 static DelayedEventCallback delayedTimerCallback;\r
178 static int delayedTimerEvent = 0;\r
179 static int buttonCount = 2;\r
180 char *icsTextMenuString;\r
181 char *icsNames;\r
182 char *firstChessProgramNames;\r
183 char *secondChessProgramNames;\r
184 \r
185 #define PALETTESIZE 256\r
186 \r
187 HINSTANCE hInst;          /* current instance */\r
188 Boolean alwaysOnTop = FALSE;\r
189 RECT boardRect;\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
191   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
193 HPALETTE hPal;\r
194 ColorClass currentColorClass;\r
195 \r
196 static HWND savedHwnd;\r
197 HWND hCommPort = NULL;    /* currently open comm port */\r
198 static HWND hwndPause;    /* pause button */\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,\r
201   blackSquareBrush, /* [HGM] for band between board and holdings */\r
202   explodeBrush,     /* [HGM] atomic */\r
203   markerBrush[8],   /* [HGM] markers */\r
204   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
207 static HPEN gridPen = NULL;\r
208 static HPEN highlightPen = NULL;\r
209 static HPEN premovePen = NULL;\r
210 static NPLOGPALETTE pLogPal;\r
211 static BOOL paletteChanged = FALSE;\r
212 static HICON iconWhite, iconBlack, iconCurrent;\r
213 static int doingSizing = FALSE;\r
214 static int lastSizing = 0;\r
215 static int prevStderrPort;\r
216 static HBITMAP userLogo;\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 \r
229 #if defined(_winmajor)\r
230 #define oldDialog (_winmajor < 4)\r
231 #else\r
232 #define oldDialog 0\r
233 #endif\r
234 #endif\r
235 \r
236 #define INTERNATIONAL\r
237 \r
238 #ifdef INTERNATIONAL\r
239 #  define _(s) T_(s)\r
240 #  define N_(s) s\r
241 #else\r
242 #  define _(s) s\r
243 #  define N_(s) s\r
244 #  define T_(s) s\r
245 #  define Translate(x, y)\r
246 #  define LoadLanguageFile(s)\r
247 #endif\r
248 \r
249 #ifdef INTERNATIONAL\r
250 \r
251 Boolean barbaric; // flag indicating if translation is needed\r
252 \r
253 // list of item numbers used in each dialog (used to alter language at run time)\r
254 \r
255 #define ABOUTBOX -1  /* not sure why these are needed */\r
256 #define ABOUTBOX2 -1\r
257 \r
258 int dialogItems[][42] = {\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
261   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
263   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
264   OPT_Ranget, IDOK, IDCANCEL }, \r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
266   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
269   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
272   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
273 { ABOUTBOX2, IDC_ChessBoard }, \r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
275   OPT_GameListClose, IDC_GameListDoFilter }, \r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
277 { DLG_Error, IDOK }, \r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
279   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
282   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
283   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
284 { DLG_IndexNumber, IDC_Index }, \r
285 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
286 { DLG_TypeInName, IDOK, IDCANCEL }, \r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
288   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
290   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
291   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
292   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
293   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
294   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
295   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
297   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
298   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
299   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
300   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
301   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
302   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
303   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
304   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
306   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
307   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
308   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
309   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
310   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
311   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
312   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
314   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
315   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
316   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
317   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
318   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
319   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
320   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
321   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
323   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
324   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
325   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
326   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
329   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
330 { DLG_MoveHistory }, \r
331 { DLG_EvalGraph }, \r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
335   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
336   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
337   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
339   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
340   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
341 { 0 }\r
342 };\r
343 \r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
345 static int lastChecked;\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
347 extern int tinyLayout;\r
348 extern char * menuBarText[][10];\r
349 \r
350 void\r
351 LoadLanguageFile(char *name)\r
352 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
353     FILE *f;\r
354     int i=0, j=0, n=0, k;\r
355     char buf[MSG_SIZ];\r
356 \r
357     if(!name || name[0] == NULLCHAR) return;\r
358       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
359     appData.language = oldLanguage;\r
360     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
361     if((f = fopen(buf, "r")) == NULL) return;\r
362     while((k = fgetc(f)) != EOF) {\r
363         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
364         languageBuf[i] = k;\r
365         if(k == '\n') {\r
366             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
367                 char *p;\r
368                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
369                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
370                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
371                         english[j] = languageBuf + n + 1; *p = 0;\r
372                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
374                     }\r
375                 }\r
376             }\r
377             n = i + 1;\r
378         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
379             switch(k) {\r
380               case 'n': k = '\n'; break;\r
381               case 'r': k = '\r'; break;\r
382               case 't': k = '\t'; break;\r
383             }\r
384             languageBuf[--i] = k;\r
385         }\r
386         i++;\r
387     }\r
388     fclose(f);\r
389     barbaric = (j != 0);\r
390     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
391 }\r
392 \r
393 char *\r
394 T_(char *s)\r
395 {   // return the translation of the given string\r
396     // efficiency can be improved a lot...\r
397     int i=0;\r
398     static char buf[MSG_SIZ];\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
400     if(!barbaric) return s;\r
401     if(!s) return ""; // sanity\r
402     while(english[i]) {\r
403         if(!strcmp(s, english[i])) return foreign[i];\r
404         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
405             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
406             return buf;\r
407         }\r
408         i++;\r
409     }\r
410     return s;\r
411 }\r
412 \r
413 void\r
414 Translate(HWND hDlg, int dialogID)\r
415 {   // translate all text items in the given dialog\r
416     int i=0, j, k;\r
417     char buf[MSG_SIZ], *s;\r
418     if(!barbaric) return;\r
419     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
420     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
421     GetWindowText( hDlg, buf, MSG_SIZ );\r
422     s = T_(buf);\r
423     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
424     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
425         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
426         if(strlen(buf) == 0) continue;\r
427         s = T_(buf);\r
428         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
429     }\r
430 }\r
431 \r
432 HMENU\r
433 TranslateOneMenu(int i, HMENU subMenu)\r
434 {\r
435     int j;\r
436     static MENUITEMINFO info;\r
437 \r
438     info.cbSize = sizeof(MENUITEMINFO);\r
439     info.fMask = MIIM_STATE | MIIM_TYPE;\r
440           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
441             char buf[MSG_SIZ];\r
442             info.dwTypeData = buf;\r
443             info.cch = sizeof(buf);\r
444             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
445             if(i < 10) {\r
446                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
447                 else menuText[i][j] = strdup(buf); // remember original on first change\r
448             }\r
449             if(buf[0] == NULLCHAR) continue;\r
450             info.dwTypeData = T_(buf);\r
451             info.cch = strlen(buf)+1;\r
452             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
453           }\r
454     return subMenu;\r
455 }\r
456 \r
457 void\r
458 TranslateMenus(int addLanguage)\r
459 {\r
460     int i;\r
461     WIN32_FIND_DATA fileData;\r
462     HANDLE hFind;\r
463 #define IDM_English 1970\r
464     if(1) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
467           HMENU subMenu = GetSubMenu(mainMenu, i);\r
468           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
469                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
470           TranslateOneMenu(i, subMenu);\r
471         }\r
472         DrawMenuBar(hwndMain);\r
473     }\r
474 \r
475     if(!addLanguage) return;\r
476     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
477         HMENU mainMenu = GetMenu(hwndMain);\r
478         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
479         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
480         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
481         i = 0; lastChecked = IDM_English;\r
482         do {\r
483             char *p, *q = fileData.cFileName;\r
484             int checkFlag = MF_UNCHECKED;\r
485             languageFile[i] = strdup(q);\r
486             if(barbaric && !strcmp(oldLanguage, q)) {\r
487                 checkFlag = MF_CHECKED;\r
488                 lastChecked = IDM_English + i + 1;\r
489                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
490             }\r
491             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
492             p = strstr(fileData.cFileName, ".lng");\r
493             if(p) *p = 0;\r
494             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
495         } while(FindNextFile(hFind, &fileData));\r
496         FindClose(hFind);\r
497     }\r
498 }\r
499 \r
500 #endif\r
501 \r
502 #define IDM_RecentEngines 3000\r
503 \r
504 void\r
505 RecentEngineMenu (char *s)\r
506 {\r
507     if(appData.icsActive) return;\r
508     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
509         HMENU mainMenu = GetMenu(hwndMain);\r
510         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
511         int i=IDM_RecentEngines;\r
512         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
513         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
514         while(*s) {\r
515           char *p = strchr(s, '\n');\r
516           if(p == NULL) return; // malformed!\r
517           *p = NULLCHAR;\r
518           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
519           *p = '\n';\r
520           s = p+1;\r
521         }\r
522     }\r
523 }\r
524 \r
525 \r
526 typedef struct {\r
527   char *name;\r
528   int squareSize;\r
529   int lineGap;\r
530   int smallLayout;\r
531   int tinyLayout;\r
532   int cliWidth, cliHeight;\r
533 } SizeInfo;\r
534 \r
535 SizeInfo sizeInfo[] = \r
536 {\r
537   { "tiny",     21, 0, 1, 1, 0, 0 },\r
538   { "teeny",    25, 1, 1, 1, 0, 0 },\r
539   { "dinky",    29, 1, 1, 1, 0, 0 },\r
540   { "petite",   33, 1, 1, 1, 0, 0 },\r
541   { "slim",     37, 2, 1, 0, 0, 0 },\r
542   { "small",    40, 2, 1, 0, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
574   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
575   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
576   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
577   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
578   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
607 };\r
608 \r
609 \r
610 MySound sounds[(int)NSoundClasses];\r
611 MyTextAttribs textAttribs[(int)NColorClasses];\r
612 \r
613 MyColorizeAttribs colorizeAttribs[] = {\r
614   { (COLORREF)0, 0, N_("Shout Text") },\r
615   { (COLORREF)0, 0, N_("SShout/CShout") },\r
616   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
617   { (COLORREF)0, 0, N_("Channel Text") },\r
618   { (COLORREF)0, 0, N_("Kibitz Text") },\r
619   { (COLORREF)0, 0, N_("Tell Text") },\r
620   { (COLORREF)0, 0, N_("Challenge Text") },\r
621   { (COLORREF)0, 0, N_("Request Text") },\r
622   { (COLORREF)0, 0, N_("Seek Text") },\r
623   { (COLORREF)0, 0, N_("Normal Text") },\r
624   { (COLORREF)0, 0, N_("None") }\r
625 };\r
626 \r
627 \r
628 \r
629 static char *commentTitle;\r
630 static char *commentText;\r
631 static int commentIndex;\r
632 static Boolean editComment = FALSE;\r
633 \r
634 \r
635 char errorTitle[MSG_SIZ];\r
636 char errorMessage[2*MSG_SIZ];\r
637 HWND errorDialog = NULL;\r
638 BOOLEAN moveErrorMessageUp = FALSE;\r
639 BOOLEAN consoleEcho = TRUE;\r
640 CHARFORMAT consoleCF;\r
641 COLORREF consoleBackgroundColor;\r
642 \r
643 char *programVersion;\r
644 \r
645 #define CPReal 1\r
646 #define CPComm 2\r
647 #define CPSock 3\r
648 #define CPRcmd 4\r
649 typedef int CPKind;\r
650 \r
651 typedef struct {\r
652   CPKind kind;\r
653   HANDLE hProcess;\r
654   DWORD pid;\r
655   HANDLE hTo;\r
656   HANDLE hFrom;\r
657   SOCKET sock;\r
658   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
659 } ChildProc;\r
660 \r
661 #define INPUT_SOURCE_BUF_SIZE 4096\r
662 \r
663 typedef struct _InputSource {\r
664   CPKind kind;\r
665   HANDLE hFile;\r
666   SOCKET sock;\r
667   int lineByLine;\r
668   HANDLE hThread;\r
669   DWORD id;\r
670   char buf[INPUT_SOURCE_BUF_SIZE];\r
671   char *next;\r
672   DWORD count;\r
673   int error;\r
674   InputCallback func;\r
675   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
676   VOIDSTAR closure;\r
677 } InputSource;\r
678 \r
679 InputSource *consoleInputSource;\r
680 \r
681 DCB dcb;\r
682 \r
683 /* forward */\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
685 VOID ConsoleCreate();\r
686 LRESULT CALLBACK\r
687   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);\r
691 LRESULT CALLBACK\r
692   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
694 void ParseIcsTextMenu(char *icsTextMenuString);\r
695 VOID PopUpNameDialog(char firstchar);\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
697 \r
698 /* [AS] */\r
699 int NewGameFRC();\r
700 int GameListOptions();\r
701 \r
702 int dummy; // [HGM] for obsolete args\r
703 \r
704 HWND hwndMain = NULL;        /* root window*/\r
705 HWND hwndConsole = NULL;\r
706 HWND commentDialog = NULL;\r
707 HWND moveHistoryDialog = NULL;\r
708 HWND evalGraphDialog = NULL;\r
709 HWND engineOutputDialog = NULL;\r
710 HWND gameListDialog = NULL;\r
711 HWND editTagsDialog = NULL;\r
712 \r
713 int commentUp = FALSE;\r
714 \r
715 WindowPlacement wpMain;\r
716 WindowPlacement wpConsole;\r
717 WindowPlacement wpComment;\r
718 WindowPlacement wpMoveHistory;\r
719 WindowPlacement wpEvalGraph;\r
720 WindowPlacement wpEngineOutput;\r
721 WindowPlacement wpGameList;\r
722 WindowPlacement wpTags;\r
723 \r
724 VOID EngineOptionsPopup(); // [HGM] settings\r
725 \r
726 VOID GothicPopUp(char *title, VariantClass variant);\r
727 /*\r
728  * Setting "frozen" should disable all user input other than deleting\r
729  * the window.  We do this while engines are initializing themselves.\r
730  */\r
731 static int frozen = 0;\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];\r
733 void FreezeUI()\r
734 {\r
735   HMENU hmenu;\r
736   int i;\r
737 \r
738   if (frozen) return;\r
739   frozen = 1;\r
740   hmenu = GetMenu(hwndMain);\r
741   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
742     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
743   }\r
744   DrawMenuBar(hwndMain);\r
745 }\r
746 \r
747 /* Undo a FreezeUI */\r
748 void ThawUI()\r
749 {\r
750   HMENU hmenu;\r
751   int i;\r
752 \r
753   if (!frozen) return;\r
754   frozen = 0;\r
755   hmenu = GetMenu(hwndMain);\r
756   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
757     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
758   }\r
759   DrawMenuBar(hwndMain);\r
760 }\r
761 \r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
763 \r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
765 #ifdef JAWS\r
766 #include "jaws.c"\r
767 #else\r
768 #define JAWS_INIT\r
769 #define JAWS_ARGS\r
770 #define JAWS_ALT_INTERCEPT\r
771 #define JAWS_KBUP_NAVIGATION\r
772 #define JAWS_KBDOWN_NAVIGATION\r
773 #define JAWS_MENU_ITEMS\r
774 #define JAWS_SILENCE\r
775 #define JAWS_REPLAY\r
776 #define JAWS_ACCEL\r
777 #define JAWS_COPYRIGHT\r
778 #define JAWS_DELETE(X) X\r
779 #define SAYMACHINEMOVE()\r
780 #define SAY(X)\r
781 #endif\r
782 \r
783 /*---------------------------------------------------------------------------*\\r
784  *\r
785  * WinMain\r
786  *\r
787 \*---------------------------------------------------------------------------*/\r
788 \r
789 static void HandleMessage P((MSG *message));\r
790 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
791 \r
792 int APIENTRY\r
793 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
794         LPSTR lpCmdLine, int nCmdShow)\r
795 {\r
796   MSG msg;\r
797 //  INITCOMMONCONTROLSEX ex;\r
798 \r
799   debugFP = stderr;\r
800 \r
801   LoadLibrary("RICHED32.DLL");\r
802   consoleCF.cbSize = sizeof(CHARFORMAT);\r
803 \r
804   if (!InitApplication(hInstance)) {\r
805     return (FALSE);\r
806   }\r
807   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
808     return (FALSE);\r
809   }\r
810 \r
811   JAWS_INIT\r
812   TranslateMenus(1);\r
813 \r
814 //  InitCommonControlsEx(&ex);\r
815   InitCommonControls();\r
816 \r
817   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
818   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
819   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
820 \r
821   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
822 \r
823   while (GetMessage(&msg, /* message structure */\r
824                     NULL, /* handle of window receiving the message */\r
825                     0,    /* lowest message to examine */\r
826                     0))   /* highest message to examine */\r
827     {\r
828         HandleMessage(msg);\r
829     }\r
830 \r
831 \r
832   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
833 }\r
834 \r
835 static void\r
836 HandleMessage (MSG *message)\r
837 {\r
838     MSG msg = *message;\r
839 \r
840       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
841         // [HGM] navigate: switch between all windows with tab\r
842         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
843         int i, currentElement = 0;\r
844 \r
845         // first determine what element of the chain we come from (if any)\r
846         if(appData.icsActive) {\r
847             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
848             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
849         }\r
850         if(engineOutputDialog && EngineOutputIsUp()) {\r
851             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
852             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
853         }\r
854         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
855             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
856         }\r
857         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
858         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
859         if(msg.hwnd == e1)                 currentElement = 2; else\r
860         if(msg.hwnd == e2)                 currentElement = 3; else\r
861         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
862         if(msg.hwnd == mh)                currentElement = 4; else\r
863         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
864         if(msg.hwnd == hText)  currentElement = 5; else\r
865         if(msg.hwnd == hInput) currentElement = 6; else\r
866         for (i = 0; i < N_BUTTONS; i++) {\r
867             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
868         }\r
869 \r
870         // determine where to go to\r
871         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
872           do {\r
873             currentElement = (currentElement + direction) % 7;\r
874             switch(currentElement) {\r
875                 case 0:\r
876                   h = hwndMain; break; // passing this case always makes the loop exit\r
877                 case 1:\r
878                   h = buttonDesc[0].hwnd; break; // could be NULL\r
879                 case 2:\r
880                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
881                   h = e1; break;\r
882                 case 3:\r
883                   if(!EngineOutputIsUp()) continue;\r
884                   h = e2; break;\r
885                 case 4:\r
886                   if(!MoveHistoryIsUp()) continue;\r
887                   h = mh; break;\r
888 //              case 6: // input to eval graph does not seem to get here!\r
889 //                if(!EvalGraphIsUp()) continue;\r
890 //                h = evalGraphDialog; break;\r
891                 case 5:\r
892                   if(!appData.icsActive) continue;\r
893                   SAY("display");\r
894                   h = hText; break;\r
895                 case 6:\r
896                   if(!appData.icsActive) continue;\r
897                   SAY("input");\r
898                   h = hInput; break;\r
899             }\r
900           } while(h == 0);\r
901 \r
902           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
903           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
904           SetFocus(h);\r
905 \r
906           return; // this message now has been processed\r
907         }\r
908       }\r
909 \r
910       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
911           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
912           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
913           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
914           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
915           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
916           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
917           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
918           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
919           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
920         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
921         for(i=0; i<MAX_CHAT; i++) \r
922             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
923                 done = 1; break;\r
924         }\r
925         if(done) return; // [HGM] chat: end patch\r
926         TranslateMessage(&msg); /* Translates virtual key codes */\r
927         DispatchMessage(&msg);  /* Dispatches message to window */\r
928       }\r
929 }\r
930 \r
931 void\r
932 DoEvents ()\r
933 { /* Dispatch pending messages */\r
934   MSG msg;\r
935   while (PeekMessage(&msg, /* message structure */\r
936                      NULL, /* handle of window receiving the message */\r
937                      0,    /* lowest message to examine */\r
938                      0,    /* highest message to examine */\r
939                      PM_REMOVE))\r
940     {\r
941         HandleMessage(msg);\r
942     }\r
943 }\r
944 \r
945 /*---------------------------------------------------------------------------*\\r
946  *\r
947  * Initialization functions\r
948  *\r
949 \*---------------------------------------------------------------------------*/\r
950 \r
951 void\r
952 SetUserLogo()\r
953 {   // update user logo if necessary\r
954     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
955 \r
956     if(appData.autoLogo) {\r
957           curName = UserName();\r
958           if(strcmp(curName, oldUserName)) {\r
959                 GetCurrentDirectory(MSG_SIZ, dir);\r
960                 SetCurrentDirectory(installDir);\r
961                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
962                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
963                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
964                 if(userLogo == NULL)\r
965                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
966                 SetCurrentDirectory(dir); /* return to prev directory */\r
967           }\r
968     }\r
969 }\r
970 \r
971 BOOL\r
972 InitApplication(HINSTANCE hInstance)\r
973 {\r
974   WNDCLASS wc;\r
975 \r
976   /* Fill in window class structure with parameters that describe the */\r
977   /* main window. */\r
978 \r
979   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
980   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
981   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
982   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
983   wc.hInstance     = hInstance;         /* Owner of this class */\r
984   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
985   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
986   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
987   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
988   wc.lpszClassName = szAppName;                 /* Name to register as */\r
989 \r
990   /* Register the window class and return success/failure code. */\r
991   if (!RegisterClass(&wc)) return FALSE;\r
992 \r
993   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
994   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
995   wc.cbClsExtra    = 0;\r
996   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
997   wc.hInstance     = hInstance;\r
998   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
999   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1000   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1001   wc.lpszMenuName  = NULL;\r
1002   wc.lpszClassName = szConsoleName;\r
1003 \r
1004   if (!RegisterClass(&wc)) return FALSE;\r
1005   return TRUE;\r
1006 }\r
1007 \r
1008 \r
1009 /* Set by InitInstance, used by EnsureOnScreen */\r
1010 int screenHeight, screenWidth;\r
1011 RECT screenGeometry;\r
1012 \r
1013 void\r
1014 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1015 {\r
1016 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1017   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1018   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1019   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1020   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1021   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1022 }\r
1023 \r
1024 VOID\r
1025 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1026 {\r
1027   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1028   GetCurrentDirectory(MSG_SIZ, dir);\r
1029   SetCurrentDirectory(installDir);\r
1030   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1031       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1032 \r
1033       if (cps->programLogo == NULL && appData.debugMode) {\r
1034           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1035       }\r
1036   } else if(appData.autoLogo) {\r
1037       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1038         char *opponent = "";\r
1039         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1040         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1041         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1042         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1043             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1044             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1045         }\r
1046       } else\r
1047       if(appData.directory[n] && appData.directory[n][0]) {\r
1048         SetCurrentDirectory(appData.directory[n]);\r
1049         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1050       }\r
1051   }\r
1052   SetCurrentDirectory(dir); /* return to prev directory */\r
1053 }\r
1054 \r
1055 VOID\r
1056 InitTextures()\r
1057 {\r
1058   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1059   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1060   \r
1061   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1062       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1063       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1064       liteBackTextureMode = appData.liteBackTextureMode;\r
1065 \r
1066       if (liteBackTexture == NULL && appData.debugMode) {\r
1067           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1068       }\r
1069   }\r
1070   \r
1071   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1072       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1073       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1074       darkBackTextureMode = appData.darkBackTextureMode;\r
1075 \r
1076       if (darkBackTexture == NULL && appData.debugMode) {\r
1077           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1078       }\r
1079   }\r
1080 }\r
1081 \r
1082 #ifndef SM_CXVIRTUALSCREEN\r
1083 #define SM_CXVIRTUALSCREEN 78\r
1084 #endif\r
1085 #ifndef SM_CYVIRTUALSCREEN\r
1086 #define SM_CYVIRTUALSCREEN 79\r
1087 #endif\r
1088 #ifndef SM_XVIRTUALSCREEN \r
1089 #define SM_XVIRTUALSCREEN 76\r
1090 #endif\r
1091 #ifndef SM_YVIRTUALSCREEN \r
1092 #define SM_YVIRTUALSCREEN 77\r
1093 #endif\r
1094 \r
1095 VOID\r
1096 InitGeometry()\r
1097 {\r
1098   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1099   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1100   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1101   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1102   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1103   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1104   screenGeometry.right = screenGeometry.left + screenWidth;\r
1105   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1106 }\r
1107 \r
1108 BOOL\r
1109 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1110 {\r
1111   HWND hwnd; /* Main window handle. */\r
1112   int ibs;\r
1113   WINDOWPLACEMENT wp;\r
1114   char *filepart;\r
1115 \r
1116   hInst = hInstance;    /* Store instance handle in our global variable */\r
1117   programName = szAppName;\r
1118 \r
1119   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1120     *filepart = NULLCHAR;\r
1121     SetCurrentDirectory(installDir);\r
1122   } else {\r
1123     GetCurrentDirectory(MSG_SIZ, installDir);\r
1124   }\r
1125   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1126   InitGeometry();\r
1127   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1128   /* xboard, and older WinBoards, controlled the move sound with the\r
1129      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1130      always turn the option on (so that the backend will call us),\r
1131      then let the user turn the sound off by setting it to silence if\r
1132      desired.  To accommodate old winboard.ini files saved by old\r
1133      versions of WinBoard, we also turn off the sound if the option\r
1134      was initially set to false. [HGM] taken out of InitAppData */\r
1135   if (!appData.ringBellAfterMoves) {\r
1136     sounds[(int)SoundMove].name = strdup("");\r
1137     appData.ringBellAfterMoves = TRUE;\r
1138   }\r
1139   if (appData.debugMode) {\r
1140     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1141     setbuf(debugFP, NULL);\r
1142   }\r
1143 \r
1144   LoadLanguageFile(appData.language);\r
1145 \r
1146   InitBackEnd1();\r
1147 \r
1148 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1149 //  InitEngineUCI( installDir, &second );\r
1150 \r
1151   /* Create a main window for this application instance. */\r
1152   hwnd = CreateWindow(szAppName, szTitle,\r
1153                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1154                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1155                       NULL, NULL, hInstance, NULL);\r
1156   hwndMain = hwnd;\r
1157 \r
1158   /* If window could not be created, return "failure" */\r
1159   if (!hwnd) {\r
1160     return (FALSE);\r
1161   }\r
1162 \r
1163   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1164   LoadLogo(&first, 0, FALSE);\r
1165   LoadLogo(&second, 1, appData.icsActive);\r
1166 \r
1167   SetUserLogo();\r
1168 \r
1169   iconWhite = LoadIcon(hInstance, "icon_white");\r
1170   iconBlack = LoadIcon(hInstance, "icon_black");\r
1171   iconCurrent = iconWhite;\r
1172   InitDrawingColors();\r
1173 \r
1174   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1175   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1176     /* Compute window size for each board size, and use the largest\r
1177        size that fits on this screen as the default. */\r
1178     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1179     if (boardSize == (BoardSize)-1 &&\r
1180         winH <= screenHeight\r
1181            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1182         && winW <= screenWidth) {\r
1183       boardSize = (BoardSize)ibs;\r
1184     }\r
1185   }\r
1186 \r
1187   InitDrawingSizes(boardSize, 0);\r
1188   RecentEngineMenu(appData.recentEngineList);\r
1189   InitMenuChecks();\r
1190   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1191 \r
1192   /* [AS] Load textures if specified */\r
1193   InitTextures();\r
1194 \r
1195   mysrandom( (unsigned) time(NULL) );\r
1196 \r
1197   /* [AS] Restore layout */\r
1198   if( wpMoveHistory.visible ) {\r
1199       MoveHistoryPopUp();\r
1200   }\r
1201 \r
1202   if( wpEvalGraph.visible ) {\r
1203       EvalGraphPopUp();\r
1204   }\r
1205 \r
1206   if( wpEngineOutput.visible ) {\r
1207       EngineOutputPopUp();\r
1208   }\r
1209 \r
1210   /* Make the window visible; update its client area; and return "success" */\r
1211   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1212   wp.length = sizeof(WINDOWPLACEMENT);\r
1213   wp.flags = 0;\r
1214   wp.showCmd = nCmdShow;\r
1215   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1216   wp.rcNormalPosition.left = wpMain.x;\r
1217   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1218   wp.rcNormalPosition.top = wpMain.y;\r
1219   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1220   SetWindowPlacement(hwndMain, &wp);\r
1221 \r
1222   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1223 \r
1224   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1225                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1226 \r
1227   if (hwndConsole) {\r
1228 #if AOT_CONSOLE\r
1229     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1230                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1231 #endif\r
1232     ShowWindow(hwndConsole, nCmdShow);\r
1233     SetActiveWindow(hwndConsole);\r
1234   }\r
1235   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1236   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1237 \r
1238   return TRUE;\r
1239 \r
1240 }\r
1241 \r
1242 VOID\r
1243 InitMenuChecks()\r
1244 {\r
1245   HMENU hmenu = GetMenu(hwndMain);\r
1246 \r
1247   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1248                         MF_BYCOMMAND|((appData.icsActive &&\r
1249                                        *appData.icsCommPort != NULLCHAR) ?\r
1250                                       MF_ENABLED : MF_GRAYED));\r
1251   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1252                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1253                                      MF_CHECKED : MF_UNCHECKED));\r
1254   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1255 }\r
1256 \r
1257 //---------------------------------------------------------------------------------------------------------\r
1258 \r
1259 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1260 #define XBOARD FALSE\r
1261 \r
1262 #define OPTCHAR "/"\r
1263 #define SEPCHAR "="\r
1264 #define TOPLEVEL 0\r
1265 \r
1266 #include "args.h"\r
1267 \r
1268 // front-end part of option handling\r
1269 \r
1270 VOID\r
1271 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1272 {\r
1273   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1274   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1275   DeleteDC(hdc);\r
1276   lf->lfWidth = 0;\r
1277   lf->lfEscapement = 0;\r
1278   lf->lfOrientation = 0;\r
1279   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1280   lf->lfItalic = mfp->italic;\r
1281   lf->lfUnderline = mfp->underline;\r
1282   lf->lfStrikeOut = mfp->strikeout;\r
1283   lf->lfCharSet = mfp->charset;\r
1284   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1285   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1286   lf->lfQuality = DEFAULT_QUALITY;\r
1287   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1288     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1289 }\r
1290 \r
1291 void\r
1292 CreateFontInMF(MyFont *mf)\r
1293\r
1294   LFfromMFP(&mf->lf, &mf->mfp);\r
1295   if (mf->hf) DeleteObject(mf->hf);\r
1296   mf->hf = CreateFontIndirect(&mf->lf);\r
1297 }\r
1298 \r
1299 // [HGM] This platform-dependent table provides the location for storing the color info\r
1300 void *\r
1301 colorVariable[] = {\r
1302   &whitePieceColor, \r
1303   &blackPieceColor, \r
1304   &lightSquareColor,\r
1305   &darkSquareColor, \r
1306   &highlightSquareColor,\r
1307   &premoveHighlightColor,\r
1308   NULL,\r
1309   &consoleBackgroundColor,\r
1310   &appData.fontForeColorWhite,\r
1311   &appData.fontBackColorWhite,\r
1312   &appData.fontForeColorBlack,\r
1313   &appData.fontBackColorBlack,\r
1314   &appData.evalHistColorWhite,\r
1315   &appData.evalHistColorBlack,\r
1316   &appData.highlightArrowColor,\r
1317 };\r
1318 \r
1319 /* Command line font name parser.  NULL name means do nothing.\r
1320    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1321    For backward compatibility, syntax without the colon is also\r
1322    accepted, but font names with digits in them won't work in that case.\r
1323 */\r
1324 VOID\r
1325 ParseFontName(char *name, MyFontParams *mfp)\r
1326 {\r
1327   char *p, *q;\r
1328   if (name == NULL) return;\r
1329   p = name;\r
1330   q = strchr(p, ':');\r
1331   if (q) {\r
1332     if (q - p >= sizeof(mfp->faceName))\r
1333       ExitArgError(_("Font name too long:"), name, TRUE);\r
1334     memcpy(mfp->faceName, p, q - p);\r
1335     mfp->faceName[q - p] = NULLCHAR;\r
1336     p = q + 1;\r
1337   } else {\r
1338     q = mfp->faceName;\r
1339 \r
1340     while (*p && !isdigit(*p)) {\r
1341       *q++ = *p++;\r
1342       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1343         ExitArgError(_("Font name too long:"), name, TRUE);\r
1344     }\r
1345     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1346     *q = NULLCHAR;\r
1347   }\r
1348   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1349   mfp->pointSize = (float) atof(p);\r
1350   mfp->bold = (strchr(p, 'b') != NULL);\r
1351   mfp->italic = (strchr(p, 'i') != NULL);\r
1352   mfp->underline = (strchr(p, 'u') != NULL);\r
1353   mfp->strikeout = (strchr(p, 's') != NULL);\r
1354   mfp->charset = DEFAULT_CHARSET;\r
1355   q = strchr(p, 'c');\r
1356   if (q)\r
1357     mfp->charset = (BYTE) atoi(q+1);\r
1358 }\r
1359 \r
1360 void\r
1361 ParseFont(char *name, int number)\r
1362 { // wrapper to shield back-end from 'font'\r
1363   ParseFontName(name, &font[boardSize][number]->mfp);\r
1364 }\r
1365 \r
1366 void\r
1367 SetFontDefaults()\r
1368 { // in WB  we have a 2D array of fonts; this initializes their description\r
1369   int i, j;\r
1370   /* Point font array elements to structures and\r
1371      parse default font names */\r
1372   for (i=0; i<NUM_FONTS; i++) {\r
1373     for (j=0; j<NUM_SIZES; j++) {\r
1374       font[j][i] = &fontRec[j][i];\r
1375       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1376     }\r
1377   }\r
1378 }\r
1379 \r
1380 void\r
1381 CreateFonts()\r
1382 { // here we create the actual fonts from the selected descriptions\r
1383   int i, j;\r
1384   for (i=0; i<NUM_FONTS; i++) {\r
1385     for (j=0; j<NUM_SIZES; j++) {\r
1386       CreateFontInMF(font[j][i]);\r
1387     }\r
1388   }\r
1389 }\r
1390 /* Color name parser.\r
1391    X version accepts X color names, but this one\r
1392    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1393 COLORREF\r
1394 ParseColorName(char *name)\r
1395 {\r
1396   int red, green, blue, count;\r
1397   char buf[MSG_SIZ];\r
1398 \r
1399   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1400   if (count != 3) {\r
1401     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1402       &red, &green, &blue);\r
1403   }\r
1404   if (count != 3) {\r
1405     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1406     DisplayError(buf, 0);\r
1407     return RGB(0, 0, 0);\r
1408   }\r
1409   return PALETTERGB(red, green, blue);\r
1410 }\r
1411 \r
1412 void\r
1413 ParseColor(int n, char *name)\r
1414 { // for WinBoard the color is an int, which needs to be derived from the string\r
1415   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1416 }\r
1417 \r
1418 void\r
1419 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1420 {\r
1421   char *e = argValue;\r
1422   int eff = 0;\r
1423 \r
1424   while (*e) {\r
1425     if (*e == 'b')      eff |= CFE_BOLD;\r
1426     else if (*e == 'i') eff |= CFE_ITALIC;\r
1427     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1428     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1429     else if (*e == '#' || isdigit(*e)) break;\r
1430     e++;\r
1431   }\r
1432   *effects = eff;\r
1433   *color   = ParseColorName(e);\r
1434 }\r
1435 \r
1436 void\r
1437 ParseTextAttribs(ColorClass cc, char *s)\r
1438 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1439     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1440     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1441 }\r
1442 \r
1443 void\r
1444 ParseBoardSize(void *addr, char *name)\r
1445 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1446   BoardSize bs = SizeTiny;\r
1447   while (sizeInfo[bs].name != NULL) {\r
1448     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1449         *(BoardSize *)addr = bs;\r
1450         return;\r
1451     }\r
1452     bs++;\r
1453   }\r
1454   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1455 }\r
1456 \r
1457 void\r
1458 LoadAllSounds()\r
1459 { // [HGM] import name from appData first\r
1460   ColorClass cc;\r
1461   SoundClass sc;\r
1462   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1463     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1464     textAttribs[cc].sound.data = NULL;\r
1465     MyLoadSound(&textAttribs[cc].sound);\r
1466   }\r
1467   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1468     textAttribs[cc].sound.name = strdup("");\r
1469     textAttribs[cc].sound.data = NULL;\r
1470   }\r
1471   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1472     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1473     sounds[sc].data = NULL;\r
1474     MyLoadSound(&sounds[sc]);\r
1475   }\r
1476 }\r
1477 \r
1478 void\r
1479 SetCommPortDefaults()\r
1480 {\r
1481    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1482   dcb.DCBlength = sizeof(DCB);\r
1483   dcb.BaudRate = 9600;\r
1484   dcb.fBinary = TRUE;\r
1485   dcb.fParity = FALSE;\r
1486   dcb.fOutxCtsFlow = FALSE;\r
1487   dcb.fOutxDsrFlow = FALSE;\r
1488   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1489   dcb.fDsrSensitivity = FALSE;\r
1490   dcb.fTXContinueOnXoff = TRUE;\r
1491   dcb.fOutX = FALSE;\r
1492   dcb.fInX = FALSE;\r
1493   dcb.fNull = FALSE;\r
1494   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1495   dcb.fAbortOnError = FALSE;\r
1496   dcb.ByteSize = 7;\r
1497   dcb.Parity = SPACEPARITY;\r
1498   dcb.StopBits = ONESTOPBIT;\r
1499 }\r
1500 \r
1501 // [HGM] args: these three cases taken out to stay in front-end\r
1502 void\r
1503 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1504 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1505         // while the curent board size determines the element. This system should be ported to XBoard.\r
1506         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1507         int bs;\r
1508         for (bs=0; bs<NUM_SIZES; bs++) {\r
1509           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1510           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1511           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1512             ad->argName, mfp->faceName, mfp->pointSize,\r
1513             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1514             mfp->bold ? "b" : "",\r
1515             mfp->italic ? "i" : "",\r
1516             mfp->underline ? "u" : "",\r
1517             mfp->strikeout ? "s" : "",\r
1518             (int)mfp->charset);\r
1519         }\r
1520       }\r
1521 \r
1522 void\r
1523 ExportSounds()\r
1524 { // [HGM] copy the names from the internal WB variables to appData\r
1525   ColorClass cc;\r
1526   SoundClass sc;\r
1527   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1528     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1529   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1530     (&appData.soundMove)[sc] = sounds[sc].name;\r
1531 }\r
1532 \r
1533 void\r
1534 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1535 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1536         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1537         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1538           (ta->effects & CFE_BOLD) ? "b" : "",\r
1539           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1540           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1541           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1542           (ta->effects) ? " " : "",\r
1543           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1544       }\r
1545 \r
1546 void\r
1547 SaveColor(FILE *f, ArgDescriptor *ad)\r
1548 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1549         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1550         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1551           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1552 }\r
1553 \r
1554 void\r
1555 SaveBoardSize(FILE *f, char *name, void *addr)\r
1556 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1557   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1558 }\r
1559 \r
1560 void\r
1561 ParseCommPortSettings(char *s)\r
1562 { // wrapper to keep dcb from back-end\r
1563   ParseCommSettings(s, &dcb);\r
1564 }\r
1565 \r
1566 void\r
1567 GetWindowCoords()\r
1568 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1569   GetActualPlacement(hwndMain, &wpMain);\r
1570   GetActualPlacement(hwndConsole, &wpConsole);\r
1571   GetActualPlacement(commentDialog, &wpComment);\r
1572   GetActualPlacement(editTagsDialog, &wpTags);\r
1573   GetActualPlacement(gameListDialog, &wpGameList);\r
1574   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1575   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1576   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1577 }\r
1578 \r
1579 void\r
1580 PrintCommPortSettings(FILE *f, char *name)\r
1581 { // wrapper to shield back-end from DCB\r
1582       PrintCommSettings(f, name, &dcb);\r
1583 }\r
1584 \r
1585 int\r
1586 MySearchPath(char *installDir, char *name, char *fullname)\r
1587 {\r
1588   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1589   if(name[0]== '%') {\r
1590     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1591     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1592       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1593       *strchr(buf, '%') = 0;\r
1594       strcat(fullname, getenv(buf));\r
1595       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1596     }\r
1597     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1598     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1599     return (int) strlen(fullname);\r
1600   }\r
1601   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1602 }\r
1603 \r
1604 int\r
1605 MyGetFullPathName(char *name, char *fullname)\r
1606 {\r
1607   char *dummy;\r
1608   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1609 }\r
1610 \r
1611 int\r
1612 MainWindowUp()\r
1613 { // [HGM] args: allows testing if main window is realized from back-end\r
1614   return hwndMain != NULL;\r
1615 }\r
1616 \r
1617 void\r
1618 PopUpStartupDialog()\r
1619 {\r
1620     FARPROC lpProc;\r
1621     \r
1622     LoadLanguageFile(appData.language);\r
1623     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1624     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1625     FreeProcInstance(lpProc);\r
1626 }\r
1627 \r
1628 /*---------------------------------------------------------------------------*\\r
1629  *\r
1630  * GDI board drawing routines\r
1631  *\r
1632 \*---------------------------------------------------------------------------*/\r
1633 \r
1634 /* [AS] Draw square using background texture */\r
1635 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1636 {\r
1637     XFORM   x;\r
1638 \r
1639     if( mode == 0 ) {\r
1640         return; /* Should never happen! */\r
1641     }\r
1642 \r
1643     SetGraphicsMode( dst, GM_ADVANCED );\r
1644 \r
1645     switch( mode ) {\r
1646     case 1:\r
1647         /* Identity */\r
1648         break;\r
1649     case 2:\r
1650         /* X reflection */\r
1651         x.eM11 = -1.0;\r
1652         x.eM12 = 0;\r
1653         x.eM21 = 0;\r
1654         x.eM22 = 1.0;\r
1655         x.eDx = (FLOAT) dw + dx - 1;\r
1656         x.eDy = 0;\r
1657         dx = 0;\r
1658         SetWorldTransform( dst, &x );\r
1659         break;\r
1660     case 3:\r
1661         /* Y reflection */\r
1662         x.eM11 = 1.0;\r
1663         x.eM12 = 0;\r
1664         x.eM21 = 0;\r
1665         x.eM22 = -1.0;\r
1666         x.eDx = 0;\r
1667         x.eDy = (FLOAT) dh + dy - 1;\r
1668         dy = 0;\r
1669         SetWorldTransform( dst, &x );\r
1670         break;\r
1671     case 4:\r
1672         /* X/Y flip */\r
1673         x.eM11 = 0;\r
1674         x.eM12 = 1.0;\r
1675         x.eM21 = 1.0;\r
1676         x.eM22 = 0;\r
1677         x.eDx = (FLOAT) dx;\r
1678         x.eDy = (FLOAT) dy;\r
1679         dx = 0;\r
1680         dy = 0;\r
1681         SetWorldTransform( dst, &x );\r
1682         break;\r
1683     }\r
1684 \r
1685     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1686 \r
1687     x.eM11 = 1.0;\r
1688     x.eM12 = 0;\r
1689     x.eM21 = 0;\r
1690     x.eM22 = 1.0;\r
1691     x.eDx = 0;\r
1692     x.eDy = 0;\r
1693     SetWorldTransform( dst, &x );\r
1694 \r
1695     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1696 }\r
1697 \r
1698 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1699 enum {\r
1700     PM_WP = (int) WhitePawn, \r
1701     PM_WN = (int) WhiteKnight, \r
1702     PM_WB = (int) WhiteBishop, \r
1703     PM_WR = (int) WhiteRook, \r
1704     PM_WQ = (int) WhiteQueen, \r
1705     PM_WF = (int) WhiteFerz, \r
1706     PM_WW = (int) WhiteWazir, \r
1707     PM_WE = (int) WhiteAlfil, \r
1708     PM_WM = (int) WhiteMan, \r
1709     PM_WO = (int) WhiteCannon, \r
1710     PM_WU = (int) WhiteUnicorn, \r
1711     PM_WH = (int) WhiteNightrider, \r
1712     PM_WA = (int) WhiteAngel, \r
1713     PM_WC = (int) WhiteMarshall, \r
1714     PM_WAB = (int) WhiteCardinal, \r
1715     PM_WD = (int) WhiteDragon, \r
1716     PM_WL = (int) WhiteLance, \r
1717     PM_WS = (int) WhiteCobra, \r
1718     PM_WV = (int) WhiteFalcon, \r
1719     PM_WSG = (int) WhiteSilver, \r
1720     PM_WG = (int) WhiteGrasshopper, \r
1721     PM_WK = (int) WhiteKing,\r
1722     PM_BP = (int) BlackPawn, \r
1723     PM_BN = (int) BlackKnight, \r
1724     PM_BB = (int) BlackBishop, \r
1725     PM_BR = (int) BlackRook, \r
1726     PM_BQ = (int) BlackQueen, \r
1727     PM_BF = (int) BlackFerz, \r
1728     PM_BW = (int) BlackWazir, \r
1729     PM_BE = (int) BlackAlfil, \r
1730     PM_BM = (int) BlackMan,\r
1731     PM_BO = (int) BlackCannon, \r
1732     PM_BU = (int) BlackUnicorn, \r
1733     PM_BH = (int) BlackNightrider, \r
1734     PM_BA = (int) BlackAngel, \r
1735     PM_BC = (int) BlackMarshall, \r
1736     PM_BG = (int) BlackGrasshopper, \r
1737     PM_BAB = (int) BlackCardinal,\r
1738     PM_BD = (int) BlackDragon,\r
1739     PM_BL = (int) BlackLance,\r
1740     PM_BS = (int) BlackCobra,\r
1741     PM_BV = (int) BlackFalcon,\r
1742     PM_BSG = (int) BlackSilver,\r
1743     PM_BK = (int) BlackKing\r
1744 };\r
1745 \r
1746 static HFONT hPieceFont = NULL;\r
1747 static HBITMAP hPieceMask[(int) EmptySquare];\r
1748 static HBITMAP hPieceFace[(int) EmptySquare];\r
1749 static int fontBitmapSquareSize = 0;\r
1750 static char pieceToFontChar[(int) EmptySquare] =\r
1751                               { 'p', 'n', 'b', 'r', 'q', \r
1752                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1753                       'k', 'o', 'm', 'v', 't', 'w', \r
1754                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1755                                                               'l' };\r
1756 \r
1757 extern BOOL SetCharTable( char *table, const char * map );\r
1758 /* [HGM] moved to backend.c */\r
1759 \r
1760 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1761 {\r
1762     HBRUSH hbrush;\r
1763     BYTE r1 = GetRValue( color );\r
1764     BYTE g1 = GetGValue( color );\r
1765     BYTE b1 = GetBValue( color );\r
1766     BYTE r2 = r1 / 2;\r
1767     BYTE g2 = g1 / 2;\r
1768     BYTE b2 = b1 / 2;\r
1769     RECT rc;\r
1770 \r
1771     /* Create a uniform background first */\r
1772     hbrush = CreateSolidBrush( color );\r
1773     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1774     FillRect( hdc, &rc, hbrush );\r
1775     DeleteObject( hbrush );\r
1776     \r
1777     if( mode == 1 ) {\r
1778         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1779         int steps = squareSize / 2;\r
1780         int i;\r
1781 \r
1782         for( i=0; i<steps; i++ ) {\r
1783             BYTE r = r1 - (r1-r2) * i / steps;\r
1784             BYTE g = g1 - (g1-g2) * i / steps;\r
1785             BYTE b = b1 - (b1-b2) * i / steps;\r
1786 \r
1787             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1788             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1789             FillRect( hdc, &rc, hbrush );\r
1790             DeleteObject(hbrush);\r
1791         }\r
1792     }\r
1793     else if( mode == 2 ) {\r
1794         /* Diagonal gradient, good more or less for every piece */\r
1795         POINT triangle[3];\r
1796         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1797         HBRUSH hbrush_old;\r
1798         int steps = squareSize;\r
1799         int i;\r
1800 \r
1801         triangle[0].x = squareSize - steps;\r
1802         triangle[0].y = squareSize;\r
1803         triangle[1].x = squareSize;\r
1804         triangle[1].y = squareSize;\r
1805         triangle[2].x = squareSize;\r
1806         triangle[2].y = squareSize - steps;\r
1807 \r
1808         for( i=0; i<steps; i++ ) {\r
1809             BYTE r = r1 - (r1-r2) * i / steps;\r
1810             BYTE g = g1 - (g1-g2) * i / steps;\r
1811             BYTE b = b1 - (b1-b2) * i / steps;\r
1812 \r
1813             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1814             hbrush_old = SelectObject( hdc, hbrush );\r
1815             Polygon( hdc, triangle, 3 );\r
1816             SelectObject( hdc, hbrush_old );\r
1817             DeleteObject(hbrush);\r
1818             triangle[0].x++;\r
1819             triangle[2].y++;\r
1820         }\r
1821 \r
1822         SelectObject( hdc, hpen );\r
1823     }\r
1824 }\r
1825 \r
1826 /*\r
1827     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1828     seems to work ok. The main problem here is to find the "inside" of a chess\r
1829     piece: follow the steps as explained below.\r
1830 */\r
1831 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1832 {\r
1833     HBITMAP hbm;\r
1834     HBITMAP hbm_old;\r
1835     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1836     RECT rc;\r
1837     SIZE sz;\r
1838 \r
1839 \r
1840     POINT pt;\r
1841     int backColor = whitePieceColor; \r
1842     int foreColor = blackPieceColor;\r
1843     \r
1844     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1845         backColor = appData.fontBackColorWhite;\r
1846         foreColor = appData.fontForeColorWhite;\r
1847     }\r
1848     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1849         backColor = appData.fontBackColorBlack;\r
1850         foreColor = appData.fontForeColorBlack;\r
1851     }\r
1852 \r
1853     /* Mask */\r
1854     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1855 \r
1856     hbm_old = SelectObject( hdc, hbm );\r
1857 \r
1858     rc.left = 0;\r
1859     rc.top = 0;\r
1860     rc.right = squareSize;\r
1861     rc.bottom = squareSize;\r
1862 \r
1863     /* Step 1: background is now black */\r
1864     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1865 \r
1866     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1867 \r
1868     pt.x = (squareSize - sz.cx) / 2;\r
1869     pt.y = (squareSize - sz.cy) / 2;\r
1870 \r
1871     SetBkMode( hdc, TRANSPARENT );\r
1872     SetTextColor( hdc, chroma );\r
1873     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1874     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1875 \r
1876     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1877     /* Step 3: the area outside the piece is filled with white */\r
1878 //    FloodFill( hdc, 0, 0, chroma );\r
1879     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1880     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1881     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1882     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1883     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1884     /* \r
1885         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1886         but if the start point is not inside the piece we're lost!\r
1887         There should be a better way to do this... if we could create a region or path\r
1888         from the fill operation we would be fine for example.\r
1889     */\r
1890 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1891     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1892 \r
1893     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1894         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1895         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1896 \r
1897         SelectObject( dc2, bm2 );\r
1898         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1899         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1900         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1901         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1902         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1903 \r
1904         DeleteDC( dc2 );\r
1905         DeleteObject( bm2 );\r
1906     }\r
1907 \r
1908     SetTextColor( hdc, 0 );\r
1909     /* \r
1910         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1911         draw the piece again in black for safety.\r
1912     */\r
1913     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1914 \r
1915     SelectObject( hdc, hbm_old );\r
1916 \r
1917     if( hPieceMask[index] != NULL ) {\r
1918         DeleteObject( hPieceMask[index] );\r
1919     }\r
1920 \r
1921     hPieceMask[index] = hbm;\r
1922 \r
1923     /* Face */\r
1924     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1925 \r
1926     SelectObject( hdc, hbm );\r
1927 \r
1928     {\r
1929         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1930         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1931         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1932 \r
1933         SelectObject( dc1, hPieceMask[index] );\r
1934         SelectObject( dc2, bm2 );\r
1935         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1936         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1937         \r
1938         /* \r
1939             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1940             the piece background and deletes (makes transparent) the rest.\r
1941             Thanks to that mask, we are free to paint the background with the greates\r
1942             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1943             We use this, to make gradients and give the pieces a "roundish" look.\r
1944         */\r
1945         SetPieceBackground( hdc, backColor, 2 );\r
1946         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1947 \r
1948         DeleteDC( dc2 );\r
1949         DeleteDC( dc1 );\r
1950         DeleteObject( bm2 );\r
1951     }\r
1952 \r
1953     SetTextColor( hdc, foreColor );\r
1954     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1955 \r
1956     SelectObject( hdc, hbm_old );\r
1957 \r
1958     if( hPieceFace[index] != NULL ) {\r
1959         DeleteObject( hPieceFace[index] );\r
1960     }\r
1961 \r
1962     hPieceFace[index] = hbm;\r
1963 }\r
1964 \r
1965 static int TranslatePieceToFontPiece( int piece )\r
1966 {\r
1967     switch( piece ) {\r
1968     case BlackPawn:\r
1969         return PM_BP;\r
1970     case BlackKnight:\r
1971         return PM_BN;\r
1972     case BlackBishop:\r
1973         return PM_BB;\r
1974     case BlackRook:\r
1975         return PM_BR;\r
1976     case BlackQueen:\r
1977         return PM_BQ;\r
1978     case BlackKing:\r
1979         return PM_BK;\r
1980     case WhitePawn:\r
1981         return PM_WP;\r
1982     case WhiteKnight:\r
1983         return PM_WN;\r
1984     case WhiteBishop:\r
1985         return PM_WB;\r
1986     case WhiteRook:\r
1987         return PM_WR;\r
1988     case WhiteQueen:\r
1989         return PM_WQ;\r
1990     case WhiteKing:\r
1991         return PM_WK;\r
1992 \r
1993     case BlackAngel:\r
1994         return PM_BA;\r
1995     case BlackMarshall:\r
1996         return PM_BC;\r
1997     case BlackFerz:\r
1998         return PM_BF;\r
1999     case BlackNightrider:\r
2000         return PM_BH;\r
2001     case BlackAlfil:\r
2002         return PM_BE;\r
2003     case BlackWazir:\r
2004         return PM_BW;\r
2005     case BlackUnicorn:\r
2006         return PM_BU;\r
2007     case BlackCannon:\r
2008         return PM_BO;\r
2009     case BlackGrasshopper:\r
2010         return PM_BG;\r
2011     case BlackMan:\r
2012         return PM_BM;\r
2013     case BlackSilver:\r
2014         return PM_BSG;\r
2015     case BlackLance:\r
2016         return PM_BL;\r
2017     case BlackFalcon:\r
2018         return PM_BV;\r
2019     case BlackCobra:\r
2020         return PM_BS;\r
2021     case BlackCardinal:\r
2022         return PM_BAB;\r
2023     case BlackDragon:\r
2024         return PM_BD;\r
2025 \r
2026     case WhiteAngel:\r
2027         return PM_WA;\r
2028     case WhiteMarshall:\r
2029         return PM_WC;\r
2030     case WhiteFerz:\r
2031         return PM_WF;\r
2032     case WhiteNightrider:\r
2033         return PM_WH;\r
2034     case WhiteAlfil:\r
2035         return PM_WE;\r
2036     case WhiteWazir:\r
2037         return PM_WW;\r
2038     case WhiteUnicorn:\r
2039         return PM_WU;\r
2040     case WhiteCannon:\r
2041         return PM_WO;\r
2042     case WhiteGrasshopper:\r
2043         return PM_WG;\r
2044     case WhiteMan:\r
2045         return PM_WM;\r
2046     case WhiteSilver:\r
2047         return PM_WSG;\r
2048     case WhiteLance:\r
2049         return PM_WL;\r
2050     case WhiteFalcon:\r
2051         return PM_WV;\r
2052     case WhiteCobra:\r
2053         return PM_WS;\r
2054     case WhiteCardinal:\r
2055         return PM_WAB;\r
2056     case WhiteDragon:\r
2057         return PM_WD;\r
2058     }\r
2059 \r
2060     return 0;\r
2061 }\r
2062 \r
2063 void CreatePiecesFromFont()\r
2064 {\r
2065     LOGFONT lf;\r
2066     HDC hdc_window = NULL;\r
2067     HDC hdc = NULL;\r
2068     HFONT hfont_old;\r
2069     int fontHeight;\r
2070     int i;\r
2071 \r
2072     if( fontBitmapSquareSize < 0 ) {\r
2073         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2074         return;\r
2075     }\r
2076 \r
2077     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2078             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2079         fontBitmapSquareSize = -1;\r
2080         return;\r
2081     }\r
2082 \r
2083     if( fontBitmapSquareSize != squareSize ) {\r
2084         hdc_window = GetDC( hwndMain );\r
2085         hdc = CreateCompatibleDC( hdc_window );\r
2086 \r
2087         if( hPieceFont != NULL ) {\r
2088             DeleteObject( hPieceFont );\r
2089         }\r
2090         else {\r
2091             for( i=0; i<=(int)BlackKing; i++ ) {\r
2092                 hPieceMask[i] = NULL;\r
2093                 hPieceFace[i] = NULL;\r
2094             }\r
2095         }\r
2096 \r
2097         fontHeight = 75;\r
2098 \r
2099         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2100             fontHeight = appData.fontPieceSize;\r
2101         }\r
2102 \r
2103         fontHeight = (fontHeight * squareSize) / 100;\r
2104 \r
2105         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2106         lf.lfWidth = 0;\r
2107         lf.lfEscapement = 0;\r
2108         lf.lfOrientation = 0;\r
2109         lf.lfWeight = FW_NORMAL;\r
2110         lf.lfItalic = 0;\r
2111         lf.lfUnderline = 0;\r
2112         lf.lfStrikeOut = 0;\r
2113         lf.lfCharSet = DEFAULT_CHARSET;\r
2114         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2115         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2116         lf.lfQuality = PROOF_QUALITY;\r
2117         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2118         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2119         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2120 \r
2121         hPieceFont = CreateFontIndirect( &lf );\r
2122 \r
2123         if( hPieceFont == NULL ) {\r
2124             fontBitmapSquareSize = -2;\r
2125         }\r
2126         else {\r
2127             /* Setup font-to-piece character table */\r
2128             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2129                 /* No (or wrong) global settings, try to detect the font */\r
2130                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2131                     /* Alpha */\r
2132                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2133                 }\r
2134                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2135                     /* DiagramTT* family */\r
2136                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2137                 }\r
2138                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2139                     /* Fairy symbols */\r
2140                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2141                 }\r
2142                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2143                     /* Good Companion (Some characters get warped as literal :-( */\r
2144                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2145                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2146                     SetCharTable(pieceToFontChar, s);\r
2147                 }\r
2148                 else {\r
2149                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2150                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2151                 }\r
2152             }\r
2153 \r
2154             /* Create bitmaps */\r
2155             hfont_old = SelectObject( hdc, hPieceFont );\r
2156             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2157                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2158                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2159 \r
2160             SelectObject( hdc, hfont_old );\r
2161 \r
2162             fontBitmapSquareSize = squareSize;\r
2163         }\r
2164     }\r
2165 \r
2166     if( hdc != NULL ) {\r
2167         DeleteDC( hdc );\r
2168     }\r
2169 \r
2170     if( hdc_window != NULL ) {\r
2171         ReleaseDC( hwndMain, hdc_window );\r
2172     }\r
2173 }\r
2174 \r
2175 HBITMAP\r
2176 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2177 {\r
2178   char name[128], buf[MSG_SIZ];\r
2179 \r
2180     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2181   if(appData.pieceDirectory[0]) {\r
2182     HBITMAP res;\r
2183     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2184     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2185     if(res) return res;\r
2186   }\r
2187   if (gameInfo.event &&\r
2188       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2189       strcmp(name, "k80s") == 0) {\r
2190     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2191   }\r
2192   return LoadBitmap(hinst, name);\r
2193 }\r
2194 \r
2195 \r
2196 /* Insert a color into the program's logical palette\r
2197    structure.  This code assumes the given color is\r
2198    the result of the RGB or PALETTERGB macro, and it\r
2199    knows how those macros work (which is documented).\r
2200 */\r
2201 VOID\r
2202 InsertInPalette(COLORREF color)\r
2203 {\r
2204   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2205 \r
2206   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2207     DisplayFatalError(_("Too many colors"), 0, 1);\r
2208     pLogPal->palNumEntries--;\r
2209     return;\r
2210   }\r
2211 \r
2212   pe->peFlags = (char) 0;\r
2213   pe->peRed = (char) (0xFF & color);\r
2214   pe->peGreen = (char) (0xFF & (color >> 8));\r
2215   pe->peBlue = (char) (0xFF & (color >> 16));\r
2216   return;\r
2217 }\r
2218 \r
2219 \r
2220 VOID\r
2221 InitDrawingColors()\r
2222 {\r
2223   int i;\r
2224   if (pLogPal == NULL) {\r
2225     /* Allocate enough memory for a logical palette with\r
2226      * PALETTESIZE entries and set the size and version fields\r
2227      * of the logical palette structure.\r
2228      */\r
2229     pLogPal = (NPLOGPALETTE)\r
2230       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2231                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2232     pLogPal->palVersion    = 0x300;\r
2233   }\r
2234   pLogPal->palNumEntries = 0;\r
2235 \r
2236   InsertInPalette(lightSquareColor);\r
2237   InsertInPalette(darkSquareColor);\r
2238   InsertInPalette(whitePieceColor);\r
2239   InsertInPalette(blackPieceColor);\r
2240   InsertInPalette(highlightSquareColor);\r
2241   InsertInPalette(premoveHighlightColor);\r
2242 \r
2243   /*  create a logical color palette according the information\r
2244    *  in the LOGPALETTE structure.\r
2245    */\r
2246   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2247 \r
2248   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2249   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2250   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2251   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2252   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2253   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2254   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2255     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2256 \r
2257    /* [AS] Force rendering of the font-based pieces */\r
2258   if( fontBitmapSquareSize > 0 ) {\r
2259     fontBitmapSquareSize = 0;\r
2260   }\r
2261 }\r
2262 \r
2263 \r
2264 int\r
2265 BoardWidth(int boardSize, int n)\r
2266 { /* [HGM] argument n added to allow different width and height */\r
2267   int lineGap = sizeInfo[boardSize].lineGap;\r
2268 \r
2269   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2270       lineGap = appData.overrideLineGap;\r
2271   }\r
2272 \r
2273   return (n + 1) * lineGap +\r
2274           n * sizeInfo[boardSize].squareSize;\r
2275 }\r
2276 \r
2277 /* Respond to board resize by dragging edge */\r
2278 VOID\r
2279 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2280 {\r
2281   BoardSize newSize = NUM_SIZES - 1;\r
2282   static int recurse = 0;\r
2283   if (IsIconic(hwndMain)) return;\r
2284   if (recurse > 0) return;\r
2285   recurse++;\r
2286   while (newSize > 0) {\r
2287         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2288         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2289            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2290     newSize--;\r
2291   } \r
2292   boardSize = newSize;\r
2293   InitDrawingSizes(boardSize, flags);\r
2294   recurse--;\r
2295 }\r
2296 \r
2297 \r
2298 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2299 \r
2300 VOID\r
2301 InitDrawingSizes(BoardSize boardSize, int flags)\r
2302 {\r
2303   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2304   ChessSquare piece;\r
2305   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2306   HDC hdc;\r
2307   SIZE clockSize, messageSize;\r
2308   HFONT oldFont;\r
2309   char buf[MSG_SIZ];\r
2310   char *str;\r
2311   HMENU hmenu = GetMenu(hwndMain);\r
2312   RECT crect, wrect, oldRect;\r
2313   int offby;\r
2314   LOGBRUSH logbrush;\r
2315   VariantClass v = gameInfo.variant;\r
2316 \r
2317   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2318   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2319 \r
2320   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2321   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2322   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2323   oldBoardSize = boardSize;\r
2324 \r
2325   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2326   { // correct board size to one where built-in pieces exist\r
2327     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2328        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2329 \r
2330       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2331       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2332       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2333       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2334       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2335                                    boardSize = SizeMiddling;\r
2336     }\r
2337   }\r
2338   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2339 \r
2340   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2341   oldRect.top = wpMain.y;\r
2342   oldRect.right = wpMain.x + wpMain.width;\r
2343   oldRect.bottom = wpMain.y + wpMain.height;\r
2344 \r
2345   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2346   smallLayout = sizeInfo[boardSize].smallLayout;\r
2347   squareSize = sizeInfo[boardSize].squareSize;\r
2348   lineGap = sizeInfo[boardSize].lineGap;\r
2349   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2350   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2351 \r
2352   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2353       lineGap = appData.overrideLineGap;\r
2354   }\r
2355 \r
2356   if (tinyLayout != oldTinyLayout) {\r
2357     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2358     if (tinyLayout) {\r
2359       style &= ~WS_SYSMENU;\r
2360       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2361                  "&Minimize\tCtrl+F4");\r
2362     } else {\r
2363       style |= WS_SYSMENU;\r
2364       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2365     }\r
2366     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2367 \r
2368     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2369       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2370         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2371     }\r
2372     DrawMenuBar(hwndMain);\r
2373   }\r
2374 \r
2375   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2376   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2377 \r
2378   /* Get text area sizes */\r
2379   hdc = GetDC(hwndMain);\r
2380   if (appData.clockMode) {\r
2381     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2382   } else {\r
2383     snprintf(buf, MSG_SIZ, _("White"));\r
2384   }\r
2385   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2386   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2387   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2388   str = _("We only care about the height here");\r
2389   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2390   SelectObject(hdc, oldFont);\r
2391   ReleaseDC(hwndMain, hdc);\r
2392 \r
2393   /* Compute where everything goes */\r
2394   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2395         /* [HGM] logo: if either logo is on, reserve space for it */\r
2396         logoHeight =  2*clockSize.cy;\r
2397         leftLogoRect.left   = OUTER_MARGIN;\r
2398         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2399         leftLogoRect.top    = OUTER_MARGIN;\r
2400         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2401 \r
2402         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2403         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2404         rightLogoRect.top    = OUTER_MARGIN;\r
2405         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2406 \r
2407 \r
2408     whiteRect.left = leftLogoRect.right;\r
2409     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2410     whiteRect.top = OUTER_MARGIN;\r
2411     whiteRect.bottom = whiteRect.top + logoHeight;\r
2412 \r
2413     blackRect.right = rightLogoRect.left;\r
2414     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2415     blackRect.top = whiteRect.top;\r
2416     blackRect.bottom = whiteRect.bottom;\r
2417   } else {\r
2418     whiteRect.left = OUTER_MARGIN;\r
2419     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2420     whiteRect.top = OUTER_MARGIN;\r
2421     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2422 \r
2423     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2424     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2425     blackRect.top = whiteRect.top;\r
2426     blackRect.bottom = whiteRect.bottom;\r
2427 \r
2428     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2429   }\r
2430 \r
2431   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2432   if (appData.showButtonBar) {\r
2433     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2434       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2435   } else {\r
2436     messageRect.right = OUTER_MARGIN + boardWidth;\r
2437   }\r
2438   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2439   messageRect.bottom = messageRect.top + messageSize.cy;\r
2440 \r
2441   boardRect.left = OUTER_MARGIN;\r
2442   boardRect.right = boardRect.left + boardWidth;\r
2443   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2444   boardRect.bottom = boardRect.top + boardHeight;\r
2445 \r
2446   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2447   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2448   oldTinyLayout = tinyLayout;\r
2449   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2450   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2451     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2452   winW *= 1 + twoBoards;\r
2453   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2454   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2455   wpMain.height = winH; //       without disturbing window attachments\r
2456   GetWindowRect(hwndMain, &wrect);\r
2457   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2458                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2459 \r
2460   // [HGM] placement: let attached windows follow size change.\r
2461   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2462   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2463   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2464   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2465   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2466 \r
2467   /* compensate if menu bar wrapped */\r
2468   GetClientRect(hwndMain, &crect);\r
2469   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2470   wpMain.height += offby;\r
2471   switch (flags) {\r
2472   case WMSZ_TOPLEFT:\r
2473     SetWindowPos(hwndMain, NULL, \r
2474                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2475                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2476     break;\r
2477 \r
2478   case WMSZ_TOPRIGHT:\r
2479   case WMSZ_TOP:\r
2480     SetWindowPos(hwndMain, NULL, \r
2481                  wrect.left, wrect.bottom - wpMain.height, \r
2482                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2483     break;\r
2484 \r
2485   case WMSZ_BOTTOMLEFT:\r
2486   case WMSZ_LEFT:\r
2487     SetWindowPos(hwndMain, NULL, \r
2488                  wrect.right - wpMain.width, wrect.top, \r
2489                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2490     break;\r
2491 \r
2492   case WMSZ_BOTTOMRIGHT:\r
2493   case WMSZ_BOTTOM:\r
2494   case WMSZ_RIGHT:\r
2495   default:\r
2496     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2497                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2498     break;\r
2499   }\r
2500 \r
2501   hwndPause = NULL;\r
2502   for (i = 0; i < N_BUTTONS; i++) {\r
2503     if (buttonDesc[i].hwnd != NULL) {\r
2504       DestroyWindow(buttonDesc[i].hwnd);\r
2505       buttonDesc[i].hwnd = NULL;\r
2506     }\r
2507     if (appData.showButtonBar) {\r
2508       buttonDesc[i].hwnd =\r
2509         CreateWindow("BUTTON", buttonDesc[i].label,\r
2510                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2511                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2512                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2513                      (HMENU) buttonDesc[i].id,\r
2514                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2515       if (tinyLayout) {\r
2516         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2517                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2518                     MAKELPARAM(FALSE, 0));\r
2519       }\r
2520       if (buttonDesc[i].id == IDM_Pause)\r
2521         hwndPause = buttonDesc[i].hwnd;\r
2522       buttonDesc[i].wndproc = (WNDPROC)\r
2523         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2524     }\r
2525   }\r
2526   if (gridPen != NULL) DeleteObject(gridPen);\r
2527   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2528   if (premovePen != NULL) DeleteObject(premovePen);\r
2529   if (lineGap != 0) {\r
2530     logbrush.lbStyle = BS_SOLID;\r
2531     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2532     gridPen =\r
2533       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2534                    lineGap, &logbrush, 0, NULL);\r
2535     logbrush.lbColor = highlightSquareColor;\r
2536     highlightPen =\r
2537       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2538                    lineGap, &logbrush, 0, NULL);\r
2539 \r
2540     logbrush.lbColor = premoveHighlightColor; \r
2541     premovePen =\r
2542       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2543                    lineGap, &logbrush, 0, NULL);\r
2544 \r
2545     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2546     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2547       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2548       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2549         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2550       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2551         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2552       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2553     }\r
2554     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2555       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2556       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2557         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2558         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2559       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2560         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2561       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2562     }\r
2563   }\r
2564 \r
2565   /* [HGM] Licensing requirement */\r
2566 #ifdef GOTHIC\r
2567   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2568 #endif\r
2569 #ifdef FALCON\r
2570   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2571 #endif\r
2572   GothicPopUp( "", VariantNormal);\r
2573 \r
2574 \r
2575 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2576 \r
2577   /* Load piece bitmaps for this board size */\r
2578   for (i=0; i<=2; i++) {\r
2579     for (piece = WhitePawn;\r
2580          (int) piece < (int) BlackPawn;\r
2581          piece = (ChessSquare) ((int) piece + 1)) {\r
2582       if (pieceBitmap[i][piece] != NULL)\r
2583         DeleteObject(pieceBitmap[i][piece]);\r
2584     }\r
2585   }\r
2586 \r
2587   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2588   // Orthodox Chess pieces\r
2589   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2590   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2591   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2592   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2593   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2594   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2595   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2596   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2597   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2598   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2599   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2600   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2601   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2602   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2603   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2604   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2605     // in Shogi, Hijack the unused Queen for Lance\r
2606     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2607     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2608     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2609   } else {\r
2610     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2611     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2612     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2613   }\r
2614 \r
2615   if(squareSize <= 72 && squareSize >= 33) { \r
2616     /* A & C are available in most sizes now */\r
2617     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2618       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2619       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2620       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2621       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2622       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2623       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2624       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2625       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2626       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2627       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2628       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2629       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2630     } else { // Smirf-like\r
2631       if(gameInfo.variant == VariantSChess) {\r
2632         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2633         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2634         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2635       } else {\r
2636         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2637         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2638         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2639       }\r
2640     }\r
2641     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2642       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2643       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2644       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2645     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2646       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2647       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2648       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2649     } else { // WinBoard standard\r
2650       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2651       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2652       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2653     }\r
2654   }\r
2655 \r
2656 \r
2657   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2658     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2659     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2660     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2661     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2662     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2663     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2664     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2665     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2666     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2667     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2668     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2669     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2670     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2671     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2672     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2673     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2674     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2675     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2676     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2677     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2678     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2679     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2680     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2681     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2682     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2683     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2684     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2685     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2686     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2687     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2688     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2689     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2690     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2691 \r
2692     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2693       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2694       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2695       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2696       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2697       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2698       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2699       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2700       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2701       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2702       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2703       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2704       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2705     } else {\r
2706       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2707       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2708       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2709       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2710       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2711       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2712       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2713       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2714       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2715       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2716       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2717       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2718     }\r
2719 \r
2720   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2721     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2722     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2723     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2724     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2725     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2726     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2727     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2728     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2729     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2730     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2731     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2732     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2733     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2734     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2735   }\r
2736 \r
2737 \r
2738   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2739   /* special Shogi support in this size */\r
2740   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2741       for (piece = WhitePawn;\r
2742            (int) piece < (int) BlackPawn;\r
2743            piece = (ChessSquare) ((int) piece + 1)) {\r
2744         if (pieceBitmap[i][piece] != NULL)\r
2745           DeleteObject(pieceBitmap[i][piece]);\r
2746       }\r
2747     }\r
2748   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2749   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2750   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2751   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2752   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2753   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2754   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2755   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2756   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2757   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2758   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2759   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2760   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2761   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2762   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2763   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2764   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2765   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2766   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2767   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2768   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2769   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2770   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2771   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2772   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2773   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2774   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2775   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2776   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2777   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2778   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2779   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2780   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2781   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2782   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2783   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2784   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2785   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2786   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2787   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2788   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2789   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2790   minorSize = 0;\r
2791   }\r
2792 }\r
2793 \r
2794 HBITMAP\r
2795 PieceBitmap(ChessSquare p, int kind)\r
2796 {\r
2797   if ((int) p >= (int) BlackPawn)\r
2798     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2799 \r
2800   return pieceBitmap[kind][(int) p];\r
2801 }\r
2802 \r
2803 /***************************************************************/\r
2804 \r
2805 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2806 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2807 /*\r
2808 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2809 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2810 */\r
2811 \r
2812 VOID\r
2813 SquareToPos(int row, int column, int * x, int * y)\r
2814 {\r
2815   if (flipView) {\r
2816     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2817     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2818   } else {\r
2819     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2820     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2821   }\r
2822 }\r
2823 \r
2824 VOID\r
2825 DrawCoordsOnDC(HDC hdc)\r
2826 {\r
2827   static char files[] = "0123456789012345678901221098765432109876543210";\r
2828   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2829   char str[2] = { NULLCHAR, NULLCHAR };\r
2830   int oldMode, oldAlign, x, y, start, i;\r
2831   HFONT oldFont;\r
2832   HBRUSH oldBrush;\r
2833 \r
2834   if (!appData.showCoords)\r
2835     return;\r
2836 \r
2837   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2838 \r
2839   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2840   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2841   oldAlign = GetTextAlign(hdc);\r
2842   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2843 \r
2844   y = boardRect.top + lineGap;\r
2845   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2846 \r
2847   if(border) {\r
2848     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2849     x += border - lineGap - 4; y += squareSize - 6;\r
2850   } else\r
2851   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2852   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2853     str[0] = files[start + i];\r
2854     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2855     y += squareSize + lineGap;\r
2856   }\r
2857 \r
2858   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2859 \r
2860   if(border) {\r
2861     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2862     x += -border + 4; y += border - squareSize + 6;\r
2863   } else\r
2864   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2865   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2866     str[0] = ranks[start + i];\r
2867     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2868     x += squareSize + lineGap;\r
2869   }    \r
2870 \r
2871   SelectObject(hdc, oldBrush);\r
2872   SetBkMode(hdc, oldMode);\r
2873   SetTextAlign(hdc, oldAlign);\r
2874   SelectObject(hdc, oldFont);\r
2875 }\r
2876 \r
2877 VOID\r
2878 DrawGridOnDC(HDC hdc)\r
2879 {\r
2880   HPEN oldPen;\r
2881  \r
2882   if (lineGap != 0) {\r
2883     oldPen = SelectObject(hdc, gridPen);\r
2884     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2885     SelectObject(hdc, oldPen);\r
2886   }\r
2887 }\r
2888 \r
2889 #define HIGHLIGHT_PEN 0\r
2890 #define PREMOVE_PEN   1\r
2891 \r
2892 VOID\r
2893 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2894 {\r
2895   int x1, y1;\r
2896   HPEN oldPen, hPen;\r
2897   if (lineGap == 0) return;\r
2898   if (flipView) {\r
2899     x1 = boardRect.left +\r
2900       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2901     y1 = boardRect.top +\r
2902       lineGap/2 + y * (squareSize + lineGap) + border;\r
2903   } else {\r
2904     x1 = boardRect.left +\r
2905       lineGap/2 + x * (squareSize + lineGap) + border;\r
2906     y1 = boardRect.top +\r
2907       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2908   }\r
2909   hPen = pen ? premovePen : highlightPen;\r
2910   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2911   MoveToEx(hdc, x1, y1, NULL);\r
2912   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2913   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2914   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2915   LineTo(hdc, x1, y1);\r
2916   SelectObject(hdc, oldPen);\r
2917 }\r
2918 \r
2919 VOID\r
2920 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2921 {\r
2922   int i;\r
2923   for (i=0; i<2; i++) {\r
2924     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2925       DrawHighlightOnDC(hdc, TRUE,\r
2926                         h->sq[i].x, h->sq[i].y,\r
2927                         pen);\r
2928   }\r
2929 }\r
2930 \r
2931 /* Note: sqcolor is used only in monoMode */\r
2932 /* Note that this code is largely duplicated in woptions.c,\r
2933    function DrawSampleSquare, so that needs to be updated too */\r
2934 VOID\r
2935 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2936 {\r
2937   HBITMAP oldBitmap;\r
2938   HBRUSH oldBrush;\r
2939   int tmpSize;\r
2940 \r
2941   if (appData.blindfold) return;\r
2942 \r
2943   /* [AS] Use font-based pieces if needed */\r
2944   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2945     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2946     CreatePiecesFromFont();\r
2947 \r
2948     if( fontBitmapSquareSize == squareSize ) {\r
2949         int index = TranslatePieceToFontPiece(piece);\r
2950 \r
2951         SelectObject( tmphdc, hPieceMask[ index ] );\r
2952 \r
2953       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2954         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2955       else\r
2956         BitBlt( hdc,\r
2957             x, y,\r
2958             squareSize, squareSize,\r
2959             tmphdc,\r
2960             0, 0,\r
2961             SRCAND );\r
2962 \r
2963         SelectObject( tmphdc, hPieceFace[ index ] );\r
2964 \r
2965       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2966         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2967       else\r
2968         BitBlt( hdc,\r
2969             x, y,\r
2970             squareSize, squareSize,\r
2971             tmphdc,\r
2972             0, 0,\r
2973             SRCPAINT );\r
2974 \r
2975         return;\r
2976     }\r
2977   }\r
2978 \r
2979   if (appData.monoMode) {\r
2980     SelectObject(tmphdc, PieceBitmap(piece, \r
2981       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2982     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2983            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2984   } else {\r
2985     HBRUSH xBrush = whitePieceBrush;\r
2986     tmpSize = squareSize;\r
2987     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2988     if(minorSize &&\r
2989         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2990          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2991       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2992       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2993       x += (squareSize - minorSize)>>1;\r
2994       y += squareSize - minorSize - 2;\r
2995       tmpSize = minorSize;\r
2996     }\r
2997     if (color || appData.allWhite ) {\r
2998       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2999       if( color )\r
3000               oldBrush = SelectObject(hdc, xBrush);\r
3001       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3002       if(appData.upsideDown && color==flipView)\r
3003         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3004       else\r
3005         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3006       /* Use black for outline of white pieces */\r
3007       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3008       if(appData.upsideDown && color==flipView)\r
3009         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3010       else\r
3011         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3012     } else if(appData.pieceDirectory[0]) {\r
3013       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3014       oldBrush = SelectObject(hdc, xBrush);\r
3015       if(appData.upsideDown && color==flipView)\r
3016         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3017       else\r
3018         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3019       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3020       if(appData.upsideDown && color==flipView)\r
3021         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3022       else\r
3023         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3024     } else {\r
3025       /* Use square color for details of black pieces */\r
3026       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3027       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3028       if(appData.upsideDown && !flipView)\r
3029         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3030       else\r
3031         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3032     }\r
3033     SelectObject(hdc, oldBrush);\r
3034     SelectObject(tmphdc, oldBitmap);\r
3035   }\r
3036 }\r
3037 \r
3038 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3039 int GetBackTextureMode( int algo )\r
3040 {\r
3041     int result = BACK_TEXTURE_MODE_DISABLED;\r
3042 \r
3043     switch( algo ) \r
3044     {\r
3045         case BACK_TEXTURE_MODE_PLAIN:\r
3046             result = 1; /* Always use identity map */\r
3047             break;\r
3048         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3049             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3050             break;\r
3051     }\r
3052 \r
3053     return result;\r
3054 }\r
3055 \r
3056 /* \r
3057     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3058     to handle redraws cleanly (as random numbers would always be different).\r
3059 */\r
3060 VOID RebuildTextureSquareInfo()\r
3061 {\r
3062     BITMAP bi;\r
3063     int lite_w = 0;\r
3064     int lite_h = 0;\r
3065     int dark_w = 0;\r
3066     int dark_h = 0;\r
3067     int row;\r
3068     int col;\r
3069 \r
3070     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3071 \r
3072     if( liteBackTexture != NULL ) {\r
3073         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3074             lite_w = bi.bmWidth;\r
3075             lite_h = bi.bmHeight;\r
3076         }\r
3077     }\r
3078 \r
3079     if( darkBackTexture != NULL ) {\r
3080         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3081             dark_w = bi.bmWidth;\r
3082             dark_h = bi.bmHeight;\r
3083         }\r
3084     }\r
3085 \r
3086     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3087         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3088             if( (col + row) & 1 ) {\r
3089                 /* Lite square */\r
3090                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3091                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3092                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3093                   else\r
3094                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3095                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3096                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3097                   else\r
3098                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3099                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3100                 }\r
3101             }\r
3102             else {\r
3103                 /* Dark square */\r
3104                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3105                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3106                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3107                   else\r
3108                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3109                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3110                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3111                   else\r
3112                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3113                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3114                 }\r
3115             }\r
3116         }\r
3117     }\r
3118 }\r
3119 \r
3120 /* [AS] Arrow highlighting support */\r
3121 \r
3122 static double A_WIDTH = 5; /* Width of arrow body */\r
3123 \r
3124 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3125 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3126 \r
3127 static double Sqr( double x )\r
3128 {\r
3129     return x*x;\r
3130 }\r
3131 \r
3132 static int Round( double x )\r
3133 {\r
3134     return (int) (x + 0.5);\r
3135 }\r
3136 \r
3137 /* Draw an arrow between two points using current settings */\r
3138 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3139 {\r
3140     POINT arrow[7];\r
3141     double dx, dy, j, k, x, y;\r
3142 \r
3143     if( d_x == s_x ) {\r
3144         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3145 \r
3146         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3147         arrow[0].y = s_y;\r
3148 \r
3149         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3150         arrow[1].y = d_y - h;\r
3151 \r
3152         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3153         arrow[2].y = d_y - h;\r
3154 \r
3155         arrow[3].x = d_x;\r
3156         arrow[3].y = d_y;\r
3157 \r
3158         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3159         arrow[5].y = d_y - h;\r
3160 \r
3161         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3162         arrow[4].y = d_y - h;\r
3163 \r
3164         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3165         arrow[6].y = s_y;\r
3166     }\r
3167     else if( d_y == s_y ) {\r
3168         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3169 \r
3170         arrow[0].x = s_x;\r
3171         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3172 \r
3173         arrow[1].x = d_x - w;\r
3174         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3175 \r
3176         arrow[2].x = d_x - w;\r
3177         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3178 \r
3179         arrow[3].x = d_x;\r
3180         arrow[3].y = d_y;\r
3181 \r
3182         arrow[5].x = d_x - w;\r
3183         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3184 \r
3185         arrow[4].x = d_x - w;\r
3186         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3187 \r
3188         arrow[6].x = s_x;\r
3189         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3190     }\r
3191     else {\r
3192         /* [AS] Needed a lot of paper for this! :-) */\r
3193         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3194         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3195   \r
3196         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3197 \r
3198         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3199 \r
3200         x = s_x;\r
3201         y = s_y;\r
3202 \r
3203         arrow[0].x = Round(x - j);\r
3204         arrow[0].y = Round(y + j*dx);\r
3205 \r
3206         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3207         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3208 \r
3209         if( d_x > s_x ) {\r
3210             x = (double) d_x - k;\r
3211             y = (double) d_y - k*dy;\r
3212         }\r
3213         else {\r
3214             x = (double) d_x + k;\r
3215             y = (double) d_y + k*dy;\r
3216         }\r
3217 \r
3218         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3219 \r
3220         arrow[6].x = Round(x - j);\r
3221         arrow[6].y = Round(y + j*dx);\r
3222 \r
3223         arrow[2].x = Round(arrow[6].x + 2*j);\r
3224         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3225 \r
3226         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3227         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3228 \r
3229         arrow[4].x = d_x;\r
3230         arrow[4].y = d_y;\r
3231 \r
3232         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3233         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3234     }\r
3235 \r
3236     Polygon( hdc, arrow, 7 );\r
3237 }\r
3238 \r
3239 /* [AS] Draw an arrow between two squares */\r
3240 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3241 {\r
3242     int s_x, s_y, d_x, d_y;\r
3243     HPEN hpen;\r
3244     HPEN holdpen;\r
3245     HBRUSH hbrush;\r
3246     HBRUSH holdbrush;\r
3247     LOGBRUSH stLB;\r
3248 \r
3249     if( s_col == d_col && s_row == d_row ) {\r
3250         return;\r
3251     }\r
3252 \r
3253     /* Get source and destination points */\r
3254     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3255     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3256 \r
3257     if( d_y > s_y ) {\r
3258         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3259     }\r
3260     else if( d_y < s_y ) {\r
3261         d_y += squareSize / 2 + squareSize / 4;\r
3262     }\r
3263     else {\r
3264         d_y += squareSize / 2;\r
3265     }\r
3266 \r
3267     if( d_x > s_x ) {\r
3268         d_x += squareSize / 2 - squareSize / 4;\r
3269     }\r
3270     else if( d_x < s_x ) {\r
3271         d_x += squareSize / 2 + squareSize / 4;\r
3272     }\r
3273     else {\r
3274         d_x += squareSize / 2;\r
3275     }\r
3276 \r
3277     s_x += squareSize / 2;\r
3278     s_y += squareSize / 2;\r
3279 \r
3280     /* Adjust width */\r
3281     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3282 \r
3283     /* Draw */\r
3284     stLB.lbStyle = BS_SOLID;\r
3285     stLB.lbColor = appData.highlightArrowColor;\r
3286     stLB.lbHatch = 0;\r
3287 \r
3288     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3289     holdpen = SelectObject( hdc, hpen );\r
3290     hbrush = CreateBrushIndirect( &stLB );\r
3291     holdbrush = SelectObject( hdc, hbrush );\r
3292 \r
3293     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3294 \r
3295     SelectObject( hdc, holdpen );\r
3296     SelectObject( hdc, holdbrush );\r
3297     DeleteObject( hpen );\r
3298     DeleteObject( hbrush );\r
3299 }\r
3300 \r
3301 BOOL HasHighlightInfo()\r
3302 {\r
3303     BOOL result = FALSE;\r
3304 \r
3305     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3306         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3307     {\r
3308         result = TRUE;\r
3309     }\r
3310 \r
3311     return result;\r
3312 \r
3313 \r
3314 \r
3315 }\r
3316 \r
3317 BOOL IsDrawArrowEnabled()\r
3318 {\r
3319     BOOL result = FALSE;\r
3320 \r
3321     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3322         result = TRUE;\r
3323     }\r
3324 \r
3325     return result;\r
3326 }\r
3327 \r
3328 VOID DrawArrowHighlight( HDC hdc )\r
3329 {\r
3330     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3331         DrawArrowBetweenSquares( hdc,\r
3332             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3333             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3334     }\r
3335 }\r
3336 \r
3337 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3338 {\r
3339     HRGN result = NULL;\r
3340 \r
3341     if( HasHighlightInfo() ) {\r
3342         int x1, y1, x2, y2;\r
3343         int sx, sy, dx, dy;\r
3344 \r
3345         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3346         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3347 \r
3348         sx = MIN( x1, x2 );\r
3349         sy = MIN( y1, y2 );\r
3350         dx = MAX( x1, x2 ) + squareSize;\r
3351         dy = MAX( y1, y2 ) + squareSize;\r
3352 \r
3353         result = CreateRectRgn( sx, sy, dx, dy );\r
3354     }\r
3355 \r
3356     return result;\r
3357 }\r
3358 \r
3359 /*\r
3360     Warning: this function modifies the behavior of several other functions. \r
3361     \r
3362     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3363     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3364     repaint is scattered all over the place, which is not good for features such as\r
3365     "arrow highlighting" that require a full repaint of the board.\r
3366 \r
3367     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3368     user interaction, when speed is not so important) but especially to avoid errors\r
3369     in the displayed graphics.\r
3370 \r
3371     In such patched places, I always try refer to this function so there is a single\r
3372     place to maintain knowledge.\r
3373     \r
3374     To restore the original behavior, just return FALSE unconditionally.\r
3375 */\r
3376 BOOL IsFullRepaintPreferrable()\r
3377 {\r
3378     BOOL result = FALSE;\r
3379 \r
3380     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3381         /* Arrow may appear on the board */\r
3382         result = TRUE;\r
3383     }\r
3384 \r
3385     return result;\r
3386 }\r
3387 \r
3388 /* \r
3389     This function is called by DrawPosition to know whether a full repaint must\r
3390     be forced or not.\r
3391 \r
3392     Only DrawPosition may directly call this function, which makes use of \r
3393     some state information. Other function should call DrawPosition specifying \r
3394     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3395 */\r
3396 BOOL DrawPositionNeedsFullRepaint()\r
3397 {\r
3398     BOOL result = FALSE;\r
3399 \r
3400     /* \r
3401         Probably a slightly better policy would be to trigger a full repaint\r
3402         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3403         but animation is fast enough that it's difficult to notice.\r
3404     */\r
3405     if( animInfo.piece == EmptySquare ) {\r
3406         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3407             result = TRUE;\r
3408         }\r
3409     }\r
3410 \r
3411     return result;\r
3412 }\r
3413 \r
3414 static HBITMAP borderBitmap;\r
3415 \r
3416 VOID\r
3417 DrawBackgroundOnDC(HDC hdc)\r
3418 {\r
3419   \r
3420   BITMAP bi;\r
3421   HDC tmphdc;\r
3422   HBITMAP hbm;\r
3423   static char oldBorder[MSG_SIZ];\r
3424   int w = 600, h = 600, mode;\r
3425 \r
3426   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3427     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3428     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3429   }\r
3430   if(borderBitmap == NULL) { // loading failed, use white\r
3431     FillRect( hdc, &boardRect, whitePieceBrush );\r
3432     return;\r
3433   }\r
3434   tmphdc = CreateCompatibleDC(hdc);\r
3435   hbm = SelectObject(tmphdc, borderBitmap);\r
3436   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3437             w = bi.bmWidth;\r
3438             h = bi.bmHeight;\r
3439   }\r
3440   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3441   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3442                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3443   SetStretchBltMode(hdc, mode);\r
3444   SelectObject(tmphdc, hbm);\r
3445   DeleteDC(tmphdc);\r
3446 }\r
3447 \r
3448 VOID\r
3449 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3450 {\r
3451   int row, column, x, y, square_color, piece_color;\r
3452   ChessSquare piece;\r
3453   HBRUSH oldBrush;\r
3454   HDC texture_hdc = NULL;\r
3455 \r
3456   /* [AS] Initialize background textures if needed */\r
3457   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3458       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3459       if( backTextureSquareSize != squareSize \r
3460        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3461           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3462           backTextureSquareSize = squareSize;\r
3463           RebuildTextureSquareInfo();\r
3464       }\r
3465 \r
3466       texture_hdc = CreateCompatibleDC( hdc );\r
3467   }\r
3468 \r
3469   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3470     for (column = 0; column < BOARD_WIDTH; column++) {\r
3471   \r
3472       SquareToPos(row, column, &x, &y);\r
3473 \r
3474       piece = board[row][column];\r
3475 \r
3476       square_color = ((column + row) % 2) == 1;\r
3477       if( gameInfo.variant == VariantXiangqi ) {\r
3478           square_color = !InPalace(row, column);\r
3479           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3480           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3481       }\r
3482       piece_color = (int) piece < (int) BlackPawn;\r
3483 \r
3484 \r
3485       /* [HGM] holdings file: light square or black */\r
3486       if(column == BOARD_LEFT-2) {\r
3487             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3488                 square_color = 1;\r
3489             else {\r
3490                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3491                 continue;\r
3492             }\r
3493       } else\r
3494       if(column == BOARD_RGHT + 1 ) {\r
3495             if( row < gameInfo.holdingsSize )\r
3496                 square_color = 1;\r
3497             else {\r
3498                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3499                 continue;\r
3500             }\r
3501       }\r
3502       if(column == BOARD_LEFT-1 ) /* left align */\r
3503             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3504       else if( column == BOARD_RGHT) /* right align */\r
3505             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3506       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3507       else\r
3508       if (appData.monoMode) {\r
3509         if (piece == EmptySquare) {\r
3510           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3511                  square_color ? WHITENESS : BLACKNESS);\r
3512         } else {\r
3513           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3514         }\r
3515       } \r
3516       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3517           /* [AS] Draw the square using a texture bitmap */\r
3518           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3519           int r = row, c = column; // [HGM] do not flip board in flipView\r
3520           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3521 \r
3522           DrawTile( x, y, \r
3523               squareSize, squareSize, \r
3524               hdc, \r
3525               texture_hdc,\r
3526               backTextureSquareInfo[r][c].mode,\r
3527               backTextureSquareInfo[r][c].x,\r
3528               backTextureSquareInfo[r][c].y );\r
3529 \r
3530           SelectObject( texture_hdc, hbm );\r
3531 \r
3532           if (piece != EmptySquare) {\r
3533               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3534           }\r
3535       }\r
3536       else {\r
3537         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3538 \r
3539         oldBrush = SelectObject(hdc, brush );\r
3540         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3541         SelectObject(hdc, oldBrush);\r
3542         if (piece != EmptySquare)\r
3543           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3544       }\r
3545     }\r
3546   }\r
3547 \r
3548   if( texture_hdc != NULL ) {\r
3549     DeleteDC( texture_hdc );\r
3550   }\r
3551 }\r
3552 \r
3553 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3554 void fputDW(FILE *f, int x)\r
3555 {\r
3556         fputc(x     & 255, f);\r
3557         fputc(x>>8  & 255, f);\r
3558         fputc(x>>16 & 255, f);\r
3559         fputc(x>>24 & 255, f);\r
3560 }\r
3561 \r
3562 #define MAX_CLIPS 200   /* more than enough */\r
3563 \r
3564 VOID\r
3565 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3566 {\r
3567 //  HBITMAP bufferBitmap;\r
3568   BITMAP bi;\r
3569 //  RECT Rect;\r
3570   HDC tmphdc;\r
3571   HBITMAP hbm;\r
3572   int w = 100, h = 50;\r
3573 \r
3574   if(logo == NULL) {\r
3575     if(!logoHeight) return;\r
3576     FillRect( hdc, &logoRect, whitePieceBrush );\r
3577   }\r
3578 //  GetClientRect(hwndMain, &Rect);\r
3579 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3580 //                                      Rect.bottom-Rect.top+1);\r
3581   tmphdc = CreateCompatibleDC(hdc);\r
3582   hbm = SelectObject(tmphdc, logo);\r
3583   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3584             w = bi.bmWidth;\r
3585             h = bi.bmHeight;\r
3586   }\r
3587   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3588                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3589   SelectObject(tmphdc, hbm);\r
3590   DeleteDC(tmphdc);\r
3591 }\r
3592 \r
3593 VOID\r
3594 DisplayLogos()\r
3595 {\r
3596   if(logoHeight) {\r
3597         HDC hdc = GetDC(hwndMain);\r
3598         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3599         if(appData.autoLogo) {\r
3600           \r
3601           switch(gameMode) { // pick logos based on game mode\r
3602             case IcsObserving:\r
3603                 whiteLogo = second.programLogo; // ICS logo\r
3604                 blackLogo = second.programLogo;\r
3605             default:\r
3606                 break;\r
3607             case IcsPlayingWhite:\r
3608                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3609                 blackLogo = second.programLogo; // ICS logo\r
3610                 break;\r
3611             case IcsPlayingBlack:\r
3612                 whiteLogo = second.programLogo; // ICS logo\r
3613                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3614                 break;\r
3615             case TwoMachinesPlay:\r
3616                 if(first.twoMachinesColor[0] == 'b') {\r
3617                     whiteLogo = second.programLogo;\r
3618                     blackLogo = first.programLogo;\r
3619                 }\r
3620                 break;\r
3621             case MachinePlaysWhite:\r
3622                 blackLogo = userLogo;\r
3623                 break;\r
3624             case MachinePlaysBlack:\r
3625                 whiteLogo = userLogo;\r
3626                 blackLogo = first.programLogo;\r
3627           }\r
3628         }\r
3629         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3630         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3631         ReleaseDC(hwndMain, hdc);\r
3632   }\r
3633 }\r
3634 \r
3635 void\r
3636 UpdateLogos(int display)\r
3637 { // called after loading new engine(s), in tourney or from menu\r
3638   LoadLogo(&first, 0, FALSE);\r
3639   LoadLogo(&second, 1, appData.icsActive);\r
3640   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3641   if(display) DisplayLogos();\r
3642 }\r
3643 \r
3644 static HDC hdcSeek;\r
3645 \r
3646 // [HGM] seekgraph\r
3647 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3648 {\r
3649     POINT stPt;\r
3650     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3651     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3652     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3653     SelectObject( hdcSeek, hp );\r
3654 }\r
3655 \r
3656 // front-end wrapper for drawing functions to do rectangles\r
3657 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3658 {\r
3659     HPEN hp;\r
3660     RECT rc;\r
3661 \r
3662     if (hdcSeek == NULL) {\r
3663     hdcSeek = GetDC(hwndMain);\r
3664       if (!appData.monoMode) {\r
3665         SelectPalette(hdcSeek, hPal, FALSE);\r
3666         RealizePalette(hdcSeek);\r
3667       }\r
3668     }\r
3669     hp = SelectObject( hdcSeek, gridPen );\r
3670     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3671     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3672     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3673     SelectObject( hdcSeek, hp );\r
3674 }\r
3675 \r
3676 // front-end wrapper for putting text in graph\r
3677 void DrawSeekText(char *buf, int x, int y)\r
3678 {\r
3679         SIZE stSize;\r
3680         SetBkMode( hdcSeek, TRANSPARENT );\r
3681         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3682         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3683 }\r
3684 \r
3685 void DrawSeekDot(int x, int y, int color)\r
3686 {\r
3687         int square = color & 0x80;\r
3688         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3689                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3690         color &= 0x7F;\r
3691         if(square)\r
3692             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3693                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3694         else\r
3695             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3696                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3697             SelectObject(hdcSeek, oldBrush);\r
3698 }\r
3699 \r
3700 void DrawSeekOpen()\r
3701 {\r
3702 }\r
3703 \r
3704 void DrawSeekClose()\r
3705 {\r
3706 }\r
3707 \r
3708 VOID\r
3709 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3710 {\r
3711   static Board lastReq[2], lastDrawn[2];\r
3712   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3713   static int lastDrawnFlipView = 0;\r
3714   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3715   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3716   HDC tmphdc;\r
3717   HDC hdcmem;\r
3718   HBITMAP bufferBitmap;\r
3719   HBITMAP oldBitmap;\r
3720   RECT Rect;\r
3721   HRGN clips[MAX_CLIPS];\r
3722   ChessSquare dragged_piece = EmptySquare;\r
3723   int nr = twoBoards*partnerUp;\r
3724 \r
3725   /* I'm undecided on this - this function figures out whether a full\r
3726    * repaint is necessary on its own, so there's no real reason to have the\r
3727    * caller tell it that.  I think this can safely be set to FALSE - but\r
3728    * if we trust the callers not to request full repaints unnessesarily, then\r
3729    * we could skip some clipping work.  In other words, only request a full\r
3730    * redraw when the majority of pieces have changed positions (ie. flip, \r
3731    * gamestart and similar)  --Hawk\r
3732    */\r
3733   Boolean fullrepaint = repaint;\r
3734 \r
3735   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3736 \r
3737   if( DrawPositionNeedsFullRepaint() ) {\r
3738       fullrepaint = TRUE;\r
3739   }\r
3740 \r
3741   if (board == NULL) {\r
3742     if (!lastReqValid[nr]) {\r
3743       return;\r
3744     }\r
3745     board = lastReq[nr];\r
3746   } else {\r
3747     CopyBoard(lastReq[nr], board);\r
3748     lastReqValid[nr] = 1;\r
3749   }\r
3750 \r
3751   if (doingSizing) {\r
3752     return;\r
3753   }\r
3754 \r
3755   if (IsIconic(hwndMain)) {\r
3756     return;\r
3757   }\r
3758 \r
3759   if (hdc == NULL) {\r
3760     hdc = GetDC(hwndMain);\r
3761     if (!appData.monoMode) {\r
3762       SelectPalette(hdc, hPal, FALSE);\r
3763       RealizePalette(hdc);\r
3764     }\r
3765     releaseDC = TRUE;\r
3766   } else {\r
3767     releaseDC = FALSE;\r
3768   }\r
3769 \r
3770   /* Create some work-DCs */\r
3771   hdcmem = CreateCompatibleDC(hdc);\r
3772   tmphdc = CreateCompatibleDC(hdc);\r
3773 \r
3774   /* If dragging is in progress, we temporarely remove the piece */\r
3775   /* [HGM] or temporarily decrease count if stacked              */\r
3776   /*       !! Moved to before board compare !!                   */\r
3777   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3778     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3779     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3780             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3781         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3782     } else \r
3783     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3784             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3785         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3786     } else \r
3787         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3788   }\r
3789 \r
3790   /* Figure out which squares need updating by comparing the \r
3791    * newest board with the last drawn board and checking if\r
3792    * flipping has changed.\r
3793    */\r
3794   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3795     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3796       for (column = 0; column < BOARD_WIDTH; column++) {\r
3797         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3798           SquareToPos(row, column, &x, &y);\r
3799           clips[num_clips++] =\r
3800             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3801         }\r
3802       }\r
3803     }\r
3804    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3805     for (i=0; i<2; i++) {\r
3806       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3807           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3808         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3809             lastDrawnHighlight.sq[i].y >= 0) {\r
3810           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3811                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3812           clips[num_clips++] =\r
3813             CreateRectRgn(x - lineGap, y - lineGap, \r
3814                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3815         }\r
3816         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3817           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3818           clips[num_clips++] =\r
3819             CreateRectRgn(x - lineGap, y - lineGap, \r
3820                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3821         }\r
3822       }\r
3823     }\r
3824     for (i=0; i<2; i++) {\r
3825       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3826           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3827         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3828             lastDrawnPremove.sq[i].y >= 0) {\r
3829           SquareToPos(lastDrawnPremove.sq[i].y,\r
3830                       lastDrawnPremove.sq[i].x, &x, &y);\r
3831           clips[num_clips++] =\r
3832             CreateRectRgn(x - lineGap, y - lineGap, \r
3833                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3834         }\r
3835         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3836             premoveHighlightInfo.sq[i].y >= 0) {\r
3837           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3838                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3839           clips[num_clips++] =\r
3840             CreateRectRgn(x - lineGap, y - lineGap, \r
3841                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3842         }\r
3843       }\r
3844     }\r
3845    } else { // nr == 1\r
3846         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3847         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3848         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3849         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3850       for (i=0; i<2; i++) {\r
3851         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3852             partnerHighlightInfo.sq[i].y >= 0) {\r
3853           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3854                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3855           clips[num_clips++] =\r
3856             CreateRectRgn(x - lineGap, y - lineGap, \r
3857                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3858         }\r
3859         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3860             oldPartnerHighlight.sq[i].y >= 0) {\r
3861           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3862                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3863           clips[num_clips++] =\r
3864             CreateRectRgn(x - lineGap, y - lineGap, \r
3865                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3866         }\r
3867       }\r
3868    }\r
3869   } else {\r
3870     fullrepaint = TRUE;\r
3871   }\r
3872 \r
3873   /* Create a buffer bitmap - this is the actual bitmap\r
3874    * being written to.  When all the work is done, we can\r
3875    * copy it to the real DC (the screen).  This avoids\r
3876    * the problems with flickering.\r
3877    */\r
3878   GetClientRect(hwndMain, &Rect);\r
3879   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3880                                         Rect.bottom-Rect.top+1);\r
3881   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3882   if (!appData.monoMode) {\r
3883     SelectPalette(hdcmem, hPal, FALSE);\r
3884   }\r
3885 \r
3886   /* Create clips for dragging */\r
3887   if (!fullrepaint) {\r
3888     if (dragInfo.from.x >= 0) {\r
3889       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3890       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3891     }\r
3892     if (dragInfo.start.x >= 0) {\r
3893       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3894       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3895     }\r
3896     if (dragInfo.pos.x >= 0) {\r
3897       x = dragInfo.pos.x - squareSize / 2;\r
3898       y = dragInfo.pos.y - squareSize / 2;\r
3899       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3900     }\r
3901     if (dragInfo.lastpos.x >= 0) {\r
3902       x = dragInfo.lastpos.x - squareSize / 2;\r
3903       y = dragInfo.lastpos.y - squareSize / 2;\r
3904       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3905     }\r
3906   }\r
3907 \r
3908   /* Are we animating a move?  \r
3909    * If so, \r
3910    *   - remove the piece from the board (temporarely)\r
3911    *   - calculate the clipping region\r
3912    */\r
3913   if (!fullrepaint) {\r
3914     if (animInfo.piece != EmptySquare) {\r
3915       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3916       x = boardRect.left + animInfo.lastpos.x;\r
3917       y = boardRect.top + animInfo.lastpos.y;\r
3918       x2 = boardRect.left + animInfo.pos.x;\r
3919       y2 = boardRect.top + animInfo.pos.y;\r
3920       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3921       /* Slight kludge.  The real problem is that after AnimateMove is\r
3922          done, the position on the screen does not match lastDrawn.\r
3923          This currently causes trouble only on e.p. captures in\r
3924          atomic, where the piece moves to an empty square and then\r
3925          explodes.  The old and new positions both had an empty square\r
3926          at the destination, but animation has drawn a piece there and\r
3927          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3928       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3929     }\r
3930   }\r
3931 \r
3932   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3933   if (num_clips == 0)\r
3934     fullrepaint = TRUE;\r
3935 \r
3936   /* Set clipping on the memory DC */\r
3937   if (!fullrepaint) {\r
3938     SelectClipRgn(hdcmem, clips[0]);\r
3939     for (x = 1; x < num_clips; x++) {\r
3940       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3941         abort();  // this should never ever happen!\r
3942     }\r
3943   }\r
3944 \r
3945   /* Do all the drawing to the memory DC */\r
3946   if(explodeInfo.radius) { // [HGM] atomic\r
3947         HBRUSH oldBrush;\r
3948         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3949         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3950         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3951         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3952         x += squareSize/2;\r
3953         y += squareSize/2;\r
3954         if(!fullrepaint) {\r
3955           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3956           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3957         }\r
3958         DrawGridOnDC(hdcmem);\r
3959         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3960         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3961         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3962         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3963         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3964         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3965         SelectObject(hdcmem, oldBrush);\r
3966   } else {\r
3967     if(border) DrawBackgroundOnDC(hdcmem);\r
3968     DrawGridOnDC(hdcmem);\r
3969     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3970         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3971         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3972     } else {\r
3973         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3974         oldPartnerHighlight = partnerHighlightInfo;\r
3975     }\r
3976     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3977   }\r
3978   if(nr == 0) // [HGM] dual: markers only on left board\r
3979   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3980     for (column = 0; column < BOARD_WIDTH; column++) {\r
3981         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3982             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3983             SquareToPos(row, column, &x, &y);\r
3984             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3985                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3986             SelectObject(hdcmem, oldBrush);\r
3987         }\r
3988     }\r
3989   }\r
3990 \r
3991   if( appData.highlightMoveWithArrow ) {\r
3992     DrawArrowHighlight(hdcmem);\r
3993   }\r
3994 \r
3995   DrawCoordsOnDC(hdcmem);\r
3996 \r
3997   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3998                  /* to make sure lastDrawn contains what is actually drawn */\r
3999 \r
4000   /* Put the dragged piece back into place and draw it (out of place!) */\r
4001     if (dragged_piece != EmptySquare) {\r
4002     /* [HGM] or restack */\r
4003     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4004                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4005     else\r
4006     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4007                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4008 \r
4009     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4010     x = dragInfo.pos.x - squareSize / 2;\r
4011     y = dragInfo.pos.y - squareSize / 2;\r
4012     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4013                   ((int) dragInfo.piece < (int) BlackPawn), \r
4014                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4015   }   \r
4016   \r
4017   /* Put the animated piece back into place and draw it */\r
4018   if (animInfo.piece != EmptySquare) {\r
4019     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4020     x = boardRect.left + animInfo.pos.x;\r
4021     y = boardRect.top + animInfo.pos.y;\r
4022     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4023                   ((int) animInfo.piece < (int) BlackPawn),\r
4024                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4025   }\r
4026 \r
4027   /* Release the bufferBitmap by selecting in the old bitmap \r
4028    * and delete the memory DC\r
4029    */\r
4030   SelectObject(hdcmem, oldBitmap);\r
4031   DeleteDC(hdcmem);\r
4032 \r
4033   /* Set clipping on the target DC */\r
4034   if (!fullrepaint) {\r
4035     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4036         RECT rect;\r
4037         GetRgnBox(clips[x], &rect);\r
4038         DeleteObject(clips[x]);\r
4039         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4040                           rect.right + wpMain.width/2, rect.bottom);\r
4041     }\r
4042     SelectClipRgn(hdc, clips[0]);\r
4043     for (x = 1; x < num_clips; x++) {\r
4044       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4045         abort();   // this should never ever happen!\r
4046     } \r
4047   }\r
4048 \r
4049   /* Copy the new bitmap onto the screen in one go.\r
4050    * This way we avoid any flickering\r
4051    */\r
4052   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4053   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4054          boardRect.right - boardRect.left,\r
4055          boardRect.bottom - boardRect.top,\r
4056          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4057   if(saveDiagFlag) { \r
4058     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4059     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4060 \r
4061     GetObject(bufferBitmap, sizeof(b), &b);\r
4062     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4063         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4064         bih.biWidth = b.bmWidth;\r
4065         bih.biHeight = b.bmHeight;\r
4066         bih.biPlanes = 1;\r
4067         bih.biBitCount = b.bmBitsPixel;\r
4068         bih.biCompression = 0;\r
4069         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4070         bih.biXPelsPerMeter = 0;\r
4071         bih.biYPelsPerMeter = 0;\r
4072         bih.biClrUsed = 0;\r
4073         bih.biClrImportant = 0;\r
4074 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4075 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4076         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4077 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4078 \r
4079         wb = b.bmWidthBytes;\r
4080         // count colors\r
4081         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4082                 int k = ((int*) pData)[i];\r
4083                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4084                 if(j >= 16) break;\r
4085                 color[j] = k;\r
4086                 if(j >= nrColors) nrColors = j+1;\r
4087         }\r
4088         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4089                 INT p = 0;\r
4090                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4091                     for(w=0; w<(wb>>2); w+=2) {\r
4092                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4093                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4094                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4095                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4096                         pData[p++] = m | j<<4;\r
4097                     }\r
4098                     while(p&3) pData[p++] = 0;\r
4099                 }\r
4100                 fac = 3;\r
4101                 wb = ((wb+31)>>5)<<2;\r
4102         }\r
4103         // write BITMAPFILEHEADER\r
4104         fprintf(diagFile, "BM");\r
4105         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4106         fputDW(diagFile, 0);\r
4107         fputDW(diagFile, 0x36 + (fac?64:0));\r
4108         // write BITMAPINFOHEADER\r
4109         fputDW(diagFile, 40);\r
4110         fputDW(diagFile, b.bmWidth);\r
4111         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4112         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4113         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4114         fputDW(diagFile, 0);\r
4115         fputDW(diagFile, 0);\r
4116         fputDW(diagFile, 0);\r
4117         fputDW(diagFile, 0);\r
4118         fputDW(diagFile, 0);\r
4119         fputDW(diagFile, 0);\r
4120         // write color table\r
4121         if(fac)\r
4122         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4123         // write bitmap data\r
4124         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4125                 fputc(pData[i], diagFile);\r
4126         free(pData);\r
4127      }\r
4128   }\r
4129 \r
4130   SelectObject(tmphdc, oldBitmap);\r
4131 \r
4132   /* Massive cleanup */\r
4133   for (x = 0; x < num_clips; x++)\r
4134     DeleteObject(clips[x]);\r
4135 \r
4136   DeleteDC(tmphdc);\r
4137   DeleteObject(bufferBitmap);\r
4138 \r
4139   if (releaseDC) \r
4140     ReleaseDC(hwndMain, hdc);\r
4141   \r
4142   if (lastDrawnFlipView != flipView && nr == 0) {\r
4143     if (flipView)\r
4144       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4145     else\r
4146       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4147   }\r
4148 \r
4149 /*  CopyBoard(lastDrawn, board);*/\r
4150   lastDrawnHighlight = highlightInfo;\r
4151   lastDrawnPremove   = premoveHighlightInfo;\r
4152   lastDrawnFlipView = flipView;\r
4153   lastDrawnValid[nr] = 1;\r
4154 }\r
4155 \r
4156 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4157 int\r
4158 SaveDiagram(f)\r
4159      FILE *f;\r
4160 {\r
4161     saveDiagFlag = 1; diagFile = f;\r
4162     HDCDrawPosition(NULL, TRUE, NULL);\r
4163     saveDiagFlag = 0;\r
4164 \r
4165     fclose(f);\r
4166     return TRUE;\r
4167 }\r
4168 \r
4169 \r
4170 /*---------------------------------------------------------------------------*\\r
4171 | CLIENT PAINT PROCEDURE\r
4172 |   This is the main event-handler for the WM_PAINT message.\r
4173 |\r
4174 \*---------------------------------------------------------------------------*/\r
4175 VOID\r
4176 PaintProc(HWND hwnd)\r
4177 {\r
4178   HDC         hdc;\r
4179   PAINTSTRUCT ps;\r
4180   HFONT       oldFont;\r
4181 \r
4182   if((hdc = BeginPaint(hwnd, &ps))) {\r
4183     if (IsIconic(hwnd)) {\r
4184       DrawIcon(hdc, 2, 2, iconCurrent);\r
4185     } else {\r
4186       if (!appData.monoMode) {\r
4187         SelectPalette(hdc, hPal, FALSE);\r
4188         RealizePalette(hdc);\r
4189       }\r
4190       HDCDrawPosition(hdc, 1, NULL);\r
4191       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4192         flipView = !flipView; partnerUp = !partnerUp;\r
4193         HDCDrawPosition(hdc, 1, NULL);\r
4194         flipView = !flipView; partnerUp = !partnerUp;\r
4195       }\r
4196       oldFont =\r
4197         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4198       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4199                  ETO_CLIPPED|ETO_OPAQUE,\r
4200                  &messageRect, messageText, strlen(messageText), NULL);\r
4201       SelectObject(hdc, oldFont);\r
4202       DisplayBothClocks();\r
4203       DisplayLogos();\r
4204     }\r
4205     EndPaint(hwnd,&ps);\r
4206   }\r
4207 \r
4208   return;\r
4209 }\r
4210 \r
4211 \r
4212 /*\r
4213  * If the user selects on a border boundary, return -1; if off the board,\r
4214  *   return -2.  Otherwise map the event coordinate to the square.\r
4215  * The offset boardRect.left or boardRect.top must already have been\r
4216  *   subtracted from x.\r
4217  */\r
4218 int EventToSquare(x, limit)\r
4219      int x, limit;\r
4220 {\r
4221   if (x <= border)\r
4222     return -2;\r
4223   if (x < lineGap + border)\r
4224     return -1;\r
4225   x -= lineGap + border;\r
4226   if ((x % (squareSize + lineGap)) >= squareSize)\r
4227     return -1;\r
4228   x /= (squareSize + lineGap);\r
4229     if (x >= limit)\r
4230     return -2;\r
4231   return x;\r
4232 }\r
4233 \r
4234 typedef struct {\r
4235   char piece;\r
4236   int command;\r
4237   char* name;\r
4238 } DropEnable;\r
4239 \r
4240 DropEnable dropEnables[] = {\r
4241   { 'P', DP_Pawn, N_("Pawn") },\r
4242   { 'N', DP_Knight, N_("Knight") },\r
4243   { 'B', DP_Bishop, N_("Bishop") },\r
4244   { 'R', DP_Rook, N_("Rook") },\r
4245   { 'Q', DP_Queen, N_("Queen") },\r
4246 };\r
4247 \r
4248 VOID\r
4249 SetupDropMenu(HMENU hmenu)\r
4250 {\r
4251   int i, count, enable;\r
4252   char *p;\r
4253   extern char white_holding[], black_holding[];\r
4254   char item[MSG_SIZ];\r
4255 \r
4256   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4257     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4258                dropEnables[i].piece);\r
4259     count = 0;\r
4260     while (p && *p++ == dropEnables[i].piece) count++;\r
4261       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4262     enable = count > 0 || !appData.testLegality\r
4263       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4264                       && !appData.icsActive);\r
4265     ModifyMenu(hmenu, dropEnables[i].command,\r
4266                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4267                dropEnables[i].command, item);\r
4268   }\r
4269 }\r
4270 \r
4271 void DragPieceBegin(int x, int y, Boolean instantly)\r
4272 {\r
4273       dragInfo.lastpos.x = boardRect.left + x;\r
4274       dragInfo.lastpos.y = boardRect.top + y;\r
4275       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4276       dragInfo.from.x = fromX;\r
4277       dragInfo.from.y = fromY;\r
4278       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4279       dragInfo.start = dragInfo.from;\r
4280       SetCapture(hwndMain);\r
4281 }\r
4282 \r
4283 void DragPieceEnd(int x, int y)\r
4284 {\r
4285     ReleaseCapture();\r
4286     dragInfo.start.x = dragInfo.start.y = -1;\r
4287     dragInfo.from = dragInfo.start;\r
4288     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4289 }\r
4290 \r
4291 void ChangeDragPiece(ChessSquare piece)\r
4292 {\r
4293     dragInfo.piece = piece;\r
4294 }\r
4295 \r
4296 /* Event handler for mouse messages */\r
4297 VOID\r
4298 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4299 {\r
4300   int x, y, menuNr;\r
4301   POINT pt;\r
4302   static int recursive = 0;\r
4303   HMENU hmenu;\r
4304   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4305 \r
4306   if (recursive) {\r
4307     if (message == WM_MBUTTONUP) {\r
4308       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4309          to the middle button: we simulate pressing the left button too!\r
4310          */\r
4311       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4312       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4313     }\r
4314     return;\r
4315   }\r
4316   recursive++;\r
4317   \r
4318   pt.x = LOWORD(lParam);\r
4319   pt.y = HIWORD(lParam);\r
4320   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4321   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4322   if (!flipView && y >= 0) {\r
4323     y = BOARD_HEIGHT - 1 - y;\r
4324   }\r
4325   if (flipView && x >= 0) {\r
4326     x = BOARD_WIDTH - 1 - x;\r
4327   }\r
4328 \r
4329   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4330   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4331 \r
4332   switch (message) {\r
4333   case WM_LBUTTONDOWN:\r
4334       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4335         ClockClick(flipClock); break;\r
4336       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4337         ClockClick(!flipClock); break;\r
4338       }\r
4339     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4340       dragInfo.start.x = dragInfo.start.y = -1;\r
4341       dragInfo.from = dragInfo.start;\r
4342     }\r
4343     if(fromX == -1 && frozen) { // not sure where this is for\r
4344                 fromX = fromY = -1; \r
4345       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4346       break;\r
4347     }\r
4348       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4349       DrawPosition(TRUE, NULL);\r
4350     break;\r
4351 \r
4352   case WM_LBUTTONUP:\r
4353       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4354       DrawPosition(TRUE, NULL);\r
4355     break;\r
4356 \r
4357   case WM_MOUSEMOVE:\r
4358     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4359     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4360     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4361     if ((appData.animateDragging || appData.highlightDragging)\r
4362         && (wParam & MK_LBUTTON || dragging == 2)\r
4363         && dragInfo.from.x >= 0) \r
4364     {\r
4365       BOOL full_repaint = FALSE;\r
4366 \r
4367       if (appData.animateDragging) {\r
4368         dragInfo.pos = pt;\r
4369       }\r
4370       if (appData.highlightDragging) {\r
4371         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4372         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4373             full_repaint = TRUE;\r
4374         }\r
4375       }\r
4376       \r
4377       DrawPosition( full_repaint, NULL);\r
4378       \r
4379       dragInfo.lastpos = dragInfo.pos;\r
4380     }\r
4381     break;\r
4382 \r
4383   case WM_MOUSEWHEEL: // [DM]\r
4384     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4385        /* Mouse Wheel is being rolled forward\r
4386         * Play moves forward\r
4387         */\r
4388        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4389                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4390        /* Mouse Wheel is being rolled backward\r
4391         * Play moves backward\r
4392         */\r
4393        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4394                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4395     }\r
4396     break;\r
4397 \r
4398   case WM_MBUTTONUP:\r
4399   case WM_RBUTTONUP:\r
4400     ReleaseCapture();\r
4401     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4402     break;\r
4403  \r
4404   case WM_MBUTTONDOWN:\r
4405   case WM_RBUTTONDOWN:\r
4406     ErrorPopDown();\r
4407     ReleaseCapture();\r
4408     fromX = fromY = -1;\r
4409     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4410     dragInfo.start.x = dragInfo.start.y = -1;\r
4411     dragInfo.from = dragInfo.start;\r
4412     dragInfo.lastpos = dragInfo.pos;\r
4413     if (appData.highlightDragging) {\r
4414       ClearHighlights();\r
4415     }\r
4416     if(y == -2) {\r
4417       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4418       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4419           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4420       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4421           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4422       }\r
4423       break;\r
4424     }\r
4425     DrawPosition(TRUE, NULL);\r
4426 \r
4427     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4428     switch (menuNr) {\r
4429     case 0:\r
4430       if (message == WM_MBUTTONDOWN) {\r
4431         buttonCount = 3;  /* even if system didn't think so */\r
4432         if (wParam & MK_SHIFT) \r
4433           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4434         else\r
4435           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4436       } else { /* message == WM_RBUTTONDOWN */\r
4437         /* Just have one menu, on the right button.  Windows users don't\r
4438            think to try the middle one, and sometimes other software steals\r
4439            it, or it doesn't really exist. */\r
4440         if(gameInfo.variant != VariantShogi)\r
4441             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4442         else\r
4443             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4444       }\r
4445       break;\r
4446     case 2:\r
4447       SetCapture(hwndMain);\r
4448       break;\r
4449     case 1:\r
4450       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4451       SetupDropMenu(hmenu);\r
4452       MenuPopup(hwnd, pt, hmenu, -1);\r
4453     default:\r
4454       break;\r
4455     }\r
4456     break;\r
4457   }\r
4458 \r
4459   recursive--;\r
4460 }\r
4461 \r
4462 /* Preprocess messages for buttons in main window */\r
4463 LRESULT CALLBACK\r
4464 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4465 {\r
4466   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4467   int i, dir;\r
4468 \r
4469   for (i=0; i<N_BUTTONS; i++) {\r
4470     if (buttonDesc[i].id == id) break;\r
4471   }\r
4472   if (i == N_BUTTONS) return 0;\r
4473   switch (message) {\r
4474   case WM_KEYDOWN:\r
4475     switch (wParam) {\r
4476     case VK_LEFT:\r
4477     case VK_RIGHT:\r
4478       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4479       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4480       return TRUE;\r
4481     }\r
4482     break;\r
4483   case WM_CHAR:\r
4484     switch (wParam) {\r
4485     case '\r':\r
4486       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4487       return TRUE;\r
4488     default:\r
4489       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4490         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4491         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4492         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4493         SetFocus(h);\r
4494         SendMessage(h, WM_CHAR, wParam, lParam);\r
4495         return TRUE;\r
4496       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4497         TypeInEvent((char)wParam);\r
4498       }\r
4499       break;\r
4500     }\r
4501     break;\r
4502   }\r
4503   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4504 }\r
4505 \r
4506 static int promoStyle;\r
4507 \r
4508 /* Process messages for Promotion dialog box */\r
4509 LRESULT CALLBACK\r
4510 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4511 {\r
4512   char promoChar;\r
4513 \r
4514   switch (message) {\r
4515   case WM_INITDIALOG: /* message: initialize dialog box */\r
4516     /* Center the dialog over the application window */\r
4517     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4518     Translate(hDlg, DLG_PromotionKing);\r
4519     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4520       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4521        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4522        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4523                SW_SHOW : SW_HIDE);\r
4524     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4525     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4526        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4527          PieceToChar(WhiteAngel) != '~') ||\r
4528         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4529          PieceToChar(BlackAngel) != '~')   ) ?\r
4530                SW_SHOW : SW_HIDE);\r
4531     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4532        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4533          PieceToChar(WhiteMarshall) != '~') ||\r
4534         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4535          PieceToChar(BlackMarshall) != '~')   ) ?\r
4536                SW_SHOW : SW_HIDE);\r
4537     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4538     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4539     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4540     if(promoStyle) {\r
4541         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4542         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4543         SetWindowText(hDlg, "Promote?");\r
4544     }\r
4545     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4546        gameInfo.variant == VariantSuper ?\r
4547                SW_SHOW : SW_HIDE);\r
4548     return TRUE;\r
4549 \r
4550   case WM_COMMAND: /* message: received a command */\r
4551     switch (LOWORD(wParam)) {\r
4552     case IDCANCEL:\r
4553       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4554       ClearHighlights();\r
4555       DrawPosition(FALSE, NULL);\r
4556       return TRUE;\r
4557     case PB_King:\r
4558       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4559       break;\r
4560     case PB_Queen:\r
4561       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4562       break;\r
4563     case PB_Rook:\r
4564       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4565       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4566       break;\r
4567     case PB_Bishop:\r
4568       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4569       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4570       break;\r
4571     case PB_Chancellor:\r
4572       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4573       break;\r
4574     case PB_Archbishop:\r
4575       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4576       break;\r
4577     case PB_Knight:\r
4578       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4579                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4580       break;\r
4581     default:\r
4582       return FALSE;\r
4583     }\r
4584     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4585     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4586     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4587     fromX = fromY = -1;\r
4588     if (!appData.highlightLastMove) {\r
4589       ClearHighlights();\r
4590       DrawPosition(FALSE, NULL);\r
4591     }\r
4592     return TRUE;\r
4593   }\r
4594   return FALSE;\r
4595 }\r
4596 \r
4597 /* Pop up promotion dialog */\r
4598 VOID\r
4599 PromotionPopup(HWND hwnd)\r
4600 {\r
4601   FARPROC lpProc;\r
4602 \r
4603   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4604   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4605     hwnd, (DLGPROC)lpProc);\r
4606   FreeProcInstance(lpProc);\r
4607 }\r
4608 \r
4609 void\r
4610 PromotionPopUp(char choice)\r
4611 {\r
4612   promoStyle = (choice == '+');\r
4613   DrawPosition(TRUE, NULL);\r
4614   PromotionPopup(hwndMain);\r
4615 }\r
4616 \r
4617 VOID\r
4618 LoadGameDialog(HWND hwnd, char* title)\r
4619 {\r
4620   UINT number = 0;\r
4621   FILE *f;\r
4622   char fileTitle[MSG_SIZ];\r
4623   f = OpenFileDialog(hwnd, "rb", "",\r
4624                      appData.oldSaveStyle ? "gam" : "pgn",\r
4625                      GAME_FILT,\r
4626                      title, &number, fileTitle, NULL);\r
4627   if (f != NULL) {\r
4628     cmailMsgLoaded = FALSE;\r
4629     if (number == 0) {\r
4630       int error = GameListBuild(f);\r
4631       if (error) {\r
4632         DisplayError(_("Cannot build game list"), error);\r
4633       } else if (!ListEmpty(&gameList) &&\r
4634                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4635         GameListPopUp(f, fileTitle);\r
4636         return;\r
4637       }\r
4638       GameListDestroy();\r
4639       number = 1;\r
4640     }\r
4641     LoadGame(f, number, fileTitle, FALSE);\r
4642   }\r
4643 }\r
4644 \r
4645 int get_term_width()\r
4646 {\r
4647     HDC hdc;\r
4648     TEXTMETRIC tm;\r
4649     RECT rc;\r
4650     HFONT hfont, hold_font;\r
4651     LOGFONT lf;\r
4652     HWND hText;\r
4653 \r
4654     if (hwndConsole)\r
4655         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4656     else\r
4657         return 79;\r
4658 \r
4659     // get the text metrics\r
4660     hdc = GetDC(hText);\r
4661     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4662     if (consoleCF.dwEffects & CFE_BOLD)\r
4663         lf.lfWeight = FW_BOLD;\r
4664     if (consoleCF.dwEffects & CFE_ITALIC)\r
4665         lf.lfItalic = TRUE;\r
4666     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4667         lf.lfStrikeOut = TRUE;\r
4668     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4669         lf.lfUnderline = TRUE;\r
4670     hfont = CreateFontIndirect(&lf);\r
4671     hold_font = SelectObject(hdc, hfont);\r
4672     GetTextMetrics(hdc, &tm);\r
4673     SelectObject(hdc, hold_font);\r
4674     DeleteObject(hfont);\r
4675     ReleaseDC(hText, hdc);\r
4676 \r
4677     // get the rectangle\r
4678     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4679 \r
4680     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4681 }\r
4682 \r
4683 void UpdateICSWidth(HWND hText)\r
4684 {\r
4685     LONG old_width, new_width;\r
4686 \r
4687     new_width = get_term_width(hText, FALSE);\r
4688     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4689     if (new_width != old_width)\r
4690     {\r
4691         ics_update_width(new_width);\r
4692         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4693     }\r
4694 }\r
4695 \r
4696 VOID\r
4697 ChangedConsoleFont()\r
4698 {\r
4699   CHARFORMAT cfmt;\r
4700   CHARRANGE tmpsel, sel;\r
4701   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4702   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4703   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4704   PARAFORMAT paraf;\r
4705 \r
4706   cfmt.cbSize = sizeof(CHARFORMAT);\r
4707   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4708     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4709                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4710   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4711    * size.  This was undocumented in the version of MSVC++ that I had\r
4712    * when I wrote the code, but is apparently documented now.\r
4713    */\r
4714   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4715   cfmt.bCharSet = f->lf.lfCharSet;\r
4716   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4717   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4718   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4719   /* Why are the following seemingly needed too? */\r
4720   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4721   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4722   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4723   tmpsel.cpMin = 0;\r
4724   tmpsel.cpMax = -1; /*999999?*/\r
4725   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4726   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4727   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4728    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4729    */\r
4730   paraf.cbSize = sizeof(paraf);\r
4731   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4732   paraf.dxStartIndent = 0;\r
4733   paraf.dxOffset = WRAP_INDENT;\r
4734   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4735   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4736   UpdateICSWidth(hText);\r
4737 }\r
4738 \r
4739 /*---------------------------------------------------------------------------*\\r
4740  *\r
4741  * Window Proc for main window\r
4742  *\r
4743 \*---------------------------------------------------------------------------*/\r
4744 \r
4745 /* Process messages for main window, etc. */\r
4746 LRESULT CALLBACK\r
4747 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4748 {\r
4749   FARPROC lpProc;\r
4750   int wmId;\r
4751   char *defName;\r
4752   FILE *f;\r
4753   UINT number;\r
4754   char fileTitle[MSG_SIZ];\r
4755   static SnapData sd;\r
4756   static int peek=0;\r
4757 \r
4758   switch (message) {\r
4759 \r
4760   case WM_PAINT: /* message: repaint portion of window */\r
4761     PaintProc(hwnd);\r
4762     break;\r
4763 \r
4764   case WM_ERASEBKGND:\r
4765     if (IsIconic(hwnd)) {\r
4766       /* Cheat; change the message */\r
4767       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4768     } else {\r
4769       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4770     }\r
4771     break;\r
4772 \r
4773   case WM_LBUTTONDOWN:\r
4774   case WM_MBUTTONDOWN:\r
4775   case WM_RBUTTONDOWN:\r
4776   case WM_LBUTTONUP:\r
4777   case WM_MBUTTONUP:\r
4778   case WM_RBUTTONUP:\r
4779   case WM_MOUSEMOVE:\r
4780   case WM_MOUSEWHEEL:\r
4781     MouseEvent(hwnd, message, wParam, lParam);\r
4782     break;\r
4783 \r
4784   case WM_KEYUP:\r
4785     if((char)wParam == '\b') {\r
4786       ForwardEvent(); peek = 0;\r
4787     }\r
4788 \r
4789     JAWS_KBUP_NAVIGATION\r
4790 \r
4791     break;\r
4792 \r
4793   case WM_KEYDOWN:\r
4794     if((char)wParam == '\b') {\r
4795       if(!peek) BackwardEvent(), peek = 1;\r
4796     }\r
4797 \r
4798     JAWS_KBDOWN_NAVIGATION\r
4799 \r
4800     break;\r
4801 \r
4802   case WM_CHAR:\r
4803     \r
4804     JAWS_ALT_INTERCEPT\r
4805 \r
4806     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4807         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4808         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4809         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4810         SetFocus(h);\r
4811         SendMessage(h, message, wParam, lParam);\r
4812     } else if(lParam != KF_REPEAT) {\r
4813         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4814                 TypeInEvent((char)wParam);\r
4815         } else if((char)wParam == 003) CopyGameToClipboard();\r
4816          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4817     }\r
4818 \r
4819     break;\r
4820 \r
4821   case WM_PALETTECHANGED:\r
4822     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4823       int nnew;\r
4824       HDC hdc = GetDC(hwndMain);\r
4825       SelectPalette(hdc, hPal, TRUE);\r
4826       nnew = RealizePalette(hdc);\r
4827       if (nnew > 0) {\r
4828         paletteChanged = TRUE;\r
4829 \r
4830         InvalidateRect(hwnd, &boardRect, FALSE);\r
4831       }\r
4832       ReleaseDC(hwnd, hdc);\r
4833     }\r
4834     break;\r
4835 \r
4836   case WM_QUERYNEWPALETTE:\r
4837     if (!appData.monoMode /*&& paletteChanged*/) {\r
4838       int nnew;\r
4839       HDC hdc = GetDC(hwndMain);\r
4840       paletteChanged = FALSE;\r
4841       SelectPalette(hdc, hPal, FALSE);\r
4842       nnew = RealizePalette(hdc);\r
4843       if (nnew > 0) {\r
4844         InvalidateRect(hwnd, &boardRect, FALSE);\r
4845       }\r
4846       ReleaseDC(hwnd, hdc);\r
4847       return TRUE;\r
4848     }\r
4849     return FALSE;\r
4850 \r
4851   case WM_COMMAND: /* message: command from application menu */\r
4852     wmId    = LOWORD(wParam);\r
4853 \r
4854     switch (wmId) {\r
4855     case IDM_NewGame:\r
4856       ResetGameEvent();\r
4857       SAY("new game enter a move to play against the computer with white");\r
4858       break;\r
4859 \r
4860     case IDM_NewGameFRC:\r
4861       if( NewGameFRC() == 0 ) {\r
4862         ResetGameEvent();\r
4863       }\r
4864       break;\r
4865 \r
4866     case IDM_NewVariant:\r
4867       NewVariantPopup(hwnd);\r
4868       break;\r
4869 \r
4870     case IDM_LoadGame:\r
4871       LoadGameDialog(hwnd, _("Load Game from File"));\r
4872       break;\r
4873 \r
4874     case IDM_LoadNextGame:\r
4875       ReloadGame(1);\r
4876       break;\r
4877 \r
4878     case IDM_LoadPrevGame:\r
4879       ReloadGame(-1);\r
4880       break;\r
4881 \r
4882     case IDM_ReloadGame:\r
4883       ReloadGame(0);\r
4884       break;\r
4885 \r
4886     case IDM_LoadPosition:\r
4887       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4888         Reset(FALSE, TRUE);\r
4889       }\r
4890       number = 1;\r
4891       f = OpenFileDialog(hwnd, "rb", "",\r
4892                          appData.oldSaveStyle ? "pos" : "fen",\r
4893                          POSITION_FILT,\r
4894                          _("Load Position from File"), &number, fileTitle, NULL);\r
4895       if (f != NULL) {\r
4896         LoadPosition(f, number, fileTitle);\r
4897       }\r
4898       break;\r
4899 \r
4900     case IDM_LoadNextPosition:\r
4901       ReloadPosition(1);\r
4902       break;\r
4903 \r
4904     case IDM_LoadPrevPosition:\r
4905       ReloadPosition(-1);\r
4906       break;\r
4907 \r
4908     case IDM_ReloadPosition:\r
4909       ReloadPosition(0);\r
4910       break;\r
4911 \r
4912     case IDM_SaveGame:\r
4913       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4914       f = OpenFileDialog(hwnd, "a", defName,\r
4915                          appData.oldSaveStyle ? "gam" : "pgn",\r
4916                          GAME_FILT,\r
4917                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4918       if (f != NULL) {\r
4919         SaveGame(f, 0, "");\r
4920       }\r
4921       break;\r
4922 \r
4923     case IDM_SavePosition:\r
4924       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4925       f = OpenFileDialog(hwnd, "a", defName,\r
4926                          appData.oldSaveStyle ? "pos" : "fen",\r
4927                          POSITION_FILT,\r
4928                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4929       if (f != NULL) {\r
4930         SavePosition(f, 0, "");\r
4931       }\r
4932       break;\r
4933 \r
4934     case IDM_SaveDiagram:\r
4935       defName = "diagram";\r
4936       f = OpenFileDialog(hwnd, "wb", defName,\r
4937                          "bmp",\r
4938                          DIAGRAM_FILT,\r
4939                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4940       if (f != NULL) {\r
4941         SaveDiagram(f);\r
4942       }\r
4943       break;\r
4944 \r
4945     case IDM_SaveSelected:\r
4946       f = OpenFileDialog(hwnd, "a", "",\r
4947                          "pgn",\r
4948                          GAME_FILT,\r
4949                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4950       if (f != NULL) {\r
4951         SaveSelected(f, 0, "");\r
4952       }\r
4953       break;\r
4954 \r
4955     case IDM_CreateBook:\r
4956       CreateBookEvent();\r
4957       break;\r
4958 \r
4959     case IDM_CopyGame:\r
4960       CopyGameToClipboard();\r
4961       break;\r
4962 \r
4963     case IDM_PasteGame:\r
4964       PasteGameFromClipboard();\r
4965       break;\r
4966 \r
4967     case IDM_CopyGameListToClipboard:\r
4968       CopyGameListToClipboard();\r
4969       break;\r
4970 \r
4971     /* [AS] Autodetect FEN or PGN data */\r
4972     case IDM_PasteAny:\r
4973       PasteGameOrFENFromClipboard();\r
4974       break;\r
4975 \r
4976     /* [AS] Move history */\r
4977     case IDM_ShowMoveHistory:\r
4978         if( MoveHistoryIsUp() ) {\r
4979             MoveHistoryPopDown();\r
4980         }\r
4981         else {\r
4982             MoveHistoryPopUp();\r
4983         }\r
4984         break;\r
4985 \r
4986     /* [AS] Eval graph */\r
4987     case IDM_ShowEvalGraph:\r
4988         if( EvalGraphIsUp() ) {\r
4989             EvalGraphPopDown();\r
4990         }\r
4991         else {\r
4992             EvalGraphPopUp();\r
4993             SetFocus(hwndMain);\r
4994         }\r
4995         break;\r
4996 \r
4997     /* [AS] Engine output */\r
4998     case IDM_ShowEngineOutput:\r
4999         if( EngineOutputIsUp() ) {\r
5000             EngineOutputPopDown();\r
5001         }\r
5002         else {\r
5003             EngineOutputPopUp();\r
5004         }\r
5005         break;\r
5006 \r
5007     /* [AS] User adjudication */\r
5008     case IDM_UserAdjudication_White:\r
5009         UserAdjudicationEvent( +1 );\r
5010         break;\r
5011 \r
5012     case IDM_UserAdjudication_Black:\r
5013         UserAdjudicationEvent( -1 );\r
5014         break;\r
5015 \r
5016     case IDM_UserAdjudication_Draw:\r
5017         UserAdjudicationEvent( 0 );\r
5018         break;\r
5019 \r
5020     /* [AS] Game list options dialog */\r
5021     case IDM_GameListOptions:\r
5022       GameListOptions();\r
5023       break;\r
5024 \r
5025     case IDM_NewChat:\r
5026       ChatPopUp(NULL);\r
5027       break;\r
5028 \r
5029     case IDM_CopyPosition:\r
5030       CopyFENToClipboard();\r
5031       break;\r
5032 \r
5033     case IDM_PastePosition:\r
5034       PasteFENFromClipboard();\r
5035       break;\r
5036 \r
5037     case IDM_MailMove:\r
5038       MailMoveEvent();\r
5039       break;\r
5040 \r
5041     case IDM_ReloadCMailMsg:\r
5042       Reset(TRUE, TRUE);\r
5043       ReloadCmailMsgEvent(FALSE);\r
5044       break;\r
5045 \r
5046     case IDM_Minimize:\r
5047       ShowWindow(hwnd, SW_MINIMIZE);\r
5048       break;\r
5049 \r
5050     case IDM_Exit:\r
5051       ExitEvent(0);\r
5052       break;\r
5053 \r
5054     case IDM_MachineWhite:\r
5055       MachineWhiteEvent();\r
5056       /*\r
5057        * refresh the tags dialog only if it's visible\r
5058        */\r
5059       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5060           char *tags;\r
5061           tags = PGNTags(&gameInfo);\r
5062           TagsPopUp(tags, CmailMsg());\r
5063           free(tags);\r
5064       }\r
5065       SAY("computer starts playing white");\r
5066       break;\r
5067 \r
5068     case IDM_MachineBlack:\r
5069       MachineBlackEvent();\r
5070       /*\r
5071        * refresh the tags dialog only if it's visible\r
5072        */\r
5073       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5074           char *tags;\r
5075           tags = PGNTags(&gameInfo);\r
5076           TagsPopUp(tags, CmailMsg());\r
5077           free(tags);\r
5078       }\r
5079       SAY("computer starts playing black");\r
5080       break;\r
5081 \r
5082     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5083       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5084       break;\r
5085 \r
5086     case IDM_TwoMachines:\r
5087       TwoMachinesEvent();\r
5088       /*\r
5089        * refresh the tags dialog only if it's visible\r
5090        */\r
5091       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5092           char *tags;\r
5093           tags = PGNTags(&gameInfo);\r
5094           TagsPopUp(tags, CmailMsg());\r
5095           free(tags);\r
5096       }\r
5097       SAY("computer starts playing both sides");\r
5098       break;\r
5099 \r
5100     case IDM_AnalysisMode:\r
5101       if(AnalyzeModeEvent()) {\r
5102         SAY("analyzing current position");\r
5103       }\r
5104       break;\r
5105 \r
5106     case IDM_AnalyzeFile:\r
5107       AnalyzeFileEvent();\r
5108       break;\r
5109 \r
5110     case IDM_IcsClient:\r
5111       IcsClientEvent();\r
5112       break;\r
5113 \r
5114     case IDM_EditGame:\r
5115     case IDM_EditGame2:\r
5116       EditGameEvent();\r
5117       SAY("edit game");\r
5118       break;\r
5119 \r
5120     case IDM_EditPosition:\r
5121     case IDM_EditPosition2:\r
5122       EditPositionEvent();\r
5123       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5124       break;\r
5125 \r
5126     case IDM_Training:\r
5127       TrainingEvent();\r
5128       break;\r
5129 \r
5130     case IDM_ShowGameList:\r
5131       ShowGameListProc();\r
5132       break;\r
5133 \r
5134     case IDM_EditProgs1:\r
5135       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5136       break;\r
5137 \r
5138     case IDM_LoadProg1:\r
5139      LoadEnginePopUp(hwndMain, 0);\r
5140       break;\r
5141 \r
5142     case IDM_LoadProg2:\r
5143      LoadEnginePopUp(hwndMain, 1);\r
5144       break;\r
5145 \r
5146     case IDM_EditServers:\r
5147       EditTagsPopUp(icsNames, &icsNames);\r
5148       break;\r
5149 \r
5150     case IDM_EditTags:\r
5151     case IDM_Tags:\r
5152       EditTagsProc();\r
5153       break;\r
5154 \r
5155     case IDM_EditBook:\r
5156       EditBookEvent();\r
5157       break;\r
5158 \r
5159     case IDM_EditComment:\r
5160     case IDM_Comment:\r
5161       if (commentUp && editComment) {\r
5162         CommentPopDown();\r
5163       } else {\r
5164         EditCommentEvent();\r
5165       }\r
5166       break;\r
5167 \r
5168     case IDM_Pause:\r
5169       PauseEvent();\r
5170       break;\r
5171 \r
5172     case IDM_Accept:\r
5173       AcceptEvent();\r
5174       break;\r
5175 \r
5176     case IDM_Decline:\r
5177       DeclineEvent();\r
5178       break;\r
5179 \r
5180     case IDM_Rematch:\r
5181 \r
5182       RematchEvent();\r
5183       break;\r
5184 \r
5185     case IDM_CallFlag:\r
5186       CallFlagEvent();\r
5187       break;\r
5188 \r
5189     case IDM_Draw:\r
5190       DrawEvent();\r
5191       break;\r
5192 \r
5193     case IDM_Adjourn:\r
5194       AdjournEvent();\r
5195       break;\r
5196 \r
5197     case IDM_Abort:\r
5198       AbortEvent();\r
5199       break;\r
5200 \r
5201     case IDM_Resign:\r
5202       ResignEvent();\r
5203       break;\r
5204 \r
5205     case IDM_StopObserving:\r
5206       StopObservingEvent();\r
5207       break;\r
5208 \r
5209     case IDM_StopExamining:\r
5210       StopExaminingEvent();\r
5211       break;\r
5212 \r
5213     case IDM_Upload:\r
5214       UploadGameEvent();\r
5215       break;\r
5216 \r
5217     case IDM_TypeInMove:\r
5218       TypeInEvent('\000');\r
5219       break;\r
5220 \r
5221     case IDM_TypeInName:\r
5222       PopUpNameDialog('\000');\r
5223       break;\r
5224 \r
5225     case IDM_Backward:\r
5226       BackwardEvent();\r
5227       SetFocus(hwndMain);\r
5228       break;\r
5229 \r
5230     JAWS_MENU_ITEMS\r
5231 \r
5232     case IDM_Forward:\r
5233       ForwardEvent();\r
5234       SetFocus(hwndMain);\r
5235       break;\r
5236 \r
5237     case IDM_ToStart:\r
5238       ToStartEvent();\r
5239       SetFocus(hwndMain);\r
5240       break;\r
5241 \r
5242     case IDM_ToEnd:\r
5243       ToEndEvent();\r
5244       SetFocus(hwndMain);\r
5245       break;\r
5246 \r
5247     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5248     case OPT_GameListPrev:\r
5249       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5250       break;\r
5251 \r
5252     case IDM_Revert:\r
5253       RevertEvent(FALSE);\r
5254       break;\r
5255 \r
5256     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5257       RevertEvent(TRUE);\r
5258       break;\r
5259 \r
5260     case IDM_TruncateGame:\r
5261       TruncateGameEvent();\r
5262       break;\r
5263 \r
5264     case IDM_MoveNow:\r
5265       MoveNowEvent();\r
5266       break;\r
5267 \r
5268     case IDM_RetractMove:\r
5269       RetractMoveEvent();\r
5270       break;\r
5271 \r
5272     case IDM_FlipView:\r
5273       flipView = !flipView;\r
5274       DrawPosition(FALSE, NULL);\r
5275       break;\r
5276 \r
5277     case IDM_FlipClock:\r
5278       flipClock = !flipClock;\r
5279       DisplayBothClocks();\r
5280       DisplayLogos();\r
5281       break;\r
5282 \r
5283     case IDM_MuteSounds:\r
5284       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5285       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5286                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5287       break;\r
5288 \r
5289     case IDM_GeneralOptions:\r
5290       GeneralOptionsPopup(hwnd);\r
5291       DrawPosition(TRUE, NULL);\r
5292       break;\r
5293 \r
5294     case IDM_BoardOptions:\r
5295       BoardOptionsPopup(hwnd);\r
5296       break;\r
5297 \r
5298     case IDM_ThemeOptions:\r
5299       ThemeOptionsPopup(hwnd);\r
5300       break;\r
5301 \r
5302     case IDM_EnginePlayOptions:\r
5303       EnginePlayOptionsPopup(hwnd);\r
5304       break;\r
5305 \r
5306     case IDM_Engine1Options:\r
5307       EngineOptionsPopup(hwnd, &first);\r
5308       break;\r
5309 \r
5310     case IDM_Engine2Options:\r
5311       savedHwnd = hwnd;\r
5312       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5313       EngineOptionsPopup(hwnd, &second);\r
5314       break;\r
5315 \r
5316     case IDM_OptionsUCI:\r
5317       UciOptionsPopup(hwnd);\r
5318       break;\r
5319 \r
5320     case IDM_Tourney:\r
5321       TourneyPopup(hwnd);\r
5322       break;\r
5323 \r
5324     case IDM_IcsOptions:\r
5325       IcsOptionsPopup(hwnd);\r
5326       break;\r
5327 \r
5328     case IDM_Fonts:\r
5329       FontsOptionsPopup(hwnd);\r
5330       break;\r
5331 \r
5332     case IDM_Sounds:\r
5333       SoundOptionsPopup(hwnd);\r
5334       break;\r
5335 \r
5336     case IDM_CommPort:\r
5337       CommPortOptionsPopup(hwnd);\r
5338       break;\r
5339 \r
5340     case IDM_LoadOptions:\r
5341       LoadOptionsPopup(hwnd);\r
5342       break;\r
5343 \r
5344     case IDM_SaveOptions:\r
5345       SaveOptionsPopup(hwnd);\r
5346       break;\r
5347 \r
5348     case IDM_TimeControl:\r
5349       TimeControlOptionsPopup(hwnd);\r
5350       break;\r
5351 \r
5352     case IDM_SaveSettings:\r
5353       SaveSettings(settingsFileName);\r
5354       break;\r
5355 \r
5356     case IDM_SaveSettingsOnExit:\r
5357       saveSettingsOnExit = !saveSettingsOnExit;\r
5358       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5359                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5360                                          MF_CHECKED : MF_UNCHECKED));\r
5361       break;\r
5362 \r
5363     case IDM_Hint:\r
5364       HintEvent();\r
5365       break;\r
5366 \r
5367     case IDM_Book:\r
5368       BookEvent();\r
5369       break;\r
5370 \r
5371     case IDM_AboutGame:\r
5372       AboutGameEvent();\r
5373       break;\r
5374 \r
5375     case IDM_Debug:\r
5376       appData.debugMode = !appData.debugMode;\r
5377       if (appData.debugMode) {\r
5378         char dir[MSG_SIZ];\r
5379         GetCurrentDirectory(MSG_SIZ, dir);\r
5380         SetCurrentDirectory(installDir);\r
5381         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5382         SetCurrentDirectory(dir);\r
5383         setbuf(debugFP, NULL);\r
5384       } else {\r
5385         fclose(debugFP);\r
5386         debugFP = NULL;\r
5387       }\r
5388       break;\r
5389 \r
5390     case IDM_HELPCONTENTS:\r
5391       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5392           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5393           MessageBox (GetFocus(),\r
5394                     _("Unable to activate help"),\r
5395                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5396       }\r
5397       break;\r
5398 \r
5399     case IDM_HELPSEARCH:\r
5400         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5401             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5402         MessageBox (GetFocus(),\r
5403                     _("Unable to activate help"),\r
5404                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5405       }\r
5406       break;\r
5407 \r
5408     case IDM_HELPHELP:\r
5409       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5410         MessageBox (GetFocus(),\r
5411                     _("Unable to activate help"),\r
5412                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5413       }\r
5414       break;\r
5415 \r
5416     case IDM_ABOUT:\r
5417       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5418       DialogBox(hInst, \r
5419         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5420         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5421       FreeProcInstance(lpProc);\r
5422       break;\r
5423 \r
5424     case IDM_DirectCommand1:\r
5425       AskQuestionEvent(_("Direct Command"),\r
5426                        _("Send to chess program:"), "", "1");\r
5427       break;\r
5428     case IDM_DirectCommand2:\r
5429       AskQuestionEvent(_("Direct Command"),\r
5430                        _("Send to second chess program:"), "", "2");\r
5431       break;\r
5432 \r
5433     case EP_WhitePawn:\r
5434       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5435       fromX = fromY = -1;\r
5436       break;\r
5437 \r
5438     case EP_WhiteKnight:\r
5439       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5440       fromX = fromY = -1;\r
5441       break;\r
5442 \r
5443     case EP_WhiteBishop:\r
5444       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5445       fromX = fromY = -1;\r
5446       break;\r
5447 \r
5448     case EP_WhiteRook:\r
5449       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5450       fromX = fromY = -1;\r
5451       break;\r
5452 \r
5453     case EP_WhiteQueen:\r
5454       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5455       fromX = fromY = -1;\r
5456       break;\r
5457 \r
5458     case EP_WhiteFerz:\r
5459       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5460       fromX = fromY = -1;\r
5461       break;\r
5462 \r
5463     case EP_WhiteWazir:\r
5464       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5465       fromX = fromY = -1;\r
5466       break;\r
5467 \r
5468     case EP_WhiteAlfil:\r
5469       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5470       fromX = fromY = -1;\r
5471       break;\r
5472 \r
5473     case EP_WhiteCannon:\r
5474       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5475       fromX = fromY = -1;\r
5476       break;\r
5477 \r
5478     case EP_WhiteCardinal:\r
5479       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5480       fromX = fromY = -1;\r
5481       break;\r
5482 \r
5483     case EP_WhiteMarshall:\r
5484       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5485       fromX = fromY = -1;\r
5486       break;\r
5487 \r
5488     case EP_WhiteKing:\r
5489       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5490       fromX = fromY = -1;\r
5491       break;\r
5492 \r
5493     case EP_BlackPawn:\r
5494       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5495       fromX = fromY = -1;\r
5496       break;\r
5497 \r
5498     case EP_BlackKnight:\r
5499       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5500       fromX = fromY = -1;\r
5501       break;\r
5502 \r
5503     case EP_BlackBishop:\r
5504       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5505       fromX = fromY = -1;\r
5506       break;\r
5507 \r
5508     case EP_BlackRook:\r
5509       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5510       fromX = fromY = -1;\r
5511       break;\r
5512 \r
5513     case EP_BlackQueen:\r
5514       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5515       fromX = fromY = -1;\r
5516       break;\r
5517 \r
5518     case EP_BlackFerz:\r
5519       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5520       fromX = fromY = -1;\r
5521       break;\r
5522 \r
5523     case EP_BlackWazir:\r
5524       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5525       fromX = fromY = -1;\r
5526       break;\r
5527 \r
5528     case EP_BlackAlfil:\r
5529       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5530       fromX = fromY = -1;\r
5531       break;\r
5532 \r
5533     case EP_BlackCannon:\r
5534       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5535       fromX = fromY = -1;\r
5536       break;\r
5537 \r
5538     case EP_BlackCardinal:\r
5539       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5540       fromX = fromY = -1;\r
5541       break;\r
5542 \r
5543     case EP_BlackMarshall:\r
5544       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5545       fromX = fromY = -1;\r
5546       break;\r
5547 \r
5548     case EP_BlackKing:\r
5549       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5550       fromX = fromY = -1;\r
5551       break;\r
5552 \r
5553     case EP_EmptySquare:\r
5554       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5555       fromX = fromY = -1;\r
5556       break;\r
5557 \r
5558     case EP_ClearBoard:\r
5559       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5560       fromX = fromY = -1;\r
5561       break;\r
5562 \r
5563     case EP_White:\r
5564       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5565       fromX = fromY = -1;\r
5566       break;\r
5567 \r
5568     case EP_Black:\r
5569       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5570       fromX = fromY = -1;\r
5571       break;\r
5572 \r
5573     case EP_Promote:\r
5574       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5575       fromX = fromY = -1;\r
5576       break;\r
5577 \r
5578     case EP_Demote:\r
5579       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5580       fromX = fromY = -1;\r
5581       break;\r
5582 \r
5583     case DP_Pawn:\r
5584       DropMenuEvent(WhitePawn, fromX, fromY);\r
5585       fromX = fromY = -1;\r
5586       break;\r
5587 \r
5588     case DP_Knight:\r
5589       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5590       fromX = fromY = -1;\r
5591       break;\r
5592 \r
5593     case DP_Bishop:\r
5594       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5595       fromX = fromY = -1;\r
5596       break;\r
5597 \r
5598     case DP_Rook:\r
5599       DropMenuEvent(WhiteRook, fromX, fromY);\r
5600       fromX = fromY = -1;\r
5601       break;\r
5602 \r
5603     case DP_Queen:\r
5604       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5605       fromX = fromY = -1;\r
5606       break;\r
5607 \r
5608     case IDM_English:\r
5609       barbaric = 0; appData.language = "";\r
5610       TranslateMenus(0);\r
5611       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5612       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5613       lastChecked = wmId;\r
5614       break;\r
5615 \r
5616     default:\r
5617       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5618           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5619       else\r
5620       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5621           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5622           TranslateMenus(0);\r
5623           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5624           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5625           lastChecked = wmId;\r
5626           break;\r
5627       }\r
5628       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5629     }\r
5630     break;\r
5631 \r
5632   case WM_TIMER:\r
5633     switch (wParam) {\r
5634     case CLOCK_TIMER_ID:\r
5635       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5636       clockTimerEvent = 0;\r
5637       DecrementClocks(); /* call into back end */\r
5638       break;\r
5639     case LOAD_GAME_TIMER_ID:\r
5640       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5641       loadGameTimerEvent = 0;\r
5642       AutoPlayGameLoop(); /* call into back end */\r
5643       break;\r
5644     case ANALYSIS_TIMER_ID:\r
5645       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5646                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5647         AnalysisPeriodicEvent(0);\r
5648       } else {\r
5649         KillTimer(hwnd, analysisTimerEvent);\r
5650         analysisTimerEvent = 0;\r
5651       }\r
5652       break;\r
5653     case DELAYED_TIMER_ID:\r
5654       KillTimer(hwnd, delayedTimerEvent);\r
5655       delayedTimerEvent = 0;\r
5656       delayedTimerCallback();\r
5657       break;\r
5658     }\r
5659     break;\r
5660 \r
5661   case WM_USER_Input:\r
5662     InputEvent(hwnd, message, wParam, lParam);\r
5663     break;\r
5664 \r
5665   /* [AS] Also move "attached" child windows */\r
5666   case WM_WINDOWPOSCHANGING:\r
5667 \r
5668     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5669         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5670 \r
5671         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5672             /* Window is moving */\r
5673             RECT rcMain;\r
5674 \r
5675 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5676             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5677             rcMain.right  = wpMain.x + wpMain.width;\r
5678             rcMain.top    = wpMain.y;\r
5679             rcMain.bottom = wpMain.y + wpMain.height;\r
5680             \r
5681             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5682             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5683             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5684             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5685             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5686             wpMain.x = lpwp->x;\r
5687             wpMain.y = lpwp->y;\r
5688         }\r
5689     }\r
5690     break;\r
5691 \r
5692   /* [AS] Snapping */\r
5693   case WM_ENTERSIZEMOVE:\r
5694     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5695     if (hwnd == hwndMain) {\r
5696       doingSizing = TRUE;\r
5697       lastSizing = 0;\r
5698     }\r
5699     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5700     break;\r
5701 \r
5702   case WM_SIZING:\r
5703     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5704     if (hwnd == hwndMain) {\r
5705       lastSizing = wParam;\r
5706     }\r
5707     break;\r
5708 \r
5709   case WM_MOVING:\r
5710     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5711       return OnMoving( &sd, hwnd, wParam, lParam );\r
5712 \r
5713   case WM_EXITSIZEMOVE:\r
5714     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5715     if (hwnd == hwndMain) {\r
5716       RECT client;\r
5717       doingSizing = FALSE;\r
5718       InvalidateRect(hwnd, &boardRect, FALSE);\r
5719       GetClientRect(hwnd, &client);\r
5720       ResizeBoard(client.right, client.bottom, lastSizing);\r
5721       lastSizing = 0;\r
5722       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5723     }\r
5724     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5725     break;\r
5726 \r
5727   case WM_DESTROY: /* message: window being destroyed */\r
5728     PostQuitMessage(0);\r
5729     break;\r
5730 \r
5731   case WM_CLOSE:\r
5732     if (hwnd == hwndMain) {\r
5733       ExitEvent(0);\r
5734     }\r
5735     break;\r
5736 \r
5737   default:      /* Passes it on if unprocessed */\r
5738     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5739   }\r
5740   return 0;\r
5741 }\r
5742 \r
5743 /*---------------------------------------------------------------------------*\\r
5744  *\r
5745  * Misc utility routines\r
5746  *\r
5747 \*---------------------------------------------------------------------------*/\r
5748 \r
5749 /*\r
5750  * Decent random number generator, at least not as bad as Windows\r
5751  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5752  */\r
5753 unsigned int randstate;\r
5754 \r
5755 int\r
5756 myrandom(void)\r
5757 {\r
5758   randstate = randstate * 1664525 + 1013904223;\r
5759   return (int) randstate & 0x7fffffff;\r
5760 }\r
5761 \r
5762 void\r
5763 mysrandom(unsigned int seed)\r
5764 {\r
5765   randstate = seed;\r
5766 }\r
5767 \r
5768 \r
5769 /* \r
5770  * returns TRUE if user selects a different color, FALSE otherwise \r
5771  */\r
5772 \r
5773 BOOL\r
5774 ChangeColor(HWND hwnd, COLORREF *which)\r
5775 {\r
5776   static BOOL firstTime = TRUE;\r
5777   static DWORD customColors[16];\r
5778   CHOOSECOLOR cc;\r
5779   COLORREF newcolor;\r
5780   int i;\r
5781   ColorClass ccl;\r
5782 \r
5783   if (firstTime) {\r
5784     /* Make initial colors in use available as custom colors */\r
5785     /* Should we put the compiled-in defaults here instead? */\r
5786     i = 0;\r
5787     customColors[i++] = lightSquareColor & 0xffffff;\r
5788     customColors[i++] = darkSquareColor & 0xffffff;\r
5789     customColors[i++] = whitePieceColor & 0xffffff;\r
5790     customColors[i++] = blackPieceColor & 0xffffff;\r
5791     customColors[i++] = highlightSquareColor & 0xffffff;\r
5792     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5793 \r
5794     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5795       customColors[i++] = textAttribs[ccl].color;\r
5796     }\r
5797     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5798     firstTime = FALSE;\r
5799   }\r
5800 \r
5801   cc.lStructSize = sizeof(cc);\r
5802   cc.hwndOwner = hwnd;\r
5803   cc.hInstance = NULL;\r
5804   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5805   cc.lpCustColors = (LPDWORD) customColors;\r
5806   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5807 \r
5808   if (!ChooseColor(&cc)) return FALSE;\r
5809 \r
5810   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5811   if (newcolor == *which) return FALSE;\r
5812   *which = newcolor;\r
5813   return TRUE;\r
5814 \r
5815   /*\r
5816   InitDrawingColors();\r
5817   InvalidateRect(hwnd, &boardRect, FALSE);\r
5818   */\r
5819 }\r
5820 \r
5821 BOOLEAN\r
5822 MyLoadSound(MySound *ms)\r
5823 {\r
5824   BOOL ok = FALSE;\r
5825   struct stat st;\r
5826   FILE *f;\r
5827 \r
5828   if (ms->data && ms->flag) free(ms->data);\r
5829   ms->data = NULL;\r
5830 \r
5831   switch (ms->name[0]) {\r
5832   case NULLCHAR:\r
5833     /* Silence */\r
5834     ok = TRUE;\r
5835     break;\r
5836   case '$':\r
5837     /* System sound from Control Panel.  Don't preload here. */\r
5838     ok = TRUE;\r
5839     break;\r
5840   case '!':\r
5841     if (ms->name[1] == NULLCHAR) {\r
5842       /* "!" alone = silence */\r
5843       ok = TRUE;\r
5844     } else {\r
5845       /* Builtin wave resource.  Error if not found. */\r
5846       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5847       if (h == NULL) break;\r
5848       ms->data = (void *)LoadResource(hInst, h);\r
5849       ms->flag = 0; // not maloced, so cannot be freed!\r
5850       if (h == NULL) break;\r
5851       ok = TRUE;\r
5852     }\r
5853     break;\r
5854   default:\r
5855     /* .wav file.  Error if not found. */\r
5856     f = fopen(ms->name, "rb");\r
5857     if (f == NULL) break;\r
5858     if (fstat(fileno(f), &st) < 0) break;\r
5859     ms->data = malloc(st.st_size);\r
5860     ms->flag = 1;\r
5861     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5862     fclose(f);\r
5863     ok = TRUE;\r
5864     break;\r
5865   }\r
5866   if (!ok) {\r
5867     char buf[MSG_SIZ];\r
5868       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5869     DisplayError(buf, GetLastError());\r
5870   }\r
5871   return ok;\r
5872 }\r
5873 \r
5874 BOOLEAN\r
5875 MyPlaySound(MySound *ms)\r
5876 {\r
5877   BOOLEAN ok = FALSE;\r
5878 \r
5879   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5880   switch (ms->name[0]) {\r
5881   case NULLCHAR:\r
5882         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5883     /* Silence */\r
5884     ok = TRUE;\r
5885     break;\r
5886   case '$':\r
5887     /* System sound from Control Panel (deprecated feature).\r
5888        "$" alone or an unset sound name gets default beep (still in use). */\r
5889     if (ms->name[1]) {\r
5890       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5891     }\r
5892     if (!ok) ok = MessageBeep(MB_OK);\r
5893     break; \r
5894   case '!':\r
5895     /* Builtin wave resource, or "!" alone for silence */\r
5896     if (ms->name[1]) {\r
5897       if (ms->data == NULL) return FALSE;\r
5898       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5899     } else {\r
5900       ok = TRUE;\r
5901     }\r
5902     break;\r
5903   default:\r
5904     /* .wav file.  Error if not found. */\r
5905     if (ms->data == NULL) return FALSE;\r
5906     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5907     break;\r
5908   }\r
5909   /* Don't print an error: this can happen innocently if the sound driver\r
5910      is busy; for instance, if another instance of WinBoard is playing\r
5911      a sound at about the same time. */\r
5912   return ok;\r
5913 }\r
5914 \r
5915 \r
5916 LRESULT CALLBACK\r
5917 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5918 {\r
5919   BOOL ok;\r
5920   OPENFILENAME *ofn;\r
5921   static UINT *number; /* gross that this is static */\r
5922 \r
5923   switch (message) {\r
5924   case WM_INITDIALOG: /* message: initialize dialog box */\r
5925     /* Center the dialog over the application window */\r
5926     ofn = (OPENFILENAME *) lParam;\r
5927     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5928       number = (UINT *) ofn->lCustData;\r
5929       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5930     } else {\r
5931       number = NULL;\r
5932     }\r
5933     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5934     Translate(hDlg, 1536);\r
5935     return FALSE;  /* Allow for further processing */\r
5936 \r
5937   case WM_COMMAND:\r
5938     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5939       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5940     }\r
5941     return FALSE;  /* Allow for further processing */\r
5942   }\r
5943   return FALSE;\r
5944 }\r
5945 \r
5946 UINT APIENTRY\r
5947 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5948 {\r
5949   static UINT *number;\r
5950   OPENFILENAME *ofname;\r
5951   OFNOTIFY *ofnot;\r
5952   switch (uiMsg) {\r
5953   case WM_INITDIALOG:\r
5954     Translate(hdlg, DLG_IndexNumber);\r
5955     ofname = (OPENFILENAME *)lParam;\r
5956     number = (UINT *)(ofname->lCustData);\r
5957     break;\r
5958   case WM_NOTIFY:\r
5959     ofnot = (OFNOTIFY *)lParam;\r
5960     if (ofnot->hdr.code == CDN_FILEOK) {\r
5961       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5962     }\r
5963     break;\r
5964   }\r
5965   return 0;\r
5966 }\r
5967 \r
5968 \r
5969 FILE *\r
5970 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5971                char *nameFilt, char *dlgTitle, UINT *number,\r
5972                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5973 {\r
5974   OPENFILENAME openFileName;\r
5975   char buf1[MSG_SIZ];\r
5976   FILE *f;\r
5977 \r
5978   if (fileName == NULL) fileName = buf1;\r
5979   if (defName == NULL) {\r
5980     safeStrCpy(fileName, "*.", 3 );\r
5981     strcat(fileName, defExt);\r
5982   } else {\r
5983     safeStrCpy(fileName, defName, MSG_SIZ );\r
5984   }\r
5985     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5986   if (number) *number = 0;\r
5987 \r
5988   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5989   openFileName.hwndOwner         = hwnd;\r
5990   openFileName.hInstance         = (HANDLE) hInst;\r
5991   openFileName.lpstrFilter       = nameFilt;\r
5992   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5993   openFileName.nMaxCustFilter    = 0L;\r
5994   openFileName.nFilterIndex      = 1L;\r
5995   openFileName.lpstrFile         = fileName;\r
5996   openFileName.nMaxFile          = MSG_SIZ;\r
5997   openFileName.lpstrFileTitle    = fileTitle;\r
5998   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5999   openFileName.lpstrInitialDir   = NULL;\r
6000   openFileName.lpstrTitle        = dlgTitle;\r
6001   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6002     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6003     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6004     | (oldDialog ? 0 : OFN_EXPLORER);\r
6005   openFileName.nFileOffset       = 0;\r
6006   openFileName.nFileExtension    = 0;\r
6007   openFileName.lpstrDefExt       = defExt;\r
6008   openFileName.lCustData         = (LONG) number;\r
6009   openFileName.lpfnHook          = oldDialog ?\r
6010     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6011   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6012 \r
6013   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6014                         GetOpenFileName(&openFileName)) {\r
6015     /* open the file */\r
6016     f = fopen(openFileName.lpstrFile, write);\r
6017     if (f == NULL) {\r
6018       MessageBox(hwnd, _("File open failed"), NULL,\r
6019                  MB_OK|MB_ICONEXCLAMATION);\r
6020       return NULL;\r
6021     }\r
6022   } else {\r
6023     int err = CommDlgExtendedError();\r
6024     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6025     return FALSE;\r
6026   }\r
6027   return f;\r
6028 }\r
6029 \r
6030 \r
6031 \r
6032 VOID APIENTRY\r
6033 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6034 {\r
6035   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6036 \r
6037   /*\r
6038    * Get the first pop-up menu in the menu template. This is the\r
6039    * menu that TrackPopupMenu displays.\r
6040    */\r
6041   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6042   TranslateOneMenu(10, hmenuTrackPopup);\r
6043 \r
6044   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6045 \r
6046   /*\r
6047    * TrackPopup uses screen coordinates, so convert the\r
6048    * coordinates of the mouse click to screen coordinates.\r
6049    */\r
6050   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6051 \r
6052   /* Draw and track the floating pop-up menu. */\r
6053   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6054                  pt.x, pt.y, 0, hwnd, NULL);\r
6055 \r
6056   /* Destroy the menu.*/\r
6057   DestroyMenu(hmenu);\r
6058 }\r
6059    \r
6060 typedef struct {\r
6061   HWND hDlg, hText;\r
6062   int sizeX, sizeY, newSizeX, newSizeY;\r
6063   HDWP hdwp;\r
6064 } ResizeEditPlusButtonsClosure;\r
6065 \r
6066 BOOL CALLBACK\r
6067 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6068 {\r
6069   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6070   RECT rect;\r
6071   POINT pt;\r
6072 \r
6073   if (hChild == cl->hText) return TRUE;\r
6074   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6075   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6076   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6077   ScreenToClient(cl->hDlg, &pt);\r
6078   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6079     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6080   return TRUE;\r
6081 }\r
6082 \r
6083 /* Resize a dialog that has a (rich) edit field filling most of\r
6084    the top, with a row of buttons below */\r
6085 VOID\r
6086 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6087 {\r
6088   RECT rectText;\r
6089   int newTextHeight, newTextWidth;\r
6090   ResizeEditPlusButtonsClosure cl;\r
6091   \r
6092   /*if (IsIconic(hDlg)) return;*/\r
6093   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6094   \r
6095   cl.hdwp = BeginDeferWindowPos(8);\r
6096 \r
6097   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6098   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6099   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6100   if (newTextHeight < 0) {\r
6101     newSizeY += -newTextHeight;\r
6102     newTextHeight = 0;\r
6103   }\r
6104   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6105     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6106 \r
6107   cl.hDlg = hDlg;\r
6108   cl.hText = hText;\r
6109   cl.sizeX = sizeX;\r
6110   cl.sizeY = sizeY;\r
6111   cl.newSizeX = newSizeX;\r
6112   cl.newSizeY = newSizeY;\r
6113   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6114 \r
6115   EndDeferWindowPos(cl.hdwp);\r
6116 }\r
6117 \r
6118 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6119 {\r
6120     RECT    rChild, rParent;\r
6121     int     wChild, hChild, wParent, hParent;\r
6122     int     wScreen, hScreen, xNew, yNew;\r
6123     HDC     hdc;\r
6124 \r
6125     /* Get the Height and Width of the child window */\r
6126     GetWindowRect (hwndChild, &rChild);\r
6127     wChild = rChild.right - rChild.left;\r
6128     hChild = rChild.bottom - rChild.top;\r
6129 \r
6130     /* Get the Height and Width of the parent window */\r
6131     GetWindowRect (hwndParent, &rParent);\r
6132     wParent = rParent.right - rParent.left;\r
6133     hParent = rParent.bottom - rParent.top;\r
6134 \r
6135     /* Get the display limits */\r
6136     hdc = GetDC (hwndChild);\r
6137     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6138     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6139     ReleaseDC(hwndChild, hdc);\r
6140 \r
6141     /* Calculate new X position, then adjust for screen */\r
6142     xNew = rParent.left + ((wParent - wChild) /2);\r
6143     if (xNew < 0) {\r
6144         xNew = 0;\r
6145     } else if ((xNew+wChild) > wScreen) {\r
6146         xNew = wScreen - wChild;\r
6147     }\r
6148 \r
6149     /* Calculate new Y position, then adjust for screen */\r
6150     if( mode == 0 ) {\r
6151         yNew = rParent.top  + ((hParent - hChild) /2);\r
6152     }\r
6153     else {\r
6154         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6155     }\r
6156 \r
6157     if (yNew < 0) {\r
6158         yNew = 0;\r
6159     } else if ((yNew+hChild) > hScreen) {\r
6160         yNew = hScreen - hChild;\r
6161     }\r
6162 \r
6163     /* Set it, and return */\r
6164     return SetWindowPos (hwndChild, NULL,\r
6165                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6166 }\r
6167 \r
6168 /* Center one window over another */\r
6169 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6170 {\r
6171     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6172 }\r
6173 \r
6174 /*---------------------------------------------------------------------------*\\r
6175  *\r
6176  * Startup Dialog functions\r
6177  *\r
6178 \*---------------------------------------------------------------------------*/\r
6179 void\r
6180 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6181 {\r
6182   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6183 \r
6184   while (*cd != NULL) {\r
6185     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6186     cd++;\r
6187   }\r
6188 }\r
6189 \r
6190 void\r
6191 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6192 {\r
6193   char buf1[MAX_ARG_LEN];\r
6194   int len;\r
6195 \r
6196   if (str[0] == '@') {\r
6197     FILE* f = fopen(str + 1, "r");\r
6198     if (f == NULL) {\r
6199       DisplayFatalError(str + 1, errno, 2);\r
6200       return;\r
6201     }\r
6202     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6203     fclose(f);\r
6204     buf1[len] = NULLCHAR;\r
6205     str = buf1;\r
6206   }\r
6207 \r
6208   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6209 \r
6210   for (;;) {\r
6211     char buf[MSG_SIZ];\r
6212     char *end = strchr(str, '\n');\r
6213     if (end == NULL) return;\r
6214     memcpy(buf, str, end - str);\r
6215     buf[end - str] = NULLCHAR;\r
6216     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6217     str = end + 1;\r
6218   }\r
6219 }\r
6220 \r
6221 void\r
6222 SetStartupDialogEnables(HWND hDlg)\r
6223 {\r
6224   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6225     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6226     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6227   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6228     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6229   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6230     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6231   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6232     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6233   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6234     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6235     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6236     IsDlgButtonChecked(hDlg, OPT_View));\r
6237 }\r
6238 \r
6239 char *\r
6240 QuoteForFilename(char *filename)\r
6241 {\r
6242   int dquote, space;\r
6243   dquote = strchr(filename, '"') != NULL;\r
6244   space = strchr(filename, ' ') != NULL;\r
6245   if (dquote || space) {\r
6246     if (dquote) {\r
6247       return "'";\r
6248     } else {\r
6249       return "\"";\r
6250     }\r
6251   } else {\r
6252     return "";\r
6253   }\r
6254 }\r
6255 \r
6256 VOID\r
6257 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6258 {\r
6259   char buf[MSG_SIZ];\r
6260   char *q;\r
6261 \r
6262   InitComboStringsFromOption(hwndCombo, nthnames);\r
6263   q = QuoteForFilename(nthcp);\r
6264     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6265   if (*nthdir != NULLCHAR) {\r
6266     q = QuoteForFilename(nthdir);\r
6267       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6268   }\r
6269   if (*nthcp == NULLCHAR) {\r
6270     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6271   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6272     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6273     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6274   }\r
6275 }\r
6276 \r
6277 LRESULT CALLBACK\r
6278 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6279 {\r
6280   char buf[MSG_SIZ];\r
6281   HANDLE hwndCombo;\r
6282   char *p;\r
6283 \r
6284   switch (message) {\r
6285   case WM_INITDIALOG:\r
6286     /* Center the dialog */\r
6287     CenterWindow (hDlg, GetDesktopWindow());\r
6288     Translate(hDlg, DLG_Startup);\r
6289     /* Initialize the dialog items */\r
6290     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6291                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6292                   firstChessProgramNames);\r
6293     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6294                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6295                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6296     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6297     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6298       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6299     if (*appData.icsHelper != NULLCHAR) {\r
6300       char *q = QuoteForFilename(appData.icsHelper);\r
6301       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6302     }\r
6303     if (*appData.icsHost == NULLCHAR) {\r
6304       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6305       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6306     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6307       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6308       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6309     }\r
6310 \r
6311     if (appData.icsActive) {\r
6312       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6313     }\r
6314     else if (appData.noChessProgram) {\r
6315       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6316     }\r
6317     else {\r
6318       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6319     }\r
6320 \r
6321     SetStartupDialogEnables(hDlg);\r
6322     return TRUE;\r
6323 \r
6324   case WM_COMMAND:\r
6325     switch (LOWORD(wParam)) {\r
6326     case IDOK:\r
6327       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6328         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6329         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6330         p = buf;\r
6331         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6332         ParseArgs(StringGet, &p);\r
6333         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6334         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6335         p = buf;\r
6336         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6337         ParseArgs(StringGet, &p);\r
6338         SwapEngines(singleList); // ... and then make it 'second'\r
6339 \r
6340         appData.noChessProgram = FALSE;\r
6341         appData.icsActive = FALSE;\r
6342       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6343         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6344         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6345         p = buf;\r
6346         ParseArgs(StringGet, &p);\r
6347         if (appData.zippyPlay) {\r
6348           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6349           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6350           p = buf;\r
6351           ParseArgs(StringGet, &p);\r
6352         }\r
6353       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6354         appData.noChessProgram = TRUE;\r
6355         appData.icsActive = FALSE;\r
6356       } else {\r
6357         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6358                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6359         return TRUE;\r
6360       }\r
6361       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6362         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6363         p = buf;\r
6364         ParseArgs(StringGet, &p);\r
6365       }\r
6366       EndDialog(hDlg, TRUE);\r
6367       return TRUE;\r
6368 \r
6369     case IDCANCEL:\r
6370       ExitEvent(0);\r
6371       return TRUE;\r
6372 \r
6373     case IDM_HELPCONTENTS:\r
6374       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6375         MessageBox (GetFocus(),\r
6376                     _("Unable to activate help"),\r
6377                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6378       }\r
6379       break;\r
6380 \r
6381     default:\r
6382       SetStartupDialogEnables(hDlg);\r
6383       break;\r
6384     }\r
6385     break;\r
6386   }\r
6387   return FALSE;\r
6388 }\r
6389 \r
6390 /*---------------------------------------------------------------------------*\\r
6391  *\r
6392  * About box dialog functions\r
6393  *\r
6394 \*---------------------------------------------------------------------------*/\r
6395 \r
6396 /* Process messages for "About" dialog box */\r
6397 LRESULT CALLBACK\r
6398 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6399 {\r
6400   switch (message) {\r
6401   case WM_INITDIALOG: /* message: initialize dialog box */\r
6402     /* Center the dialog over the application window */\r
6403     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6404     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6405     Translate(hDlg, ABOUTBOX);\r
6406     JAWS_COPYRIGHT\r
6407     return (TRUE);\r
6408 \r
6409   case WM_COMMAND: /* message: received a command */\r
6410     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6411         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6412       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6413       return (TRUE);\r
6414     }\r
6415     break;\r
6416   }\r
6417   return (FALSE);\r
6418 }\r
6419 \r
6420 /*---------------------------------------------------------------------------*\\r
6421  *\r
6422  * Comment Dialog functions\r
6423  *\r
6424 \*---------------------------------------------------------------------------*/\r
6425 \r
6426 LRESULT CALLBACK\r
6427 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6428 {\r
6429   static HANDLE hwndText = NULL;\r
6430   int len, newSizeX, newSizeY;\r
6431   static int sizeX, sizeY;\r
6432   char *str;\r
6433   RECT rect;\r
6434   MINMAXINFO *mmi;\r
6435 \r
6436   switch (message) {\r
6437   case WM_INITDIALOG: /* message: initialize dialog box */\r
6438     /* Initialize the dialog items */\r
6439     Translate(hDlg, DLG_EditComment);\r
6440     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6441     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6442     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6443     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6444     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6445     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6446     SetWindowText(hDlg, commentTitle);\r
6447     if (editComment) {\r
6448       SetFocus(hwndText);\r
6449     } else {\r
6450       SetFocus(GetDlgItem(hDlg, IDOK));\r
6451     }\r
6452     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6453                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6454                 MAKELPARAM(FALSE, 0));\r
6455     /* Size and position the dialog */\r
6456     if (!commentDialog) {\r
6457       commentDialog = hDlg;\r
6458       GetClientRect(hDlg, &rect);\r
6459       sizeX = rect.right;\r
6460       sizeY = rect.bottom;\r
6461       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6462           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6463         WINDOWPLACEMENT wp;\r
6464         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6465         wp.length = sizeof(WINDOWPLACEMENT);\r
6466         wp.flags = 0;\r
6467         wp.showCmd = SW_SHOW;\r
6468         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6469         wp.rcNormalPosition.left = wpComment.x;\r
6470         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6471         wp.rcNormalPosition.top = wpComment.y;\r
6472         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6473         SetWindowPlacement(hDlg, &wp);\r
6474 \r
6475         GetClientRect(hDlg, &rect);\r
6476         newSizeX = rect.right;\r
6477         newSizeY = rect.bottom;\r
6478         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6479                               newSizeX, newSizeY);\r
6480         sizeX = newSizeX;\r
6481         sizeY = newSizeY;\r
6482       }\r
6483     }\r
6484     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6485     return FALSE;\r
6486 \r
6487   case WM_COMMAND: /* message: received a command */\r
6488     switch (LOWORD(wParam)) {\r
6489     case IDOK:\r
6490       if (editComment) {\r
6491         char *p, *q;\r
6492         /* Read changed options from the dialog box */\r
6493         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6494         len = GetWindowTextLength(hwndText);\r
6495         str = (char *) malloc(len + 1);\r
6496         GetWindowText(hwndText, str, len + 1);\r
6497         p = q = str;\r
6498         while (*q) {\r
6499           if (*q == '\r')\r
6500             q++;\r
6501           else\r
6502             *p++ = *q++;\r
6503         }\r
6504         *p = NULLCHAR;\r
6505         ReplaceComment(commentIndex, str);\r
6506         free(str);\r
6507       }\r
6508       CommentPopDown();\r
6509       return TRUE;\r
6510 \r
6511     case IDCANCEL:\r
6512     case OPT_CancelComment:\r
6513       CommentPopDown();\r
6514       return TRUE;\r
6515 \r
6516     case OPT_ClearComment:\r
6517       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6518       break;\r
6519 \r
6520     case OPT_EditComment:\r
6521       EditCommentEvent();\r
6522       return TRUE;\r
6523 \r
6524     default:\r
6525       break;\r
6526     }\r
6527     break;\r
6528 \r
6529   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6530         if( wParam == OPT_CommentText ) {\r
6531             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6532 \r
6533             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6534                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6535                 POINTL pt;\r
6536                 LRESULT index;\r
6537 \r
6538                 pt.x = LOWORD( lpMF->lParam );\r
6539                 pt.y = HIWORD( lpMF->lParam );\r
6540 \r
6541                 if(lpMF->msg == WM_CHAR) {\r
6542                         CHARRANGE sel;\r
6543                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6544                         index = sel.cpMin;\r
6545                 } else\r
6546                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6547 \r
6548                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6549                 len = GetWindowTextLength(hwndText);\r
6550                 str = (char *) malloc(len + 1);\r
6551                 GetWindowText(hwndText, str, len + 1);\r
6552                 ReplaceComment(commentIndex, str);\r
6553                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6554                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6555                 free(str);\r
6556 \r
6557                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6558                 lpMF->msg = WM_USER;\r
6559 \r
6560                 return TRUE;\r
6561             }\r
6562         }\r
6563         break;\r
6564 \r
6565   case WM_SIZE:\r
6566     newSizeX = LOWORD(lParam);\r
6567     newSizeY = HIWORD(lParam);\r
6568     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6569     sizeX = newSizeX;\r
6570     sizeY = newSizeY;\r
6571     break;\r
6572 \r
6573   case WM_GETMINMAXINFO:\r
6574     /* Prevent resizing window too small */\r
6575     mmi = (MINMAXINFO *) lParam;\r
6576     mmi->ptMinTrackSize.x = 100;\r
6577     mmi->ptMinTrackSize.y = 100;\r
6578     break;\r
6579   }\r
6580   return FALSE;\r
6581 }\r
6582 \r
6583 VOID\r
6584 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6585 {\r
6586   FARPROC lpProc;\r
6587   char *p, *q;\r
6588 \r
6589   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6590 \r
6591   if (str == NULL) str = "";\r
6592   p = (char *) malloc(2 * strlen(str) + 2);\r
6593   q = p;\r
6594   while (*str) {\r
6595     if (*str == '\n') *q++ = '\r';\r
6596     *q++ = *str++;\r
6597   }\r
6598   *q = NULLCHAR;\r
6599   if (commentText != NULL) free(commentText);\r
6600 \r
6601   commentIndex = index;\r
6602   commentTitle = title;\r
6603   commentText = p;\r
6604   editComment = edit;\r
6605 \r
6606   if (commentDialog) {\r
6607     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6608     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6609   } else {\r
6610     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6611     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6612                  hwndMain, (DLGPROC)lpProc);\r
6613     FreeProcInstance(lpProc);\r
6614   }\r
6615   commentUp = TRUE;\r
6616 }\r
6617 \r
6618 \r
6619 /*---------------------------------------------------------------------------*\\r
6620  *\r
6621  * Type-in move dialog functions\r
6622  * \r
6623 \*---------------------------------------------------------------------------*/\r
6624 \r
6625 LRESULT CALLBACK\r
6626 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6627 {\r
6628   char move[MSG_SIZ];\r
6629   HWND hInput;\r
6630 \r
6631   switch (message) {\r
6632   case WM_INITDIALOG:\r
6633     move[0] = (char) lParam;\r
6634     move[1] = NULLCHAR;\r
6635     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6636     Translate(hDlg, DLG_TypeInMove);\r
6637     hInput = GetDlgItem(hDlg, OPT_Move);\r
6638     SetWindowText(hInput, move);\r
6639     SetFocus(hInput);\r
6640     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6641     return FALSE;\r
6642 \r
6643   case WM_COMMAND:\r
6644     switch (LOWORD(wParam)) {\r
6645     case IDOK:\r
6646 \r
6647       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6648       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6649       TypeInDoneEvent(move);\r
6650       EndDialog(hDlg, TRUE);\r
6651       return TRUE;\r
6652     case IDCANCEL:\r
6653       EndDialog(hDlg, FALSE);\r
6654       return TRUE;\r
6655     default:\r
6656       break;\r
6657     }\r
6658     break;\r
6659   }\r
6660   return FALSE;\r
6661 }\r
6662 \r
6663 VOID\r
6664 PopUpMoveDialog(char firstchar)\r
6665 {\r
6666     FARPROC lpProc;\r
6667 \r
6668       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6669       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6670         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6671       FreeProcInstance(lpProc);\r
6672 }\r
6673 \r
6674 /*---------------------------------------------------------------------------*\\r
6675  *\r
6676  * Type-in name dialog functions\r
6677  * \r
6678 \*---------------------------------------------------------------------------*/\r
6679 \r
6680 LRESULT CALLBACK\r
6681 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6682 {\r
6683   char move[MSG_SIZ];\r
6684   HWND hInput;\r
6685 \r
6686   switch (message) {\r
6687   case WM_INITDIALOG:\r
6688     move[0] = (char) lParam;\r
6689     move[1] = NULLCHAR;\r
6690     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6691     Translate(hDlg, DLG_TypeInName);\r
6692     hInput = GetDlgItem(hDlg, OPT_Name);\r
6693     SetWindowText(hInput, move);\r
6694     SetFocus(hInput);\r
6695     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6696     return FALSE;\r
6697 \r
6698   case WM_COMMAND:\r
6699     switch (LOWORD(wParam)) {\r
6700     case IDOK:\r
6701       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6702       appData.userName = strdup(move);\r
6703       SetUserLogo();\r
6704       SetGameInfo();\r
6705       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6706         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6707         DisplayTitle(move);\r
6708       }\r
6709 \r
6710 \r
6711       EndDialog(hDlg, TRUE);\r
6712       return TRUE;\r
6713     case IDCANCEL:\r
6714       EndDialog(hDlg, FALSE);\r
6715       return TRUE;\r
6716     default:\r
6717       break;\r
6718     }\r
6719     break;\r
6720   }\r
6721   return FALSE;\r
6722 }\r
6723 \r
6724 VOID\r
6725 PopUpNameDialog(char firstchar)\r
6726 {\r
6727     FARPROC lpProc;\r
6728     \r
6729       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6730       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6731         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6732       FreeProcInstance(lpProc);\r
6733 }\r
6734 \r
6735 /*---------------------------------------------------------------------------*\\r
6736  *\r
6737  *  Error dialogs\r
6738  * \r
6739 \*---------------------------------------------------------------------------*/\r
6740 \r
6741 /* Nonmodal error box */\r
6742 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6743                              WPARAM wParam, LPARAM lParam);\r
6744 \r
6745 VOID\r
6746 ErrorPopUp(char *title, char *content)\r
6747 {\r
6748   FARPROC lpProc;\r
6749   char *p, *q;\r
6750   BOOLEAN modal = hwndMain == NULL;\r
6751 \r
6752   p = content;\r
6753   q = errorMessage;\r
6754   while (*p) {\r
6755     if (*p == '\n') {\r
6756       if (modal) {\r
6757         *q++ = ' ';\r
6758         p++;\r
6759       } else {\r
6760         *q++ = '\r';\r
6761         *q++ = *p++;\r
6762       }\r
6763     } else {\r
6764       *q++ = *p++;\r
6765     }\r
6766   }\r
6767   *q = NULLCHAR;\r
6768   strncpy(errorTitle, title, sizeof(errorTitle));\r
6769   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6770   \r
6771   if (modal) {\r
6772     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6773   } else {\r
6774     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6775     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6776                  hwndMain, (DLGPROC)lpProc);\r
6777     FreeProcInstance(lpProc);\r
6778   }\r
6779 }\r
6780 \r
6781 VOID\r
6782 ErrorPopDown()\r
6783 {\r
6784   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6785   if (errorDialog == NULL) return;\r
6786   DestroyWindow(errorDialog);\r
6787   errorDialog = NULL;\r
6788   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6789 }\r
6790 \r
6791 LRESULT CALLBACK\r
6792 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6793 {\r
6794   RECT rChild;\r
6795 \r
6796   switch (message) {\r
6797   case WM_INITDIALOG:\r
6798     GetWindowRect(hDlg, &rChild);\r
6799 \r
6800     /*\r
6801     SetWindowPos(hDlg, NULL, rChild.left,\r
6802       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6803       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6804     */\r
6805 \r
6806     /* \r
6807         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6808         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6809         and it doesn't work when you resize the dialog.\r
6810         For now, just give it a default position.\r
6811     */\r
6812     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6813     Translate(hDlg, DLG_Error);\r
6814 \r
6815     errorDialog = hDlg;\r
6816     SetWindowText(hDlg, errorTitle);\r
6817     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6818     return FALSE;\r
6819 \r
6820   case WM_COMMAND:\r
6821     switch (LOWORD(wParam)) {\r
6822     case IDOK:\r
6823     case IDCANCEL:\r
6824       if (errorDialog == hDlg) errorDialog = NULL;\r
6825       DestroyWindow(hDlg);\r
6826       return TRUE;\r
6827 \r
6828     default:\r
6829       break;\r
6830     }\r
6831     break;\r
6832   }\r
6833   return FALSE;\r
6834 }\r
6835 \r
6836 #ifdef GOTHIC\r
6837 HWND gothicDialog = NULL;\r
6838 \r
6839 LRESULT CALLBACK\r
6840 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6841 {\r
6842   RECT rChild;\r
6843   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6844 \r
6845   switch (message) {\r
6846   case WM_INITDIALOG:\r
6847     GetWindowRect(hDlg, &rChild);\r
6848 \r
6849     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6850                                                              SWP_NOZORDER);\r
6851 \r
6852     /* \r
6853         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6854         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6855         and it doesn't work when you resize the dialog.\r
6856         For now, just give it a default position.\r
6857     */\r
6858     gothicDialog = hDlg;\r
6859     SetWindowText(hDlg, errorTitle);\r
6860     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6861     return FALSE;\r
6862 \r
6863   case WM_COMMAND:\r
6864     switch (LOWORD(wParam)) {\r
6865     case IDOK:\r
6866     case IDCANCEL:\r
6867       if (errorDialog == hDlg) errorDialog = NULL;\r
6868       DestroyWindow(hDlg);\r
6869       return TRUE;\r
6870 \r
6871     default:\r
6872       break;\r
6873     }\r
6874     break;\r
6875   }\r
6876   return FALSE;\r
6877 }\r
6878 \r
6879 VOID\r
6880 GothicPopUp(char *title, VariantClass variant)\r
6881 {\r
6882   FARPROC lpProc;\r
6883   static char *lastTitle;\r
6884 \r
6885   strncpy(errorTitle, title, sizeof(errorTitle));\r
6886   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6887 \r
6888   if(lastTitle != title && gothicDialog != NULL) {\r
6889     DestroyWindow(gothicDialog);\r
6890     gothicDialog = NULL;\r
6891   }\r
6892   if(variant != VariantNormal && gothicDialog == NULL) {\r
6893     title = lastTitle;\r
6894     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6895     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6896                  hwndMain, (DLGPROC)lpProc);\r
6897     FreeProcInstance(lpProc);\r
6898   }\r
6899 }\r
6900 #endif\r
6901 \r
6902 /*---------------------------------------------------------------------------*\\r
6903  *\r
6904  *  Ics Interaction console functions\r
6905  *\r
6906 \*---------------------------------------------------------------------------*/\r
6907 \r
6908 #define HISTORY_SIZE 64\r
6909 static char *history[HISTORY_SIZE];\r
6910 int histIn = 0, histP = 0;\r
6911 \r
6912 \r
6913 VOID\r
6914 SaveInHistory(char *cmd)\r
6915 {\r
6916   if (history[histIn] != NULL) {\r
6917     free(history[histIn]);\r
6918     history[histIn] = NULL;\r
6919   }\r
6920   if (*cmd == NULLCHAR) return;\r
6921   history[histIn] = StrSave(cmd);\r
6922   histIn = (histIn + 1) % HISTORY_SIZE;\r
6923   if (history[histIn] != NULL) {\r
6924     free(history[histIn]);\r
6925 \r
6926     history[histIn] = NULL;\r
6927   }\r
6928   histP = histIn;\r
6929 }\r
6930 \r
6931 char *\r
6932 PrevInHistory(char *cmd)\r
6933 {\r
6934   int newhp;\r
6935   if (histP == histIn) {\r
6936     if (history[histIn] != NULL) free(history[histIn]);\r
6937     history[histIn] = StrSave(cmd);\r
6938   }\r
6939   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6940   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6941   histP = newhp;\r
6942   return history[histP];\r
6943 }\r
6944 \r
6945 char *\r
6946 NextInHistory()\r
6947 {\r
6948   if (histP == histIn) return NULL;\r
6949   histP = (histP + 1) % HISTORY_SIZE;\r
6950   return history[histP];   \r
6951 }\r
6952 \r
6953 HMENU\r
6954 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6955 {\r
6956   HMENU hmenu, h;\r
6957   int i = 0;\r
6958   hmenu = LoadMenu(hInst, "TextMenu");\r
6959   h = GetSubMenu(hmenu, 0);\r
6960   while (e->item) {\r
6961     if (strcmp(e->item, "-") == 0) {\r
6962       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6963     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6964       int flags = MF_STRING, j = 0;\r
6965       if (e->item[0] == '|') {\r
6966         flags |= MF_MENUBARBREAK;\r
6967         j++;\r
6968       }\r
6969       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6970       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6971     }\r
6972     e++;\r
6973     i++;\r
6974   } \r
6975   return hmenu;\r
6976 }\r
6977 \r
6978 WNDPROC consoleTextWindowProc;\r
6979 \r
6980 void\r
6981 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6982 {\r
6983   char buf[MSG_SIZ], name[MSG_SIZ];\r
6984   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6985   CHARRANGE sel;\r
6986 \r
6987   if (!getname) {\r
6988     SetWindowText(hInput, command);\r
6989     if (immediate) {\r
6990       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6991     } else {\r
6992       sel.cpMin = 999999;\r
6993       sel.cpMax = 999999;\r
6994       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6995       SetFocus(hInput);\r
6996     }\r
6997     return;\r
6998   }    \r
6999   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7000   if (sel.cpMin == sel.cpMax) {\r
7001     /* Expand to surrounding word */\r
7002     TEXTRANGE tr;\r
7003     do {\r
7004       tr.chrg.cpMax = sel.cpMin;\r
7005       tr.chrg.cpMin = --sel.cpMin;\r
7006       if (sel.cpMin < 0) break;\r
7007       tr.lpstrText = name;\r
7008       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7009     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7010     sel.cpMin++;\r
7011 \r
7012     do {\r
7013       tr.chrg.cpMin = sel.cpMax;\r
7014       tr.chrg.cpMax = ++sel.cpMax;\r
7015       tr.lpstrText = name;\r
7016       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7017     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7018     sel.cpMax--;\r
7019 \r
7020     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7021       MessageBeep(MB_ICONEXCLAMATION);\r
7022       return;\r
7023     }\r
7024     tr.chrg = sel;\r
7025     tr.lpstrText = name;\r
7026     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7027   } else {\r
7028     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7029       MessageBeep(MB_ICONEXCLAMATION);\r
7030       return;\r
7031     }\r
7032     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7033   }\r
7034   if (immediate) {\r
7035     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7036     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7037     SetWindowText(hInput, buf);\r
7038     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7039   } else {\r
7040     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7041       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7042     SetWindowText(hInput, buf);\r
7043     sel.cpMin = 999999;\r
7044     sel.cpMax = 999999;\r
7045     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7046     SetFocus(hInput);\r
7047   }\r
7048 }\r
7049 \r
7050 LRESULT CALLBACK \r
7051 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7052 {\r
7053   HWND hInput;\r
7054   CHARRANGE sel;\r
7055 \r
7056   switch (message) {\r
7057   case WM_KEYDOWN:\r
7058     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7059     if(wParam=='R') return 0;\r
7060     switch (wParam) {\r
7061     case VK_PRIOR:\r
7062       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7063       return 0;\r
7064     case VK_NEXT:\r
7065       sel.cpMin = 999999;\r
7066       sel.cpMax = 999999;\r
7067       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7068       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7069       return 0;\r
7070     }\r
7071     break;\r
7072   case WM_CHAR:\r
7073    if(wParam != '\022') {\r
7074     if (wParam == '\t') {\r
7075       if (GetKeyState(VK_SHIFT) < 0) {\r
7076         /* shifted */\r
7077         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7078         if (buttonDesc[0].hwnd) {\r
7079           SetFocus(buttonDesc[0].hwnd);\r
7080         } else {\r
7081           SetFocus(hwndMain);\r
7082         }\r
7083       } else {\r
7084         /* unshifted */\r
7085         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7086       }\r
7087     } else {\r
7088       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7089       JAWS_DELETE( SetFocus(hInput); )\r
7090       SendMessage(hInput, message, wParam, lParam);\r
7091     }\r
7092     return 0;\r
7093    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7094    lParam = -1;\r
7095   case WM_RBUTTONDOWN:\r
7096     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7097       /* Move selection here if it was empty */\r
7098       POINT pt;\r
7099       pt.x = LOWORD(lParam);\r
7100       pt.y = HIWORD(lParam);\r
7101       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7102       if (sel.cpMin == sel.cpMax) {\r
7103         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7104         sel.cpMax = sel.cpMin;\r
7105         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7106       }\r
7107       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7108 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7109       POINT pt;\r
7110       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7111       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7112       if (sel.cpMin == sel.cpMax) {\r
7113         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7114         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7115       }\r
7116       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7117         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7118       }\r
7119       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7120       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7121       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7122       MenuPopup(hwnd, pt, hmenu, -1);\r
7123 }\r
7124     }\r
7125     return 0;\r
7126   case WM_RBUTTONUP:\r
7127     if (GetKeyState(VK_SHIFT) & ~1) {\r
7128       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7129         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7130     }\r
7131     return 0;\r
7132   case WM_PASTE:\r
7133     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7134     SetFocus(hInput);\r
7135     return SendMessage(hInput, message, wParam, lParam);\r
7136   case WM_MBUTTONDOWN:\r
7137     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7138   case WM_COMMAND:\r
7139     switch (LOWORD(wParam)) {\r
7140     case IDM_QuickPaste:\r
7141       {\r
7142         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7143         if (sel.cpMin == sel.cpMax) {\r
7144           MessageBeep(MB_ICONEXCLAMATION);\r
7145           return 0;\r
7146         }\r
7147         SendMessage(hwnd, WM_COPY, 0, 0);\r
7148         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7149         SendMessage(hInput, WM_PASTE, 0, 0);\r
7150         SetFocus(hInput);\r
7151         return 0;\r
7152       }\r
7153     case IDM_Cut:\r
7154       SendMessage(hwnd, WM_CUT, 0, 0);\r
7155       return 0;\r
7156     case IDM_Paste:\r
7157       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7158       return 0;\r
7159     case IDM_Copy:\r
7160       SendMessage(hwnd, WM_COPY, 0, 0);\r
7161       return 0;\r
7162     default:\r
7163       {\r
7164         int i = LOWORD(wParam) - IDM_CommandX;\r
7165         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7166             icsTextMenuEntry[i].command != NULL) {\r
7167           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7168                    icsTextMenuEntry[i].getname,\r
7169                    icsTextMenuEntry[i].immediate);\r
7170           return 0;\r
7171         }\r
7172       }\r
7173       break;\r
7174     }\r
7175     break;\r
7176   }\r
7177   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7178 }\r
7179 \r
7180 WNDPROC consoleInputWindowProc;\r
7181 \r
7182 LRESULT CALLBACK\r
7183 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7184 {\r
7185   char buf[MSG_SIZ];\r
7186   char *p;\r
7187   static BOOL sendNextChar = FALSE;\r
7188   static BOOL quoteNextChar = FALSE;\r
7189   InputSource *is = consoleInputSource;\r
7190   CHARFORMAT cf;\r
7191   CHARRANGE sel;\r
7192 \r
7193   switch (message) {\r
7194   case WM_CHAR:\r
7195     if (!appData.localLineEditing || sendNextChar) {\r
7196       is->buf[0] = (CHAR) wParam;\r
7197       is->count = 1;\r
7198       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7199       sendNextChar = FALSE;\r
7200       return 0;\r
7201     }\r
7202     if (quoteNextChar) {\r
7203       buf[0] = (char) wParam;\r
7204       buf[1] = NULLCHAR;\r
7205       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7206       quoteNextChar = FALSE;\r
7207       return 0;\r
7208     }\r
7209     switch (wParam) {\r
7210     case '\r':   /* Enter key */\r
7211       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7212       if (consoleEcho) SaveInHistory(is->buf);\r
7213       is->buf[is->count++] = '\n';\r
7214       is->buf[is->count] = NULLCHAR;\r
7215       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7216       if (consoleEcho) {\r
7217         ConsoleOutput(is->buf, is->count, TRUE);\r
7218       } else if (appData.localLineEditing) {\r
7219         ConsoleOutput("\n", 1, TRUE);\r
7220       }\r
7221       /* fall thru */\r
7222     case '\033': /* Escape key */\r
7223       SetWindowText(hwnd, "");\r
7224       cf.cbSize = sizeof(CHARFORMAT);\r
7225       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7226       if (consoleEcho) {\r
7227         cf.crTextColor = textAttribs[ColorNormal].color;\r
7228       } else {\r
7229         cf.crTextColor = COLOR_ECHOOFF;\r
7230       }\r
7231       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7232       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7233       return 0;\r
7234     case '\t':   /* Tab key */\r
7235       if (GetKeyState(VK_SHIFT) < 0) {\r
7236         /* shifted */\r
7237         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7238       } else {\r
7239         /* unshifted */\r
7240         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7241         if (buttonDesc[0].hwnd) {\r
7242           SetFocus(buttonDesc[0].hwnd);\r
7243         } else {\r
7244           SetFocus(hwndMain);\r
7245         }\r
7246       }\r
7247       return 0;\r
7248     case '\023': /* Ctrl+S */\r
7249       sendNextChar = TRUE;\r
7250       return 0;\r
7251     case '\021': /* Ctrl+Q */\r
7252       quoteNextChar = TRUE;\r
7253       return 0;\r
7254     JAWS_REPLAY\r
7255     default:\r
7256       break;\r
7257     }\r
7258     break;\r
7259   case WM_KEYDOWN:\r
7260     switch (wParam) {\r
7261     case VK_UP:\r
7262       GetWindowText(hwnd, buf, MSG_SIZ);\r
7263       p = PrevInHistory(buf);\r
7264       if (p != NULL) {\r
7265         SetWindowText(hwnd, p);\r
7266         sel.cpMin = 999999;\r
7267         sel.cpMax = 999999;\r
7268         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7269         return 0;\r
7270       }\r
7271       break;\r
7272     case VK_DOWN:\r
7273       p = NextInHistory();\r
7274       if (p != NULL) {\r
7275         SetWindowText(hwnd, p);\r
7276         sel.cpMin = 999999;\r
7277         sel.cpMax = 999999;\r
7278         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7279         return 0;\r
7280       }\r
7281       break;\r
7282     case VK_HOME:\r
7283     case VK_END:\r
7284       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7285       /* fall thru */\r
7286     case VK_PRIOR:\r
7287     case VK_NEXT:\r
7288       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7289       return 0;\r
7290     }\r
7291     break;\r
7292   case WM_MBUTTONDOWN:\r
7293     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7294       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7295     break;\r
7296   case WM_RBUTTONUP:\r
7297     if (GetKeyState(VK_SHIFT) & ~1) {\r
7298       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7299         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7300     } else {\r
7301       POINT pt;\r
7302       HMENU hmenu;\r
7303       hmenu = LoadMenu(hInst, "InputMenu");\r
7304       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7305       if (sel.cpMin == sel.cpMax) {\r
7306         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7307         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7308       }\r
7309       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7310         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7311       }\r
7312       pt.x = LOWORD(lParam);\r
7313       pt.y = HIWORD(lParam);\r
7314       MenuPopup(hwnd, pt, hmenu, -1);\r
7315     }\r
7316     return 0;\r
7317   case WM_COMMAND:\r
7318     switch (LOWORD(wParam)) { \r
7319     case IDM_Undo:\r
7320       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7321       return 0;\r
7322     case IDM_SelectAll:\r
7323       sel.cpMin = 0;\r
7324       sel.cpMax = -1; /*999999?*/\r
7325       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7326       return 0;\r
7327     case IDM_Cut:\r
7328       SendMessage(hwnd, WM_CUT, 0, 0);\r
7329       return 0;\r
7330     case IDM_Paste:\r
7331       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7332       return 0;\r
7333     case IDM_Copy:\r
7334       SendMessage(hwnd, WM_COPY, 0, 0);\r
7335       return 0;\r
7336     }\r
7337     break;\r
7338   }\r
7339   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7340 }\r
7341 \r
7342 #define CO_MAX  100000\r
7343 #define CO_TRIM   1000\r
7344 \r
7345 LRESULT CALLBACK\r
7346 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7347 {\r
7348   static SnapData sd;\r
7349   HWND hText, hInput;\r
7350   RECT rect;\r
7351   static int sizeX, sizeY;\r
7352   int newSizeX, newSizeY;\r
7353   MINMAXINFO *mmi;\r
7354   WORD wMask;\r
7355 \r
7356   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7357   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7358 \r
7359   switch (message) {\r
7360   case WM_NOTIFY:\r
7361     if (((NMHDR*)lParam)->code == EN_LINK)\r
7362     {\r
7363       ENLINK *pLink = (ENLINK*)lParam;\r
7364       if (pLink->msg == WM_LBUTTONUP)\r
7365       {\r
7366         TEXTRANGE tr;\r
7367 \r
7368         tr.chrg = pLink->chrg;\r
7369         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7370         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7371         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7372         free(tr.lpstrText);\r
7373       }\r
7374     }\r
7375     break;\r
7376   case WM_INITDIALOG: /* message: initialize dialog box */\r
7377     hwndConsole = hDlg;\r
7378     SetFocus(hInput);\r
7379     consoleTextWindowProc = (WNDPROC)\r
7380       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7381     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7382     consoleInputWindowProc = (WNDPROC)\r
7383       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7384     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7385     Colorize(ColorNormal, TRUE);\r
7386     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7387     ChangedConsoleFont();\r
7388     GetClientRect(hDlg, &rect);\r
7389     sizeX = rect.right;\r
7390     sizeY = rect.bottom;\r
7391     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7392         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7393       WINDOWPLACEMENT wp;\r
7394       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7395       wp.length = sizeof(WINDOWPLACEMENT);\r
7396       wp.flags = 0;\r
7397       wp.showCmd = SW_SHOW;\r
7398       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7399       wp.rcNormalPosition.left = wpConsole.x;\r
7400       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7401       wp.rcNormalPosition.top = wpConsole.y;\r
7402       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7403       SetWindowPlacement(hDlg, &wp);\r
7404     }\r
7405 \r
7406    // [HGM] Chessknight's change 2004-07-13\r
7407    else { /* Determine Defaults */\r
7408        WINDOWPLACEMENT wp;\r
7409        wpConsole.x = wpMain.width + 1;\r
7410        wpConsole.y = wpMain.y;\r
7411        wpConsole.width = screenWidth -  wpMain.width;\r
7412        wpConsole.height = wpMain.height;\r
7413        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7414        wp.length = sizeof(WINDOWPLACEMENT);\r
7415        wp.flags = 0;\r
7416        wp.showCmd = SW_SHOW;\r
7417        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7418        wp.rcNormalPosition.left = wpConsole.x;\r
7419        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7420        wp.rcNormalPosition.top = wpConsole.y;\r
7421        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7422        SetWindowPlacement(hDlg, &wp);\r
7423     }\r
7424 \r
7425    // Allow hText to highlight URLs and send notifications on them\r
7426    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7427    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7428    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7429    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7430 \r
7431     return FALSE;\r
7432 \r
7433   case WM_SETFOCUS:\r
7434     SetFocus(hInput);\r
7435     return 0;\r
7436 \r
7437   case WM_CLOSE:\r
7438     ExitEvent(0);\r
7439     /* not reached */\r
7440     break;\r
7441 \r
7442   case WM_SIZE:\r
7443     if (IsIconic(hDlg)) break;\r
7444     newSizeX = LOWORD(lParam);\r
7445     newSizeY = HIWORD(lParam);\r
7446     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7447       RECT rectText, rectInput;\r
7448       POINT pt;\r
7449       int newTextHeight, newTextWidth;\r
7450       GetWindowRect(hText, &rectText);\r
7451       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7452       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7453       if (newTextHeight < 0) {\r
7454         newSizeY += -newTextHeight;\r
7455         newTextHeight = 0;\r
7456       }\r
7457       SetWindowPos(hText, NULL, 0, 0,\r
7458         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7459       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7460       pt.x = rectInput.left;\r
7461       pt.y = rectInput.top + newSizeY - sizeY;\r
7462       ScreenToClient(hDlg, &pt);\r
7463       SetWindowPos(hInput, NULL, \r
7464         pt.x, pt.y, /* needs client coords */   \r
7465         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7466         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7467     }\r
7468     sizeX = newSizeX;\r
7469     sizeY = newSizeY;\r
7470     break;\r
7471 \r
7472   case WM_GETMINMAXINFO:\r
7473     /* Prevent resizing window too small */\r
7474     mmi = (MINMAXINFO *) lParam;\r
7475     mmi->ptMinTrackSize.x = 100;\r
7476     mmi->ptMinTrackSize.y = 100;\r
7477     break;\r
7478 \r
7479   /* [AS] Snapping */\r
7480   case WM_ENTERSIZEMOVE:\r
7481     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7482 \r
7483   case WM_SIZING:\r
7484     return OnSizing( &sd, hDlg, wParam, lParam );\r
7485 \r
7486   case WM_MOVING:\r
7487     return OnMoving( &sd, hDlg, wParam, lParam );\r
7488 \r
7489   case WM_EXITSIZEMOVE:\r
7490         UpdateICSWidth(hText);\r
7491     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7492   }\r
7493 \r
7494   return DefWindowProc(hDlg, message, wParam, lParam);\r
7495 }\r
7496 \r
7497 \r
7498 VOID\r
7499 ConsoleCreate()\r
7500 {\r
7501   HWND hCons;\r
7502   if (hwndConsole) return;\r
7503   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7504   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7505 }\r
7506 \r
7507 \r
7508 VOID\r
7509 ConsoleOutput(char* data, int length, int forceVisible)\r
7510 {\r
7511   HWND hText;\r
7512   int trim, exlen;\r
7513   char *p, *q;\r
7514   char buf[CO_MAX+1];\r
7515   POINT pEnd;\r
7516   RECT rect;\r
7517   static int delayLF = 0;\r
7518   CHARRANGE savesel, sel;\r
7519 \r
7520   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7521   p = data;\r
7522   q = buf;\r
7523   if (delayLF) {\r
7524     *q++ = '\r';\r
7525     *q++ = '\n';\r
7526     delayLF = 0;\r
7527   }\r
7528   while (length--) {\r
7529     if (*p == '\n') {\r
7530       if (*++p) {\r
7531         *q++ = '\r';\r
7532         *q++ = '\n';\r
7533       } else {\r
7534         delayLF = 1;\r
7535       }\r
7536     } else if (*p == '\007') {\r
7537        MyPlaySound(&sounds[(int)SoundBell]);\r
7538        p++;\r
7539     } else {\r
7540       *q++ = *p++;\r
7541     }\r
7542   }\r
7543   *q = NULLCHAR;\r
7544   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7545   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7546   /* Save current selection */\r
7547   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7548   exlen = GetWindowTextLength(hText);\r
7549   /* Find out whether current end of text is visible */\r
7550   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7551   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7552   /* Trim existing text if it's too long */\r
7553   if (exlen + (q - buf) > CO_MAX) {\r
7554     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7555     sel.cpMin = 0;\r
7556     sel.cpMax = trim;\r
7557     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7558     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7559     exlen -= trim;\r
7560     savesel.cpMin -= trim;\r
7561     savesel.cpMax -= trim;\r
7562     if (exlen < 0) exlen = 0;\r
7563     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7564     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7565   }\r
7566   /* Append the new text */\r
7567   sel.cpMin = exlen;\r
7568   sel.cpMax = exlen;\r
7569   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7570   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7571   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7572   if (forceVisible || exlen == 0 ||\r
7573       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7574        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7575     /* Scroll to make new end of text visible if old end of text\r
7576        was visible or new text is an echo of user typein */\r
7577     sel.cpMin = 9999999;\r
7578     sel.cpMax = 9999999;\r
7579     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7580     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7581     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7582     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7583   }\r
7584   if (savesel.cpMax == exlen || forceVisible) {\r
7585     /* Move insert point to new end of text if it was at the old\r
7586        end of text or if the new text is an echo of user typein */\r
7587     sel.cpMin = 9999999;\r
7588     sel.cpMax = 9999999;\r
7589     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7590   } else {\r
7591     /* Restore previous selection */\r
7592     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7593   }\r
7594   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7595 }\r
7596 \r
7597 /*---------*/\r
7598 \r
7599 \r
7600 void\r
7601 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7602 {\r
7603   char buf[100];\r
7604   char *str;\r
7605   COLORREF oldFg, oldBg;\r
7606   HFONT oldFont;\r
7607   RECT rect;\r
7608 \r
7609   if(copyNumber > 1)\r
7610     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7611 \r
7612   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7613   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7614   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7615 \r
7616   rect.left = x;\r
7617   rect.right = x + squareSize;\r
7618   rect.top  = y;\r
7619   rect.bottom = y + squareSize;\r
7620   str = buf;\r
7621 \r
7622   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7623                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7624              y, ETO_CLIPPED|ETO_OPAQUE,\r
7625              &rect, str, strlen(str), NULL);\r
7626 \r
7627   (void) SetTextColor(hdc, oldFg);\r
7628   (void) SetBkColor(hdc, oldBg);\r
7629   (void) SelectObject(hdc, oldFont);\r
7630 }\r
7631 \r
7632 void\r
7633 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7634               RECT *rect, char *color, char *flagFell)\r
7635 {\r
7636   char buf[100];\r
7637   char *str;\r
7638   COLORREF oldFg, oldBg;\r
7639   HFONT oldFont;\r
7640 \r
7641   if (twoBoards && partnerUp) return;\r
7642   if (appData.clockMode) {\r
7643     if (tinyLayout)\r
7644       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7645     else\r
7646       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7647     str = buf;\r
7648   } else {\r
7649     str = color;\r
7650   }\r
7651 \r
7652   if (highlight) {\r
7653     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7654     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7655   } else {\r
7656     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7657     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7658   }\r
7659   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7660 \r
7661   JAWS_SILENCE\r
7662 \r
7663   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7664              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7665              rect, str, strlen(str), NULL);\r
7666   if(logoHeight > 0 && appData.clockMode) {\r
7667       RECT r;\r
7668       str += strlen(color)+2;\r
7669       r.top = rect->top + logoHeight/2;\r
7670       r.left = rect->left;\r
7671       r.right = rect->right;\r
7672       r.bottom = rect->bottom;\r
7673       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7674                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7675                  &r, str, strlen(str), NULL);\r
7676   }\r
7677   (void) SetTextColor(hdc, oldFg);\r
7678   (void) SetBkColor(hdc, oldBg);\r
7679   (void) SelectObject(hdc, oldFont);\r
7680 }\r
7681 \r
7682 \r
7683 int\r
7684 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7685            OVERLAPPED *ovl)\r
7686 {\r
7687   int ok, err;\r
7688 \r
7689   /* [AS]  */\r
7690   if( count <= 0 ) {\r
7691     if (appData.debugMode) {\r
7692       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7693     }\r
7694 \r
7695     return ERROR_INVALID_USER_BUFFER;\r
7696   }\r
7697 \r
7698   ResetEvent(ovl->hEvent);\r
7699   ovl->Offset = ovl->OffsetHigh = 0;\r
7700   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7701   if (ok) {\r
7702     err = NO_ERROR;\r
7703   } else {\r
7704     err = GetLastError();\r
7705     if (err == ERROR_IO_PENDING) {\r
7706       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7707       if (ok)\r
7708         err = NO_ERROR;\r
7709       else\r
7710         err = GetLastError();\r
7711     }\r
7712   }\r
7713   return err;\r
7714 }\r
7715 \r
7716 int\r
7717 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7718             OVERLAPPED *ovl)\r
7719 {\r
7720   int ok, err;\r
7721 \r
7722   ResetEvent(ovl->hEvent);\r
7723   ovl->Offset = ovl->OffsetHigh = 0;\r
7724   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7725   if (ok) {\r
7726     err = NO_ERROR;\r
7727   } else {\r
7728     err = GetLastError();\r
7729     if (err == ERROR_IO_PENDING) {\r
7730       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7731       if (ok)\r
7732         err = NO_ERROR;\r
7733       else\r
7734         err = GetLastError();\r
7735     }\r
7736 \r
7737   }\r
7738   return err;\r
7739 }\r
7740 \r
7741 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7742 void CheckForInputBufferFull( InputSource * is )\r
7743 {\r
7744     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7745         /* Look for end of line */\r
7746         char * p = is->buf;\r
7747         \r
7748         while( p < is->next && *p != '\n' ) {\r
7749             p++;\r
7750         }\r
7751 \r
7752         if( p >= is->next ) {\r
7753             if (appData.debugMode) {\r
7754                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7755             }\r
7756 \r
7757             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7758             is->count = (DWORD) -1;\r
7759             is->next = is->buf;\r
7760         }\r
7761     }\r
7762 }\r
7763 \r
7764 DWORD\r
7765 InputThread(LPVOID arg)\r
7766 {\r
7767   InputSource *is;\r
7768   OVERLAPPED ovl;\r
7769 \r
7770   is = (InputSource *) arg;\r
7771   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7772   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7773   while (is->hThread != NULL) {\r
7774     is->error = DoReadFile(is->hFile, is->next,\r
7775                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7776                            &is->count, &ovl);\r
7777     if (is->error == NO_ERROR) {\r
7778       is->next += is->count;\r
7779     } else {\r
7780       if (is->error == ERROR_BROKEN_PIPE) {\r
7781         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7782         is->count = 0;\r
7783       } else {\r
7784         is->count = (DWORD) -1;\r
7785         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7786         break; \r
7787       }\r
7788     }\r
7789 \r
7790     CheckForInputBufferFull( is );\r
7791 \r
7792     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7793 \r
7794     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7795 \r
7796     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7797   }\r
7798 \r
7799   CloseHandle(ovl.hEvent);\r
7800   CloseHandle(is->hFile);\r
7801 \r
7802   if (appData.debugMode) {\r
7803     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7804   }\r
7805 \r
7806   return 0;\r
7807 }\r
7808 \r
7809 \r
7810 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7811 DWORD\r
7812 NonOvlInputThread(LPVOID arg)\r
7813 {\r
7814   InputSource *is;\r
7815   char *p, *q;\r
7816   int i;\r
7817   char prev;\r
7818 \r
7819   is = (InputSource *) arg;\r
7820   while (is->hThread != NULL) {\r
7821     is->error = ReadFile(is->hFile, is->next,\r
7822                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7823                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7824     if (is->error == NO_ERROR) {\r
7825       /* Change CRLF to LF */\r
7826       if (is->next > is->buf) {\r
7827         p = is->next - 1;\r
7828         i = is->count + 1;\r
7829       } else {\r
7830         p = is->next;\r
7831         i = is->count;\r
7832       }\r
7833       q = p;\r
7834       prev = NULLCHAR;\r
7835       while (i > 0) {\r
7836         if (prev == '\r' && *p == '\n') {\r
7837           *(q-1) = '\n';\r
7838           is->count--;\r
7839         } else { \r
7840           *q++ = *p;\r
7841         }\r
7842         prev = *p++;\r
7843         i--;\r
7844       }\r
7845       *q = NULLCHAR;\r
7846       is->next = q;\r
7847     } else {\r
7848       if (is->error == ERROR_BROKEN_PIPE) {\r
7849         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7850         is->count = 0; \r
7851       } else {\r
7852         is->count = (DWORD) -1;\r
7853       }\r
7854     }\r
7855 \r
7856     CheckForInputBufferFull( is );\r
7857 \r
7858     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7859 \r
7860     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7861 \r
7862     if (is->count < 0) break;  /* Quit on error */\r
7863   }\r
7864   CloseHandle(is->hFile);\r
7865   return 0;\r
7866 }\r
7867 \r
7868 DWORD\r
7869 SocketInputThread(LPVOID arg)\r
7870 {\r
7871   InputSource *is;\r
7872 \r
7873   is = (InputSource *) arg;\r
7874   while (is->hThread != NULL) {\r
7875     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7876     if ((int)is->count == SOCKET_ERROR) {\r
7877       is->count = (DWORD) -1;\r
7878       is->error = WSAGetLastError();\r
7879     } else {\r
7880       is->error = NO_ERROR;\r
7881       is->next += is->count;\r
7882       if (is->count == 0 && is->second == is) {\r
7883         /* End of file on stderr; quit with no message */\r
7884         break;\r
7885       }\r
7886     }\r
7887     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7888 \r
7889     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7890 \r
7891     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7892   }\r
7893   return 0;\r
7894 }\r
7895 \r
7896 VOID\r
7897 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7898 {\r
7899   InputSource *is;\r
7900 \r
7901   is = (InputSource *) lParam;\r
7902   if (is->lineByLine) {\r
7903     /* Feed in lines one by one */\r
7904     char *p = is->buf;\r
7905     char *q = p;\r
7906     while (q < is->next) {\r
7907       if (*q++ == '\n') {\r
7908         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7909         p = q;\r
7910       }\r
7911     }\r
7912     \r
7913     /* Move any partial line to the start of the buffer */\r
7914     q = is->buf;\r
7915     while (p < is->next) {\r
7916       *q++ = *p++;\r
7917     }\r
7918     is->next = q;\r
7919 \r
7920     if (is->error != NO_ERROR || is->count == 0) {\r
7921       /* Notify backend of the error.  Note: If there was a partial\r
7922          line at the end, it is not flushed through. */\r
7923       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7924     }\r
7925   } else {\r
7926     /* Feed in the whole chunk of input at once */\r
7927     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7928     is->next = is->buf;\r
7929   }\r
7930 }\r
7931 \r
7932 /*---------------------------------------------------------------------------*\\r
7933  *\r
7934  *  Menu enables. Used when setting various modes.\r
7935  *\r
7936 \*---------------------------------------------------------------------------*/\r
7937 \r
7938 typedef struct {\r
7939   int item;\r
7940   int flags;\r
7941 } Enables;\r
7942 \r
7943 VOID\r
7944 GreyRevert(Boolean grey)\r
7945 { // [HGM] vari: for retracting variations in local mode\r
7946   HMENU hmenu = GetMenu(hwndMain);\r
7947   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7948   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7949 }\r
7950 \r
7951 VOID\r
7952 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7953 {\r
7954   while (enab->item > 0) {\r
7955     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7956     enab++;\r
7957   }\r
7958 }\r
7959 \r
7960 Enables gnuEnables[] = {\r
7961   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7974 \r
7975   // Needed to switch from ncp to GNU mode on Engine Load\r
7976   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7977   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7978   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7979   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7980   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7981   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7982   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7983   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7984   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7989   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7990   { -1, -1 }\r
7991 };\r
7992 \r
7993 Enables icsEnables[] = {\r
7994   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7995   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8001   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8002   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8003   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8004   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8005   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8010   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8014   { -1, -1 }\r
8015 };\r
8016 \r
8017 #if ZIPPY\r
8018 Enables zippyEnables[] = {\r
8019   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8020   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8021   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8022   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8023   { -1, -1 }\r
8024 };\r
8025 #endif\r
8026 \r
8027 Enables ncpEnables[] = {\r
8028   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8030   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8031   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8037   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8038   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8040   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8041   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8042   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8043   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8044   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8045   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8046   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8047   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8048   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8050   { -1, -1 }\r
8051 };\r
8052 \r
8053 Enables trainingOnEnables[] = {\r
8054   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8055   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8056   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8057   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8058   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8059   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8060   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8063   { -1, -1 }\r
8064 };\r
8065 \r
8066 Enables trainingOffEnables[] = {\r
8067   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8068   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8069   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8070   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8071   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8072   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8073   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8074   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8075   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8076   { -1, -1 }\r
8077 };\r
8078 \r
8079 /* These modify either ncpEnables or gnuEnables */\r
8080 Enables cmailEnables[] = {\r
8081   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8082   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8083   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8084   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8085   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8086   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8087   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8088   { -1, -1 }\r
8089 };\r
8090 \r
8091 Enables machineThinkingEnables[] = {\r
8092   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8093   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8094   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8095   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8096   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8104   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8105 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8107   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8108   { -1, -1 }\r
8109 };\r
8110 \r
8111 Enables userThinkingEnables[] = {\r
8112   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8113   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8114   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8115   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8116   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8117   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8118   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8119   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8120   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8121   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8122   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8123   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8124   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8125 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8126   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8127   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8128   { -1, -1 }\r
8129 };\r
8130 \r
8131 /*---------------------------------------------------------------------------*\\r
8132  *\r
8133  *  Front-end interface functions exported by XBoard.\r
8134  *  Functions appear in same order as prototypes in frontend.h.\r
8135  * \r
8136 \*---------------------------------------------------------------------------*/\r
8137 VOID\r
8138 CheckMark(UINT item, int state)\r
8139 {\r
8140     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8141 }\r
8142 \r
8143 VOID\r
8144 ModeHighlight()\r
8145 {\r
8146   static UINT prevChecked = 0;\r
8147   static int prevPausing = 0;\r
8148   UINT nowChecked;\r
8149 \r
8150   if (pausing != prevPausing) {\r
8151     prevPausing = pausing;\r
8152     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8153                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8154     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8155   }\r
8156 \r
8157   switch (gameMode) {\r
8158   case BeginningOfGame:\r
8159     if (appData.icsActive)\r
8160       nowChecked = IDM_IcsClient;\r
8161     else if (appData.noChessProgram)\r
8162       nowChecked = IDM_EditGame;\r
8163     else\r
8164       nowChecked = IDM_MachineBlack;\r
8165     break;\r
8166   case MachinePlaysBlack:\r
8167     nowChecked = IDM_MachineBlack;\r
8168     break;\r
8169   case MachinePlaysWhite:\r
8170     nowChecked = IDM_MachineWhite;\r
8171     break;\r
8172   case TwoMachinesPlay:\r
8173     nowChecked = IDM_TwoMachines;\r
8174     break;\r
8175   case AnalyzeMode:\r
8176     nowChecked = IDM_AnalysisMode;\r
8177     break;\r
8178   case AnalyzeFile:\r
8179     nowChecked = IDM_AnalyzeFile;\r
8180     break;\r
8181   case EditGame:\r
8182     nowChecked = IDM_EditGame;\r
8183     break;\r
8184   case PlayFromGameFile:\r
8185     nowChecked = IDM_LoadGame;\r
8186     break;\r
8187   case EditPosition:\r
8188     nowChecked = IDM_EditPosition;\r
8189     break;\r
8190   case Training:\r
8191     nowChecked = IDM_Training;\r
8192     break;\r
8193   case IcsPlayingWhite:\r
8194   case IcsPlayingBlack:\r
8195   case IcsObserving:\r
8196   case IcsIdle:\r
8197     nowChecked = IDM_IcsClient;\r
8198     break;\r
8199   default:\r
8200   case EndOfGame:\r
8201     nowChecked = 0;\r
8202     break;\r
8203   }\r
8204   CheckMark(prevChecked, MF_UNCHECKED);\r
8205   CheckMark(nowChecked, MF_CHECKED);\r
8206   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8207 \r
8208   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8209     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8210                           MF_BYCOMMAND|MF_ENABLED);\r
8211   } else {\r
8212     (void) EnableMenuItem(GetMenu(hwndMain), \r
8213                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8214   }\r
8215 \r
8216   prevChecked = nowChecked;\r
8217 \r
8218   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8219   if (appData.icsActive) {\r
8220        if (appData.icsEngineAnalyze) {\r
8221                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8222        } else {\r
8223                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8224        }\r
8225   }\r
8226   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8227 }\r
8228 \r
8229 VOID\r
8230 SetICSMode()\r
8231 {\r
8232   HMENU hmenu = GetMenu(hwndMain);\r
8233   SetMenuEnables(hmenu, icsEnables);\r
8234   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8235     MF_BYCOMMAND|MF_ENABLED);\r
8236 #if ZIPPY\r
8237   if (appData.zippyPlay) {\r
8238     SetMenuEnables(hmenu, zippyEnables);\r
8239     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8240          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8241           MF_BYCOMMAND|MF_ENABLED);\r
8242   }\r
8243 #endif\r
8244 }\r
8245 \r
8246 VOID\r
8247 SetGNUMode()\r
8248 {\r
8249   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8250 }\r
8251 \r
8252 VOID\r
8253 SetNCPMode()\r
8254 {\r
8255   HMENU hmenu = GetMenu(hwndMain);\r
8256   SetMenuEnables(hmenu, ncpEnables);\r
8257     DrawMenuBar(hwndMain);\r
8258 }\r
8259 \r
8260 VOID\r
8261 SetCmailMode()\r
8262 {\r
8263   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8264 }\r
8265 \r
8266 VOID \r
8267 SetTrainingModeOn()\r
8268 {\r
8269   int i;\r
8270   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8271   for (i = 0; i < N_BUTTONS; i++) {\r
8272     if (buttonDesc[i].hwnd != NULL)\r
8273       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8274   }\r
8275   CommentPopDown();\r
8276 }\r
8277 \r
8278 VOID SetTrainingModeOff()\r
8279 {\r
8280   int i;\r
8281   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8282   for (i = 0; i < N_BUTTONS; i++) {\r
8283     if (buttonDesc[i].hwnd != NULL)\r
8284       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8285   }\r
8286 }\r
8287 \r
8288 \r
8289 VOID\r
8290 SetUserThinkingEnables()\r
8291 {\r
8292   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8293 }\r
8294 \r
8295 VOID\r
8296 SetMachineThinkingEnables()\r
8297 {\r
8298   HMENU hMenu = GetMenu(hwndMain);\r
8299   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8300 \r
8301   SetMenuEnables(hMenu, machineThinkingEnables);\r
8302 \r
8303   if (gameMode == MachinePlaysBlack) {\r
8304     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8305   } else if (gameMode == MachinePlaysWhite) {\r
8306     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8307   } else if (gameMode == TwoMachinesPlay) {\r
8308     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8309   }\r
8310 }\r
8311 \r
8312 \r
8313 VOID\r
8314 DisplayTitle(char *str)\r
8315 {\r
8316   char title[MSG_SIZ], *host;\r
8317   if (str[0] != NULLCHAR) {\r
8318     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8319   } else if (appData.icsActive) {\r
8320     if (appData.icsCommPort[0] != NULLCHAR)\r
8321       host = "ICS";\r
8322     else \r
8323       host = appData.icsHost;\r
8324       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8325   } else if (appData.noChessProgram) {\r
8326     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8327   } else {\r
8328     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8329     strcat(title, ": ");\r
8330     strcat(title, first.tidy);\r
8331   }\r
8332   SetWindowText(hwndMain, title);\r
8333 }\r
8334 \r
8335 \r
8336 VOID\r
8337 DisplayMessage(char *str1, char *str2)\r
8338 {\r
8339   HDC hdc;\r
8340   HFONT oldFont;\r
8341   int remain = MESSAGE_TEXT_MAX - 1;\r
8342   int len;\r
8343 \r
8344   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8345   messageText[0] = NULLCHAR;\r
8346   if (*str1) {\r
8347     len = strlen(str1);\r
8348     if (len > remain) len = remain;\r
8349     strncpy(messageText, str1, len);\r
8350     messageText[len] = NULLCHAR;\r
8351     remain -= len;\r
8352   }\r
8353   if (*str2 && remain >= 2) {\r
8354     if (*str1) {\r
8355       strcat(messageText, "  ");\r
8356       remain -= 2;\r
8357     }\r
8358     len = strlen(str2);\r
8359     if (len > remain) len = remain;\r
8360     strncat(messageText, str2, len);\r
8361   }\r
8362   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8363   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8364 \r
8365   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8366 \r
8367   SAYMACHINEMOVE();\r
8368 \r
8369   hdc = GetDC(hwndMain);\r
8370   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8371   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8372              &messageRect, messageText, strlen(messageText), NULL);\r
8373   (void) SelectObject(hdc, oldFont);\r
8374   (void) ReleaseDC(hwndMain, hdc);\r
8375 }\r
8376 \r
8377 VOID\r
8378 DisplayError(char *str, int error)\r
8379 {\r
8380   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8381   int len;\r
8382 \r
8383   if (error == 0) {\r
8384     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8385   } else {\r
8386     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8387                         NULL, error, LANG_NEUTRAL,\r
8388                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8389     if (len > 0) {\r
8390       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8391     } else {\r
8392       ErrorMap *em = errmap;\r
8393       while (em->err != 0 && em->err != error) em++;\r
8394       if (em->err != 0) {\r
8395         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8396       } else {\r
8397         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8398       }\r
8399     }\r
8400   }\r
8401   \r
8402   ErrorPopUp(_("Error"), buf);\r
8403 }\r
8404 \r
8405 \r
8406 VOID\r
8407 DisplayMoveError(char *str)\r
8408 {\r
8409   fromX = fromY = -1;\r
8410   ClearHighlights();\r
8411   DrawPosition(FALSE, NULL);\r
8412   if (appData.popupMoveErrors) {\r
8413     ErrorPopUp(_("Error"), str);\r
8414   } else {\r
8415     DisplayMessage(str, "");\r
8416     moveErrorMessageUp = TRUE;\r
8417   }\r
8418 }\r
8419 \r
8420 VOID\r
8421 DisplayFatalError(char *str, int error, int exitStatus)\r
8422 {\r
8423   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8424   int len;\r
8425   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8426 \r
8427   if (error != 0) {\r
8428     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8429                         NULL, error, LANG_NEUTRAL,\r
8430                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8431     if (len > 0) {\r
8432       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8433     } else {\r
8434       ErrorMap *em = errmap;\r
8435       while (em->err != 0 && em->err != error) em++;\r
8436       if (em->err != 0) {\r
8437         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8438       } else {\r
8439         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8440       }\r
8441     }\r
8442     str = buf;\r
8443   }\r
8444   if (appData.debugMode) {\r
8445     fprintf(debugFP, "%s: %s\n", label, str);\r
8446   }\r
8447   if (appData.popupExitMessage) {\r
8448     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8449                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8450   }\r
8451   ExitEvent(exitStatus);\r
8452 }\r
8453 \r
8454 \r
8455 VOID\r
8456 DisplayInformation(char *str)\r
8457 {\r
8458   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8459 }\r
8460 \r
8461 \r
8462 VOID\r
8463 DisplayNote(char *str)\r
8464 {\r
8465   ErrorPopUp(_("Note"), str);\r
8466 }\r
8467 \r
8468 \r
8469 typedef struct {\r
8470   char *title, *question, *replyPrefix;\r
8471   ProcRef pr;\r
8472 } QuestionParams;\r
8473 \r
8474 LRESULT CALLBACK\r
8475 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8476 {\r
8477   static QuestionParams *qp;\r
8478   char reply[MSG_SIZ];\r
8479   int len, err;\r
8480 \r
8481   switch (message) {\r
8482   case WM_INITDIALOG:\r
8483     qp = (QuestionParams *) lParam;\r
8484     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8485     Translate(hDlg, DLG_Question);\r
8486     SetWindowText(hDlg, qp->title);\r
8487     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8488     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8489     return FALSE;\r
8490 \r
8491   case WM_COMMAND:\r
8492     switch (LOWORD(wParam)) {\r
8493     case IDOK:\r
8494       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8495       if (*reply) strcat(reply, " ");\r
8496       len = strlen(reply);\r
8497       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8498       strcat(reply, "\n");\r
8499       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8500       EndDialog(hDlg, TRUE);\r
8501       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8502       return TRUE;\r
8503     case IDCANCEL:\r
8504       EndDialog(hDlg, FALSE);\r
8505       return TRUE;\r
8506     default:\r
8507       break;\r
8508     }\r
8509     break;\r
8510   }\r
8511   return FALSE;\r
8512 }\r
8513 \r
8514 VOID\r
8515 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8516 {\r
8517     QuestionParams qp;\r
8518     FARPROC lpProc;\r
8519     \r
8520     qp.title = title;\r
8521     qp.question = question;\r
8522     qp.replyPrefix = replyPrefix;\r
8523     qp.pr = pr;\r
8524     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8525     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8526       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8527     FreeProcInstance(lpProc);\r
8528 }\r
8529 \r
8530 /* [AS] Pick FRC position */\r
8531 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8532 {\r
8533     static int * lpIndexFRC;\r
8534     BOOL index_is_ok;\r
8535     char buf[16];\r
8536 \r
8537     switch( message )\r
8538     {\r
8539     case WM_INITDIALOG:\r
8540         lpIndexFRC = (int *) lParam;\r
8541 \r
8542         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8543         Translate(hDlg, DLG_NewGameFRC);\r
8544 \r
8545         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8546         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8547         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8548         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8549 \r
8550         break;\r
8551 \r
8552     case WM_COMMAND:\r
8553         switch( LOWORD(wParam) ) {\r
8554         case IDOK:\r
8555             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8556             EndDialog( hDlg, 0 );\r
8557             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8558             return TRUE;\r
8559         case IDCANCEL:\r
8560             EndDialog( hDlg, 1 );   \r
8561             return TRUE;\r
8562         case IDC_NFG_Edit:\r
8563             if( HIWORD(wParam) == EN_CHANGE ) {\r
8564                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8565 \r
8566                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8567             }\r
8568             return TRUE;\r
8569         case IDC_NFG_Random:\r
8570           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8571             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8572             return TRUE;\r
8573         }\r
8574 \r
8575         break;\r
8576     }\r
8577 \r
8578     return FALSE;\r
8579 }\r
8580 \r
8581 int NewGameFRC()\r
8582 {\r
8583     int result;\r
8584     int index = appData.defaultFrcPosition;\r
8585     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8586 \r
8587     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8588 \r
8589     if( result == 0 ) {\r
8590         appData.defaultFrcPosition = index;\r
8591     }\r
8592 \r
8593     return result;\r
8594 }\r
8595 \r
8596 /* [AS] Game list options. Refactored by HGM */\r
8597 \r
8598 HWND gameListOptionsDialog;\r
8599 \r
8600 // low-level front-end: clear text edit / list widget\r
8601 void\r
8602 \r
8603 GLT_ClearList()\r
8604 {\r
8605     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8606 }\r
8607 \r
8608 // low-level front-end: clear text edit / list widget\r
8609 void\r
8610 GLT_DeSelectList()\r
8611 {\r
8612     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8613 }\r
8614 \r
8615 // low-level front-end: append line to text edit / list widget\r
8616 void\r
8617 GLT_AddToList( char *name )\r
8618 {\r
8619     if( name != 0 ) {\r
8620             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8621     }\r
8622 }\r
8623 \r
8624 // low-level front-end: get line from text edit / list widget\r
8625 Boolean\r
8626 GLT_GetFromList( int index, char *name )\r
8627 {\r
8628     if( name != 0 ) {\r
8629             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8630                 return TRUE;\r
8631     }\r
8632     return FALSE;\r
8633 }\r
8634 \r
8635 void GLT_MoveSelection( HWND hDlg, int delta )\r
8636 {\r
8637     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8638     int idx2 = idx1 + delta;\r
8639     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8640 \r
8641     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8642         char buf[128];\r
8643 \r
8644         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8645         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8646         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8647         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8648     }\r
8649 }\r
8650 \r
8651 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8652 {\r
8653     switch( message )\r
8654     {\r
8655     case WM_INITDIALOG:\r
8656         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8657         \r
8658         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8659         Translate(hDlg, DLG_GameListOptions);\r
8660 \r
8661         /* Initialize list */\r
8662         GLT_TagsToList( lpUserGLT );\r
8663 \r
8664         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8665 \r
8666         break;\r
8667 \r
8668     case WM_COMMAND:\r
8669         switch( LOWORD(wParam) ) {\r
8670         case IDOK:\r
8671             GLT_ParseList();\r
8672             EndDialog( hDlg, 0 );\r
8673             return TRUE;\r
8674         case IDCANCEL:\r
8675             EndDialog( hDlg, 1 );\r
8676             return TRUE;\r
8677 \r
8678         case IDC_GLT_Default:\r
8679             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8680             return TRUE;\r
8681 \r
8682         case IDC_GLT_Restore:\r
8683             GLT_TagsToList( appData.gameListTags );\r
8684             return TRUE;\r
8685 \r
8686         case IDC_GLT_Up:\r
8687             GLT_MoveSelection( hDlg, -1 );\r
8688             return TRUE;\r
8689 \r
8690         case IDC_GLT_Down:\r
8691             GLT_MoveSelection( hDlg, +1 );\r
8692             return TRUE;\r
8693         }\r
8694 \r
8695         break;\r
8696     }\r
8697 \r
8698     return FALSE;\r
8699 }\r
8700 \r
8701 int GameListOptions()\r
8702 {\r
8703     int result;\r
8704     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8705 \r
8706       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8707 \r
8708     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8709 \r
8710     if( result == 0 ) {\r
8711         /* [AS] Memory leak here! */\r
8712         appData.gameListTags = strdup( lpUserGLT ); \r
8713     }\r
8714 \r
8715     return result;\r
8716 }\r
8717 \r
8718 VOID\r
8719 DisplayIcsInteractionTitle(char *str)\r
8720 {\r
8721   char consoleTitle[MSG_SIZ];\r
8722 \r
8723     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8724     SetWindowText(hwndConsole, consoleTitle);\r
8725 \r
8726     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8727       char buf[MSG_SIZ], *p = buf, *q;\r
8728         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8729       do {\r
8730         q = strchr(p, ';');\r
8731         if(q) *q++ = 0;\r
8732         if(*p) ChatPopUp(p);\r
8733       } while(p=q);\r
8734     }\r
8735 \r
8736     SetActiveWindow(hwndMain);\r
8737 }\r
8738 \r
8739 void\r
8740 DrawPosition(int fullRedraw, Board board)\r
8741 {\r
8742   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8743 }\r
8744 \r
8745 void NotifyFrontendLogin()\r
8746 {\r
8747         if (hwndConsole)\r
8748                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8749 }\r
8750 \r
8751 VOID\r
8752 ResetFrontEnd()\r
8753 {\r
8754   fromX = fromY = -1;\r
8755   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8756     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8757     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8758     dragInfo.lastpos = dragInfo.pos;\r
8759     dragInfo.start.x = dragInfo.start.y = -1;\r
8760     dragInfo.from = dragInfo.start;\r
8761     ReleaseCapture();\r
8762     DrawPosition(TRUE, NULL);\r
8763   }\r
8764   TagsPopDown();\r
8765 }\r
8766 \r
8767 \r
8768 VOID\r
8769 CommentPopUp(char *title, char *str)\r
8770 {\r
8771   HWND hwnd = GetActiveWindow();\r
8772   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8773   SAY(str);\r
8774   SetActiveWindow(hwnd);\r
8775 }\r
8776 \r
8777 VOID\r
8778 CommentPopDown(void)\r
8779 {\r
8780   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8781   if (commentDialog) {\r
8782     ShowWindow(commentDialog, SW_HIDE);\r
8783   }\r
8784   commentUp = FALSE;\r
8785 }\r
8786 \r
8787 VOID\r
8788 EditCommentPopUp(int index, char *title, char *str)\r
8789 {\r
8790   EitherCommentPopUp(index, title, str, TRUE);\r
8791 }\r
8792 \r
8793 \r
8794 int\r
8795 Roar()\r
8796 {\r
8797   MyPlaySound(&sounds[(int)SoundRoar]);\r
8798   return 1;\r
8799 }\r
8800 \r
8801 VOID\r
8802 RingBell()\r
8803 {\r
8804   MyPlaySound(&sounds[(int)SoundMove]);\r
8805 }\r
8806 \r
8807 VOID PlayIcsWinSound()\r
8808 {\r
8809   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8810 }\r
8811 \r
8812 VOID PlayIcsLossSound()\r
8813 {\r
8814   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8815 }\r
8816 \r
8817 VOID PlayIcsDrawSound()\r
8818 {\r
8819   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8820 }\r
8821 \r
8822 VOID PlayIcsUnfinishedSound()\r
8823 {\r
8824   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8825 }\r
8826 \r
8827 VOID\r
8828 PlayAlarmSound()\r
8829 {\r
8830   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8831 }\r
8832 \r
8833 VOID\r
8834 PlayTellSound()\r
8835 {\r
8836   MyPlaySound(&textAttribs[ColorTell].sound);\r
8837 }\r
8838 \r
8839 \r
8840 VOID\r
8841 EchoOn()\r
8842 {\r
8843   HWND hInput;\r
8844   consoleEcho = TRUE;\r
8845   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8846   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8847   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8848 }\r
8849 \r
8850 \r
8851 VOID\r
8852 EchoOff()\r
8853 {\r
8854   CHARFORMAT cf;\r
8855   HWND hInput;\r
8856   consoleEcho = FALSE;\r
8857   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8858   /* This works OK: set text and background both to the same color */\r
8859   cf = consoleCF;\r
8860   cf.crTextColor = COLOR_ECHOOFF;\r
8861   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8862   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8863 }\r
8864 \r
8865 /* No Raw()...? */\r
8866 \r
8867 void Colorize(ColorClass cc, int continuation)\r
8868 {\r
8869   currentColorClass = cc;\r
8870   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8871   consoleCF.crTextColor = textAttribs[cc].color;\r
8872   consoleCF.dwEffects = textAttribs[cc].effects;\r
8873   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8874 }\r
8875 \r
8876 char *\r
8877 UserName()\r
8878 {\r
8879   static char buf[MSG_SIZ];\r
8880   DWORD bufsiz = MSG_SIZ;\r
8881 \r
8882   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8883         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8884   }\r
8885   if (!GetUserName(buf, &bufsiz)) {\r
8886     /*DisplayError("Error getting user name", GetLastError());*/\r
8887     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8888   }\r
8889   return buf;\r
8890 }\r
8891 \r
8892 char *\r
8893 HostName()\r
8894 {\r
8895   static char buf[MSG_SIZ];\r
8896   DWORD bufsiz = MSG_SIZ;\r
8897 \r
8898   if (!GetComputerName(buf, &bufsiz)) {\r
8899     /*DisplayError("Error getting host name", GetLastError());*/\r
8900     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8901   }\r
8902   return buf;\r
8903 }\r
8904 \r
8905 \r
8906 int\r
8907 ClockTimerRunning()\r
8908 {\r
8909   return clockTimerEvent != 0;\r
8910 }\r
8911 \r
8912 int\r
8913 StopClockTimer()\r
8914 {\r
8915   if (clockTimerEvent == 0) return FALSE;\r
8916   KillTimer(hwndMain, clockTimerEvent);\r
8917   clockTimerEvent = 0;\r
8918   return TRUE;\r
8919 }\r
8920 \r
8921 void\r
8922 StartClockTimer(long millisec)\r
8923 {\r
8924   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8925                              (UINT) millisec, NULL);\r
8926 }\r
8927 \r
8928 void\r
8929 DisplayWhiteClock(long timeRemaining, int highlight)\r
8930 {\r
8931   HDC hdc;\r
8932   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8933 \r
8934   if(appData.noGUI) return;\r
8935   hdc = GetDC(hwndMain);\r
8936   if (!IsIconic(hwndMain)) {\r
8937     DisplayAClock(hdc, timeRemaining, highlight, \r
8938                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8939   }\r
8940   if (highlight && iconCurrent == iconBlack) {\r
8941     iconCurrent = iconWhite;\r
8942     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8943     if (IsIconic(hwndMain)) {\r
8944       DrawIcon(hdc, 2, 2, iconCurrent);\r
8945     }\r
8946   }\r
8947   (void) ReleaseDC(hwndMain, hdc);\r
8948   if (hwndConsole)\r
8949     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8950 }\r
8951 \r
8952 void\r
8953 DisplayBlackClock(long timeRemaining, int highlight)\r
8954 {\r
8955   HDC hdc;\r
8956   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8957 \r
8958 \r
8959   if(appData.noGUI) return;\r
8960   hdc = GetDC(hwndMain);\r
8961   if (!IsIconic(hwndMain)) {\r
8962     DisplayAClock(hdc, timeRemaining, highlight, \r
8963                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8964   }\r
8965   if (highlight && iconCurrent == iconWhite) {\r
8966     iconCurrent = iconBlack;\r
8967     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8968     if (IsIconic(hwndMain)) {\r
8969       DrawIcon(hdc, 2, 2, iconCurrent);\r
8970     }\r
8971   }\r
8972   (void) ReleaseDC(hwndMain, hdc);\r
8973   if (hwndConsole)\r
8974     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8975 }\r
8976 \r
8977 \r
8978 int\r
8979 LoadGameTimerRunning()\r
8980 {\r
8981   return loadGameTimerEvent != 0;\r
8982 }\r
8983 \r
8984 int\r
8985 StopLoadGameTimer()\r
8986 {\r
8987   if (loadGameTimerEvent == 0) return FALSE;\r
8988   KillTimer(hwndMain, loadGameTimerEvent);\r
8989   loadGameTimerEvent = 0;\r
8990   return TRUE;\r
8991 }\r
8992 \r
8993 void\r
8994 StartLoadGameTimer(long millisec)\r
8995 {\r
8996   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8997                                 (UINT) millisec, NULL);\r
8998 }\r
8999 \r
9000 void\r
9001 AutoSaveGame()\r
9002 {\r
9003   char *defName;\r
9004   FILE *f;\r
9005   char fileTitle[MSG_SIZ];\r
9006 \r
9007   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9008   f = OpenFileDialog(hwndMain, "a", defName,\r
9009                      appData.oldSaveStyle ? "gam" : "pgn",\r
9010                      GAME_FILT, \r
9011                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9012   if (f != NULL) {\r
9013     SaveGame(f, 0, "");\r
9014     fclose(f);\r
9015   }\r
9016 }\r
9017 \r
9018 \r
9019 void\r
9020 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9021 {\r
9022   if (delayedTimerEvent != 0) {\r
9023     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9024       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9025     }\r
9026     KillTimer(hwndMain, delayedTimerEvent);\r
9027     delayedTimerEvent = 0;\r
9028     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9029     delayedTimerCallback();\r
9030   }\r
9031   delayedTimerCallback = cb;\r
9032   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9033                                 (UINT) millisec, NULL);\r
9034 }\r
9035 \r
9036 DelayedEventCallback\r
9037 GetDelayedEvent()\r
9038 {\r
9039   if (delayedTimerEvent) {\r
9040     return delayedTimerCallback;\r
9041   } else {\r
9042     return NULL;\r
9043   }\r
9044 }\r
9045 \r
9046 void\r
9047 CancelDelayedEvent()\r
9048 {\r
9049   if (delayedTimerEvent) {\r
9050     KillTimer(hwndMain, delayedTimerEvent);\r
9051     delayedTimerEvent = 0;\r
9052   }\r
9053 }\r
9054 \r
9055 DWORD GetWin32Priority(int nice)\r
9056 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9057 /*\r
9058 REALTIME_PRIORITY_CLASS     0x00000100\r
9059 HIGH_PRIORITY_CLASS         0x00000080\r
9060 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9061 NORMAL_PRIORITY_CLASS       0x00000020\r
9062 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9063 IDLE_PRIORITY_CLASS         0x00000040\r
9064 */\r
9065         if (nice < -15) return 0x00000080;\r
9066         if (nice < 0)   return 0x00008000;\r
9067         if (nice == 0)  return 0x00000020;\r
9068         if (nice < 15)  return 0x00004000;\r
9069         return 0x00000040;\r
9070 }\r
9071 \r
9072 void RunCommand(char *cmdLine)\r
9073 {\r
9074   /* Now create the child process. */\r
9075   STARTUPINFO siStartInfo;\r
9076   PROCESS_INFORMATION piProcInfo;\r
9077 \r
9078   siStartInfo.cb = sizeof(STARTUPINFO);\r
9079   siStartInfo.lpReserved = NULL;\r
9080   siStartInfo.lpDesktop = NULL;\r
9081   siStartInfo.lpTitle = NULL;\r
9082   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9083   siStartInfo.cbReserved2 = 0;\r
9084   siStartInfo.lpReserved2 = NULL;\r
9085   siStartInfo.hStdInput = NULL;\r
9086   siStartInfo.hStdOutput = NULL;\r
9087   siStartInfo.hStdError = NULL;\r
9088 \r
9089   CreateProcess(NULL,\r
9090                 cmdLine,           /* command line */\r
9091                 NULL,      /* process security attributes */\r
9092                 NULL,      /* primary thread security attrs */\r
9093                 TRUE,      /* handles are inherited */\r
9094                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9095                 NULL,      /* use parent's environment */\r
9096                 NULL,\r
9097                 &siStartInfo, /* STARTUPINFO pointer */\r
9098                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9099 \r
9100   CloseHandle(piProcInfo.hThread);\r
9101 }\r
9102 \r
9103 /* Start a child process running the given program.\r
9104    The process's standard output can be read from "from", and its\r
9105    standard input can be written to "to".\r
9106    Exit with fatal error if anything goes wrong.\r
9107    Returns an opaque pointer that can be used to destroy the process\r
9108    later.\r
9109 */\r
9110 int\r
9111 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9112 {\r
9113 #define BUFSIZE 4096\r
9114 \r
9115   HANDLE hChildStdinRd, hChildStdinWr,\r
9116     hChildStdoutRd, hChildStdoutWr;\r
9117   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9118   SECURITY_ATTRIBUTES saAttr;\r
9119   BOOL fSuccess;\r
9120   PROCESS_INFORMATION piProcInfo;\r
9121   STARTUPINFO siStartInfo;\r
9122   ChildProc *cp;\r
9123   char buf[MSG_SIZ];\r
9124   DWORD err;\r
9125 \r
9126   if (appData.debugMode) {\r
9127     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9128   }\r
9129 \r
9130   *pr = NoProc;\r
9131 \r
9132   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9133   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9134   saAttr.bInheritHandle = TRUE;\r
9135   saAttr.lpSecurityDescriptor = NULL;\r
9136 \r
9137   /*\r
9138    * The steps for redirecting child's STDOUT:\r
9139    *     1. Create anonymous pipe to be STDOUT for child.\r
9140    *     2. Create a noninheritable duplicate of read handle,\r
9141    *         and close the inheritable read handle.\r
9142    */\r
9143 \r
9144   /* Create a pipe for the child's STDOUT. */\r
9145   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9146     return GetLastError();\r
9147   }\r
9148 \r
9149   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9150   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9151                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9152                              FALSE,     /* not inherited */\r
9153                              DUPLICATE_SAME_ACCESS);\r
9154   if (! fSuccess) {\r
9155     return GetLastError();\r
9156   }\r
9157   CloseHandle(hChildStdoutRd);\r
9158 \r
9159   /*\r
9160    * The steps for redirecting child's STDIN:\r
9161    *     1. Create anonymous pipe to be STDIN for child.\r
9162    *     2. Create a noninheritable duplicate of write handle,\r
9163    *         and close the inheritable write handle.\r
9164    */\r
9165 \r
9166   /* Create a pipe for the child's STDIN. */\r
9167   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9168     return GetLastError();\r
9169   }\r
9170 \r
9171   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9172   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9173                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9174                              FALSE,     /* not inherited */\r
9175                              DUPLICATE_SAME_ACCESS);\r
9176   if (! fSuccess) {\r
9177     return GetLastError();\r
9178   }\r
9179   CloseHandle(hChildStdinWr);\r
9180 \r
9181   /* Arrange to (1) look in dir for the child .exe file, and\r
9182    * (2) have dir be the child's working directory.  Interpret\r
9183    * dir relative to the directory WinBoard loaded from. */\r
9184   GetCurrentDirectory(MSG_SIZ, buf);\r
9185   SetCurrentDirectory(installDir);\r
9186   SetCurrentDirectory(dir);\r
9187 \r
9188   /* Now create the child process. */\r
9189 \r
9190   siStartInfo.cb = sizeof(STARTUPINFO);\r
9191   siStartInfo.lpReserved = NULL;\r
9192   siStartInfo.lpDesktop = NULL;\r
9193   siStartInfo.lpTitle = NULL;\r
9194   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9195   siStartInfo.cbReserved2 = 0;\r
9196   siStartInfo.lpReserved2 = NULL;\r
9197   siStartInfo.hStdInput = hChildStdinRd;\r
9198   siStartInfo.hStdOutput = hChildStdoutWr;\r
9199   siStartInfo.hStdError = hChildStdoutWr;\r
9200 \r
9201   fSuccess = CreateProcess(NULL,\r
9202                            cmdLine,        /* command line */\r
9203                            NULL,           /* process security attributes */\r
9204                            NULL,           /* primary thread security attrs */\r
9205                            TRUE,           /* handles are inherited */\r
9206                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9207                            NULL,           /* use parent's environment */\r
9208                            NULL,\r
9209                            &siStartInfo, /* STARTUPINFO pointer */\r
9210                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9211 \r
9212   err = GetLastError();\r
9213   SetCurrentDirectory(buf); /* return to prev directory */\r
9214   if (! fSuccess) {\r
9215     return err;\r
9216   }\r
9217 \r
9218   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9219     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9220     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9221   }\r
9222 \r
9223   /* Close the handles we don't need in the parent */\r
9224   CloseHandle(piProcInfo.hThread);\r
9225   CloseHandle(hChildStdinRd);\r
9226   CloseHandle(hChildStdoutWr);\r
9227 \r
9228   /* Prepare return value */\r
9229   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9230   cp->kind = CPReal;\r
9231   cp->hProcess = piProcInfo.hProcess;\r
9232   cp->pid = piProcInfo.dwProcessId;\r
9233   cp->hFrom = hChildStdoutRdDup;\r
9234   cp->hTo = hChildStdinWrDup;\r
9235 \r
9236   *pr = (void *) cp;\r
9237 \r
9238   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9239      2000 where engines sometimes don't see the initial command(s)\r
9240      from WinBoard and hang.  I don't understand how that can happen,\r
9241      but the Sleep is harmless, so I've put it in.  Others have also\r
9242      reported what may be the same problem, so hopefully this will fix\r
9243      it for them too.  */\r
9244   Sleep(500);\r
9245 \r
9246   return NO_ERROR;\r
9247 }\r
9248 \r
9249 \r
9250 void\r
9251 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9252 {\r
9253   ChildProc *cp; int result;\r
9254 \r
9255   cp = (ChildProc *) pr;\r
9256   if (cp == NULL) return;\r
9257 \r
9258   switch (cp->kind) {\r
9259   case CPReal:\r
9260     /* TerminateProcess is considered harmful, so... */\r
9261     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9262     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9263     /* The following doesn't work because the chess program\r
9264        doesn't "have the same console" as WinBoard.  Maybe\r
9265        we could arrange for this even though neither WinBoard\r
9266        nor the chess program uses a console for stdio? */\r
9267     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9268 \r
9269     /* [AS] Special termination modes for misbehaving programs... */\r
9270     if( signal & 8 ) { \r
9271         result = TerminateProcess( cp->hProcess, 0 );\r
9272 \r
9273         if ( appData.debugMode) {\r
9274             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9275         }\r
9276     }\r
9277     else if( signal & 4 ) {\r
9278         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9279 \r
9280         if( dw != WAIT_OBJECT_0 ) {\r
9281             result = TerminateProcess( cp->hProcess, 0 );\r
9282 \r
9283             if ( appData.debugMode) {\r
9284                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9285             }\r
9286 \r
9287         }\r
9288     }\r
9289 \r
9290     CloseHandle(cp->hProcess);\r
9291     break;\r
9292 \r
9293   case CPComm:\r
9294     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9295     break;\r
9296 \r
9297   case CPSock:\r
9298     closesocket(cp->sock);\r
9299     WSACleanup();\r
9300     break;\r
9301 \r
9302   case CPRcmd:\r
9303     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9304     closesocket(cp->sock);\r
9305     closesocket(cp->sock2);\r
9306     WSACleanup();\r
9307     break;\r
9308   }\r
9309   free(cp);\r
9310 }\r
9311 \r
9312 void\r
9313 InterruptChildProcess(ProcRef pr)\r
9314 {\r
9315   ChildProc *cp;\r
9316 \r
9317   cp = (ChildProc *) pr;\r
9318   if (cp == NULL) return;\r
9319   switch (cp->kind) {\r
9320   case CPReal:\r
9321     /* The following doesn't work because the chess program\r
9322        doesn't "have the same console" as WinBoard.  Maybe\r
9323        we could arrange for this even though neither WinBoard\r
9324        nor the chess program uses a console for stdio */\r
9325     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9326     break;\r
9327 \r
9328   case CPComm:\r
9329   case CPSock:\r
9330     /* Can't interrupt */\r
9331     break;\r
9332 \r
9333   case CPRcmd:\r
9334     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9335     break;\r
9336   }\r
9337 }\r
9338 \r
9339 \r
9340 int\r
9341 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9342 {\r
9343   char cmdLine[MSG_SIZ];\r
9344 \r
9345   if (port[0] == NULLCHAR) {\r
9346     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9347   } else {\r
9348     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9349   }\r
9350   return StartChildProcess(cmdLine, "", pr);\r
9351 }\r
9352 \r
9353 \r
9354 /* Code to open TCP sockets */\r
9355 \r
9356 int\r
9357 OpenTCP(char *host, char *port, ProcRef *pr)\r
9358 {\r
9359   ChildProc *cp;\r
9360   int err;\r
9361   SOCKET s;\r
9362 \r
9363   struct sockaddr_in sa, mysa;\r
9364   struct hostent FAR *hp;\r
9365   unsigned short uport;\r
9366   WORD wVersionRequested;\r
9367   WSADATA wsaData;\r
9368 \r
9369   /* Initialize socket DLL */\r
9370   wVersionRequested = MAKEWORD(1, 1);\r
9371   err = WSAStartup(wVersionRequested, &wsaData);\r
9372   if (err != 0) return err;\r
9373 \r
9374   /* Make socket */\r
9375   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9376     err = WSAGetLastError();\r
9377     WSACleanup();\r
9378     return err;\r
9379   }\r
9380 \r
9381   /* Bind local address using (mostly) don't-care values.\r
9382    */\r
9383   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9384   mysa.sin_family = AF_INET;\r
9385   mysa.sin_addr.s_addr = INADDR_ANY;\r
9386   uport = (unsigned short) 0;\r
9387   mysa.sin_port = htons(uport);\r
9388   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9389       == SOCKET_ERROR) {\r
9390     err = WSAGetLastError();\r
9391     WSACleanup();\r
9392     return err;\r
9393   }\r
9394 \r
9395   /* Resolve remote host name */\r
9396   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9397   if (!(hp = gethostbyname(host))) {\r
9398     unsigned int b0, b1, b2, b3;\r
9399 \r
9400     err = WSAGetLastError();\r
9401 \r
9402     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9403       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9404       hp->h_addrtype = AF_INET;\r
9405       hp->h_length = 4;\r
9406       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9407       hp->h_addr_list[0] = (char *) malloc(4);\r
9408       hp->h_addr_list[0][0] = (char) b0;\r
9409       hp->h_addr_list[0][1] = (char) b1;\r
9410       hp->h_addr_list[0][2] = (char) b2;\r
9411       hp->h_addr_list[0][3] = (char) b3;\r
9412     } else {\r
9413       WSACleanup();\r
9414       return err;\r
9415     }\r
9416   }\r
9417   sa.sin_family = hp->h_addrtype;\r
9418   uport = (unsigned short) atoi(port);\r
9419   sa.sin_port = htons(uport);\r
9420   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9421 \r
9422   /* Make connection */\r
9423   if (connect(s, (struct sockaddr *) &sa,\r
9424               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9425     err = WSAGetLastError();\r
9426     WSACleanup();\r
9427     return err;\r
9428   }\r
9429 \r
9430   /* Prepare return value */\r
9431   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9432   cp->kind = CPSock;\r
9433   cp->sock = s;\r
9434   *pr = (ProcRef *) cp;\r
9435 \r
9436   return NO_ERROR;\r
9437 }\r
9438 \r
9439 int\r
9440 OpenCommPort(char *name, ProcRef *pr)\r
9441 {\r
9442   HANDLE h;\r
9443   COMMTIMEOUTS ct;\r
9444   ChildProc *cp;\r
9445   char fullname[MSG_SIZ];\r
9446 \r
9447   if (*name != '\\')\r
9448     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9449   else\r
9450     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9451 \r
9452   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9453                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9454   if (h == (HANDLE) -1) {\r
9455     return GetLastError();\r
9456   }\r
9457   hCommPort = h;\r
9458 \r
9459   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9460 \r
9461   /* Accumulate characters until a 100ms pause, then parse */\r
9462   ct.ReadIntervalTimeout = 100;\r
9463   ct.ReadTotalTimeoutMultiplier = 0;\r
9464   ct.ReadTotalTimeoutConstant = 0;\r
9465   ct.WriteTotalTimeoutMultiplier = 0;\r
9466   ct.WriteTotalTimeoutConstant = 0;\r
9467   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9468 \r
9469   /* Prepare return value */\r
9470   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9471   cp->kind = CPComm;\r
9472   cp->hFrom = h;\r
9473   cp->hTo = h;\r
9474   *pr = (ProcRef *) cp;\r
9475 \r
9476   return NO_ERROR;\r
9477 }\r
9478 \r
9479 int\r
9480 OpenLoopback(ProcRef *pr)\r
9481 {\r
9482   DisplayFatalError(_("Not implemented"), 0, 1);\r
9483   return NO_ERROR;\r
9484 }\r
9485 \r
9486 \r
9487 int\r
9488 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9489 {\r
9490   ChildProc *cp;\r
9491   int err;\r
9492   SOCKET s, s2, s3;\r
9493   struct sockaddr_in sa, mysa;\r
9494   struct hostent FAR *hp;\r
9495   unsigned short uport;\r
9496   WORD wVersionRequested;\r
9497   WSADATA wsaData;\r
9498   int fromPort;\r
9499   char stderrPortStr[MSG_SIZ];\r
9500 \r
9501   /* Initialize socket DLL */\r
9502   wVersionRequested = MAKEWORD(1, 1);\r
9503   err = WSAStartup(wVersionRequested, &wsaData);\r
9504   if (err != 0) return err;\r
9505 \r
9506   /* Resolve remote host name */\r
9507   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9508   if (!(hp = gethostbyname(host))) {\r
9509     unsigned int b0, b1, b2, b3;\r
9510 \r
9511     err = WSAGetLastError();\r
9512 \r
9513     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9514       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9515       hp->h_addrtype = AF_INET;\r
9516       hp->h_length = 4;\r
9517       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9518       hp->h_addr_list[0] = (char *) malloc(4);\r
9519       hp->h_addr_list[0][0] = (char) b0;\r
9520       hp->h_addr_list[0][1] = (char) b1;\r
9521       hp->h_addr_list[0][2] = (char) b2;\r
9522       hp->h_addr_list[0][3] = (char) b3;\r
9523     } else {\r
9524       WSACleanup();\r
9525       return err;\r
9526     }\r
9527   }\r
9528   sa.sin_family = hp->h_addrtype;\r
9529   uport = (unsigned short) 514;\r
9530   sa.sin_port = htons(uport);\r
9531   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9532 \r
9533   /* Bind local socket to unused "privileged" port address\r
9534    */\r
9535   s = INVALID_SOCKET;\r
9536   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9537   mysa.sin_family = AF_INET;\r
9538   mysa.sin_addr.s_addr = INADDR_ANY;\r
9539   for (fromPort = 1023;; fromPort--) {\r
9540     if (fromPort < 0) {\r
9541       WSACleanup();\r
9542       return WSAEADDRINUSE;\r
9543     }\r
9544     if (s == INVALID_SOCKET) {\r
9545       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9546         err = WSAGetLastError();\r
9547         WSACleanup();\r
9548         return err;\r
9549       }\r
9550     }\r
9551     uport = (unsigned short) fromPort;\r
9552     mysa.sin_port = htons(uport);\r
9553     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9554         == SOCKET_ERROR) {\r
9555       err = WSAGetLastError();\r
9556       if (err == WSAEADDRINUSE) continue;\r
9557       WSACleanup();\r
9558       return err;\r
9559     }\r
9560     if (connect(s, (struct sockaddr *) &sa,\r
9561       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9562       err = WSAGetLastError();\r
9563       if (err == WSAEADDRINUSE) {\r
9564         closesocket(s);\r
9565         s = -1;\r
9566         continue;\r
9567       }\r
9568       WSACleanup();\r
9569       return err;\r
9570     }\r
9571     break;\r
9572   }\r
9573 \r
9574   /* Bind stderr local socket to unused "privileged" port address\r
9575    */\r
9576   s2 = INVALID_SOCKET;\r
9577   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9578   mysa.sin_family = AF_INET;\r
9579   mysa.sin_addr.s_addr = INADDR_ANY;\r
9580   for (fromPort = 1023;; fromPort--) {\r
9581     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9582     if (fromPort < 0) {\r
9583       (void) closesocket(s);\r
9584       WSACleanup();\r
9585       return WSAEADDRINUSE;\r
9586     }\r
9587     if (s2 == INVALID_SOCKET) {\r
9588       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9589         err = WSAGetLastError();\r
9590         closesocket(s);\r
9591         WSACleanup();\r
9592         return err;\r
9593       }\r
9594     }\r
9595     uport = (unsigned short) fromPort;\r
9596     mysa.sin_port = htons(uport);\r
9597     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9598         == SOCKET_ERROR) {\r
9599       err = WSAGetLastError();\r
9600       if (err == WSAEADDRINUSE) continue;\r
9601       (void) closesocket(s);\r
9602       WSACleanup();\r
9603       return err;\r
9604     }\r
9605     if (listen(s2, 1) == SOCKET_ERROR) {\r
9606       err = WSAGetLastError();\r
9607       if (err == WSAEADDRINUSE) {\r
9608         closesocket(s2);\r
9609         s2 = INVALID_SOCKET;\r
9610         continue;\r
9611       }\r
9612       (void) closesocket(s);\r
9613       (void) closesocket(s2);\r
9614       WSACleanup();\r
9615       return err;\r
9616     }\r
9617     break;\r
9618   }\r
9619   prevStderrPort = fromPort; // remember port used\r
9620   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9621 \r
9622   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9623     err = WSAGetLastError();\r
9624     (void) closesocket(s);\r
9625     (void) closesocket(s2);\r
9626     WSACleanup();\r
9627     return err;\r
9628   }\r
9629 \r
9630   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9631     err = WSAGetLastError();\r
9632     (void) closesocket(s);\r
9633     (void) closesocket(s2);\r
9634     WSACleanup();\r
9635     return err;\r
9636   }\r
9637   if (*user == NULLCHAR) user = UserName();\r
9638   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9639     err = WSAGetLastError();\r
9640     (void) closesocket(s);\r
9641     (void) closesocket(s2);\r
9642     WSACleanup();\r
9643     return err;\r
9644   }\r
9645   if (send(s, cmd, strlen(cmd) + 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 \r
9653   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9654     err = WSAGetLastError();\r
9655     (void) closesocket(s);\r
9656     (void) closesocket(s2);\r
9657     WSACleanup();\r
9658     return err;\r
9659   }\r
9660   (void) closesocket(s2);  /* Stop listening */\r
9661 \r
9662   /* Prepare return value */\r
9663   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9664   cp->kind = CPRcmd;\r
9665   cp->sock = s;\r
9666   cp->sock2 = s3;\r
9667   *pr = (ProcRef *) cp;\r
9668 \r
9669   return NO_ERROR;\r
9670 }\r
9671 \r
9672 \r
9673 InputSourceRef\r
9674 AddInputSource(ProcRef pr, int lineByLine,\r
9675                InputCallback func, VOIDSTAR closure)\r
9676 {\r
9677   InputSource *is, *is2 = NULL;\r
9678   ChildProc *cp = (ChildProc *) pr;\r
9679 \r
9680   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9681   is->lineByLine = lineByLine;\r
9682   is->func = func;\r
9683   is->closure = closure;\r
9684   is->second = NULL;\r
9685   is->next = is->buf;\r
9686   if (pr == NoProc) {\r
9687     is->kind = CPReal;\r
9688     consoleInputSource = is;\r
9689   } else {\r
9690     is->kind = cp->kind;\r
9691     /* \r
9692         [AS] Try to avoid a race condition if the thread is given control too early:\r
9693         we create all threads suspended so that the is->hThread variable can be\r
9694         safely assigned, then let the threads start with ResumeThread.\r
9695     */\r
9696     switch (cp->kind) {\r
9697     case CPReal:\r
9698       is->hFile = cp->hFrom;\r
9699       cp->hFrom = NULL; /* now owned by InputThread */\r
9700       is->hThread =\r
9701         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9702                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9703       break;\r
9704 \r
9705     case CPComm:\r
9706       is->hFile = cp->hFrom;\r
9707       cp->hFrom = NULL; /* now owned by InputThread */\r
9708       is->hThread =\r
9709         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9710                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9711       break;\r
9712 \r
9713     case CPSock:\r
9714       is->sock = cp->sock;\r
9715       is->hThread =\r
9716         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9717                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9718       break;\r
9719 \r
9720     case CPRcmd:\r
9721       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9722       *is2 = *is;\r
9723       is->sock = cp->sock;\r
9724       is->second = is2;\r
9725       is2->sock = cp->sock2;\r
9726       is2->second = is2;\r
9727       is->hThread =\r
9728         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9729                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9730       is2->hThread =\r
9731         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9732                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9733       break;\r
9734     }\r
9735 \r
9736     if( is->hThread != NULL ) {\r
9737         ResumeThread( is->hThread );\r
9738     }\r
9739 \r
9740     if( is2 != NULL && is2->hThread != NULL ) {\r
9741         ResumeThread( is2->hThread );\r
9742     }\r
9743   }\r
9744 \r
9745   return (InputSourceRef) is;\r
9746 }\r
9747 \r
9748 void\r
9749 RemoveInputSource(InputSourceRef isr)\r
9750 {\r
9751   InputSource *is;\r
9752 \r
9753   is = (InputSource *) isr;\r
9754   is->hThread = NULL;  /* tell thread to stop */\r
9755   CloseHandle(is->hThread);\r
9756   if (is->second != NULL) {\r
9757     is->second->hThread = NULL;\r
9758     CloseHandle(is->second->hThread);\r
9759   }\r
9760 }\r
9761 \r
9762 int no_wrap(char *message, int count)\r
9763 {\r
9764     ConsoleOutput(message, count, FALSE);\r
9765     return count;\r
9766 }\r
9767 \r
9768 int\r
9769 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9770 {\r
9771   DWORD dOutCount;\r
9772   int outCount = SOCKET_ERROR;\r
9773   ChildProc *cp = (ChildProc *) pr;\r
9774   static OVERLAPPED ovl;\r
9775   static int line = 0;\r
9776 \r
9777   if (pr == NoProc)\r
9778   {\r
9779     if (appData.noJoin || !appData.useInternalWrap)\r
9780       return no_wrap(message, count);\r
9781     else\r
9782     {\r
9783       int width = get_term_width();\r
9784       int len = wrap(NULL, message, count, width, &line);\r
9785       char *msg = malloc(len);\r
9786       int dbgchk;\r
9787 \r
9788       if (!msg)\r
9789         return no_wrap(message, count);\r
9790       else\r
9791       {\r
9792         dbgchk = wrap(msg, message, count, width, &line);\r
9793         if (dbgchk != len && appData.debugMode)\r
9794             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9795         ConsoleOutput(msg, len, FALSE);\r
9796         free(msg);\r
9797         return len;\r
9798       }\r
9799     }\r
9800   }\r
9801 \r
9802   if (ovl.hEvent == NULL) {\r
9803     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9804   }\r
9805   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9806 \r
9807   switch (cp->kind) {\r
9808   case CPSock:\r
9809   case CPRcmd:\r
9810     outCount = send(cp->sock, message, count, 0);\r
9811     if (outCount == SOCKET_ERROR) {\r
9812       *outError = WSAGetLastError();\r
9813     } else {\r
9814       *outError = NO_ERROR;\r
9815     }\r
9816     break;\r
9817 \r
9818   case CPReal:\r
9819     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9820                   &dOutCount, NULL)) {\r
9821       *outError = NO_ERROR;\r
9822       outCount = (int) dOutCount;\r
9823     } else {\r
9824       *outError = GetLastError();\r
9825     }\r
9826     break;\r
9827 \r
9828   case CPComm:\r
9829     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9830                             &dOutCount, &ovl);\r
9831     if (*outError == NO_ERROR) {\r
9832       outCount = (int) dOutCount;\r
9833     }\r
9834     break;\r
9835   }\r
9836   return outCount;\r
9837 }\r
9838 \r
9839 void\r
9840 DoSleep(int n)\r
9841 {\r
9842     if(n != 0) Sleep(n);\r
9843 }\r
9844 \r
9845 int\r
9846 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9847                        long msdelay)\r
9848 {\r
9849   /* Ignore delay, not implemented for WinBoard */\r
9850   return OutputToProcess(pr, message, count, outError);\r
9851 }\r
9852 \r
9853 \r
9854 void\r
9855 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9856                         char *buf, int count, int error)\r
9857 {\r
9858   DisplayFatalError(_("Not implemented"), 0, 1);\r
9859 }\r
9860 \r
9861 /* see wgamelist.c for Game List functions */\r
9862 /* see wedittags.c for Edit Tags functions */\r
9863 \r
9864 \r
9865 int\r
9866 ICSInitScript()\r
9867 {\r
9868   FILE *f;\r
9869   char buf[MSG_SIZ];\r
9870   char *dummy;\r
9871 \r
9872   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9873     f = fopen(buf, "r");\r
9874     if (f != NULL) {\r
9875       ProcessICSInitScript(f);\r
9876       fclose(f);\r
9877       return TRUE;\r
9878     }\r
9879   }\r
9880   return FALSE;\r
9881 }\r
9882 \r
9883 \r
9884 VOID\r
9885 StartAnalysisClock()\r
9886 {\r
9887   if (analysisTimerEvent) return;\r
9888   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9889                                         (UINT) 2000, NULL);\r
9890 }\r
9891 \r
9892 VOID\r
9893 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9894 {\r
9895   highlightInfo.sq[0].x = fromX;\r
9896   highlightInfo.sq[0].y = fromY;\r
9897   highlightInfo.sq[1].x = toX;\r
9898   highlightInfo.sq[1].y = toY;\r
9899 }\r
9900 \r
9901 VOID\r
9902 ClearHighlights()\r
9903 {\r
9904   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9905     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9906 }\r
9907 \r
9908 VOID\r
9909 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9910 {\r
9911   premoveHighlightInfo.sq[0].x = fromX;\r
9912   premoveHighlightInfo.sq[0].y = fromY;\r
9913   premoveHighlightInfo.sq[1].x = toX;\r
9914   premoveHighlightInfo.sq[1].y = toY;\r
9915 }\r
9916 \r
9917 VOID\r
9918 ClearPremoveHighlights()\r
9919 {\r
9920   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9921     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9922 }\r
9923 \r
9924 VOID\r
9925 ShutDownFrontEnd()\r
9926 {\r
9927   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9928   DeleteClipboardTempFiles();\r
9929 }\r
9930 \r
9931 void\r
9932 BoardToTop()\r
9933 {\r
9934     if (IsIconic(hwndMain))\r
9935       ShowWindow(hwndMain, SW_RESTORE);\r
9936 \r
9937     SetActiveWindow(hwndMain);\r
9938 }\r
9939 \r
9940 /*\r
9941  * Prototypes for animation support routines\r
9942  */\r
9943 static void ScreenSquare(int column, int row, POINT * pt);\r
9944 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9945      POINT frames[], int * nFrames);\r
9946 \r
9947 \r
9948 #define kFactor 4\r
9949 \r
9950 void\r
9951 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9952 {       // [HGM] atomic: animate blast wave\r
9953         int i;\r
9954 \r
9955         explodeInfo.fromX = fromX;\r
9956         explodeInfo.fromY = fromY;\r
9957         explodeInfo.toX = toX;\r
9958         explodeInfo.toY = toY;\r
9959         for(i=1; i<4*kFactor; i++) {\r
9960             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9961             DrawPosition(FALSE, board);\r
9962             Sleep(appData.animSpeed);\r
9963         }\r
9964         explodeInfo.radius = 0;\r
9965         DrawPosition(TRUE, board);\r
9966 }\r
9967 \r
9968 void\r
9969 AnimateMove(board, fromX, fromY, toX, toY)\r
9970      Board board;\r
9971      int fromX;\r
9972      int fromY;\r
9973      int toX;\r
9974      int toY;\r
9975 {\r
9976   ChessSquare piece;\r
9977   int x = toX, y = toY;\r
9978   POINT start, finish, mid;\r
9979   POINT frames[kFactor * 2 + 1];\r
9980   int nFrames, n;\r
9981 \r
9982   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9983 \r
9984   if (!appData.animate) return;\r
9985   if (doingSizing) return;\r
9986   if (fromY < 0 || fromX < 0) return;\r
9987   piece = board[fromY][fromX];\r
9988   if (piece >= EmptySquare) return;\r
9989 \r
9990   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
9991 \r
9992 again:\r
9993 \r
9994   ScreenSquare(fromX, fromY, &start);\r
9995   ScreenSquare(toX, toY, &finish);\r
9996 \r
9997   /* All moves except knight jumps move in straight line */\r
9998   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9999     mid.x = start.x + (finish.x - start.x) / 2;\r
10000     mid.y = start.y + (finish.y - start.y) / 2;\r
10001   } else {\r
10002     /* Knight: make straight movement then diagonal */\r
10003     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10004        mid.x = start.x + (finish.x - start.x) / 2;\r
10005        mid.y = start.y;\r
10006      } else {\r
10007        mid.x = start.x;\r
10008        mid.y = start.y + (finish.y - start.y) / 2;\r
10009      }\r
10010   }\r
10011   \r
10012   /* Don't use as many frames for very short moves */\r
10013   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10014     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10015   else\r
10016     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10017 \r
10018   animInfo.from.x = fromX;\r
10019   animInfo.from.y = fromY;\r
10020   animInfo.to.x = toX;\r
10021   animInfo.to.y = toY;\r
10022   animInfo.lastpos = start;\r
10023   animInfo.piece = piece;\r
10024   for (n = 0; n < nFrames; n++) {\r
10025     animInfo.pos = frames[n];\r
10026     DrawPosition(FALSE, NULL);\r
10027     animInfo.lastpos = animInfo.pos;\r
10028     Sleep(appData.animSpeed);\r
10029   }\r
10030   animInfo.pos = finish;\r
10031   DrawPosition(FALSE, NULL);\r
10032 \r
10033   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10034 \r
10035   animInfo.piece = EmptySquare;\r
10036   Explode(board, fromX, fromY, toX, toY);\r
10037 }\r
10038 \r
10039 /*      Convert board position to corner of screen rect and color       */\r
10040 \r
10041 static void\r
10042 ScreenSquare(column, row, pt)\r
10043      int column; int row; POINT * pt;\r
10044 {\r
10045   if (flipView) {\r
10046     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10047     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10048   } else {\r
10049     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10050     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10051   }\r
10052 }\r
10053 \r
10054 /*      Generate a series of frame coords from start->mid->finish.\r
10055         The movement rate doubles until the half way point is\r
10056         reached, then halves back down to the final destination,\r
10057         which gives a nice slow in/out effect. The algorithmn\r
10058         may seem to generate too many intermediates for short\r
10059         moves, but remember that the purpose is to attract the\r
10060         viewers attention to the piece about to be moved and\r
10061         then to where it ends up. Too few frames would be less\r
10062         noticeable.                                             */\r
10063 \r
10064 static void\r
10065 Tween(start, mid, finish, factor, frames, nFrames)\r
10066      POINT * start; POINT * mid;\r
10067      POINT * finish; int factor;\r
10068      POINT frames[]; int * nFrames;\r
10069 {\r
10070   int n, fraction = 1, count = 0;\r
10071 \r
10072   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10073   for (n = 0; n < factor; n++)\r
10074     fraction *= 2;\r
10075   for (n = 0; n < factor; n++) {\r
10076     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10077     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10078     count ++;\r
10079     fraction = fraction / 2;\r
10080   }\r
10081   \r
10082   /* Midpoint */\r
10083   frames[count] = *mid;\r
10084   count ++;\r
10085   \r
10086   /* Slow out, stepping 1/2, then 1/4, ... */\r
10087   fraction = 2;\r
10088   for (n = 0; n < factor; n++) {\r
10089     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10090     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10091     count ++;\r
10092     fraction = fraction * 2;\r
10093   }\r
10094   *nFrames = count;\r
10095 }\r
10096 \r
10097 void\r
10098 SettingsPopUp(ChessProgramState *cps)\r
10099 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10100       EngineOptionsPopup(savedHwnd, cps);\r
10101 }\r
10102 \r
10103 int flock(int fid, int code)\r
10104 {\r
10105     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10106     OVERLAPPED ov;\r
10107     ov.hEvent = NULL;\r
10108     ov.Offset = 0;\r
10109     ov.OffsetHigh = 0;\r
10110     switch(code) {\r
10111       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10112 \r
10113       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10114       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10115       default: return -1;\r
10116     }\r
10117     return 0;\r
10118 }\r
10119 \r
10120 char *\r
10121 Col2Text (int n)\r
10122 {\r
10123     static int i=0;\r
10124     static char col[8][20];\r
10125     COLORREF color = *(COLORREF *) colorVariable[n];\r
10126     i = i+1 & 7;\r
10127     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10128     return col[i];\r
10129 }\r
10130 \r
10131 void\r
10132 ActivateTheme (int new)\r
10133 {   // Redo initialization of features depending on options that can occur in themes\r
10134    InitTextures();\r
10135    if(new) InitDrawingColors();\r
10136    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10137    InitDrawingSizes(boardSize, 0);\r
10138    InvalidateRect(hwndMain, NULL, TRUE);\r
10139 }\r