Size seek graph to also cover board rim WinBoard
[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;\r
170 static int winW, winH;\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
172 static int logoHeight = 0;\r
173 static char messageText[MESSAGE_TEXT_MAX];\r
174 static int clockTimerEvent = 0;\r
175 static int loadGameTimerEvent = 0;\r
176 static int analysisTimerEvent = 0;\r
177 static DelayedEventCallback delayedTimerCallback;\r
178 static int delayedTimerEvent = 0;\r
179 static int buttonCount = 2;\r
180 char *icsTextMenuString;\r
181 char *icsNames;\r
182 char *firstChessProgramNames;\r
183 char *secondChessProgramNames;\r
184 \r
185 #define PALETTESIZE 256\r
186 \r
187 HINSTANCE hInst;          /* current instance */\r
188 Boolean alwaysOnTop = FALSE;\r
189 RECT boardRect;\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
191   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
193 HPALETTE hPal;\r
194 ColorClass currentColorClass;\r
195 \r
196 static HWND savedHwnd;\r
197 HWND hCommPort = NULL;    /* currently open comm port */\r
198 static HWND hwndPause;    /* pause button */\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,\r
201   blackSquareBrush, /* [HGM] for band between board and holdings */\r
202   explodeBrush,     /* [HGM] atomic */\r
203   markerBrush[8],   /* [HGM] markers */\r
204   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
207 static HPEN gridPen = NULL;\r
208 static HPEN highlightPen = NULL;\r
209 static HPEN premovePen = NULL;\r
210 static NPLOGPALETTE pLogPal;\r
211 static BOOL paletteChanged = FALSE;\r
212 static HICON iconWhite, iconBlack, iconCurrent;\r
213 static int doingSizing = FALSE;\r
214 static int lastSizing = 0;\r
215 static int prevStderrPort;\r
216 static HBITMAP userLogo;\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 \r
229 #if defined(_winmajor)\r
230 #define oldDialog (_winmajor < 4)\r
231 #else\r
232 #define oldDialog 0\r
233 #endif\r
234 #endif\r
235 \r
236 #define INTERNATIONAL\r
237 \r
238 #ifdef INTERNATIONAL\r
239 #  define _(s) T_(s)\r
240 #  define N_(s) s\r
241 #else\r
242 #  define _(s) s\r
243 #  define N_(s) s\r
244 #  define T_(s) s\r
245 #  define Translate(x, y)\r
246 #  define LoadLanguageFile(s)\r
247 #endif\r
248 \r
249 #ifdef INTERNATIONAL\r
250 \r
251 Boolean barbaric; // flag indicating if translation is needed\r
252 \r
253 // list of item numbers used in each dialog (used to alter language at run time)\r
254 \r
255 #define ABOUTBOX -1  /* not sure why these are needed */\r
256 #define ABOUTBOX2 -1\r
257 \r
258 int dialogItems[][42] = {\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
261   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
263   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,\r
264   OPT_Ranget, IDOK, IDCANCEL }, \r
265 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
266   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
267 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
268 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
269   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
270 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
271 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
272   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
273 { ABOUTBOX2, IDC_ChessBoard }, \r
274 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
275   OPT_GameListClose, IDC_GameListDoFilter }, \r
276 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
277 { DLG_Error, IDOK }, \r
278 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
279   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
280 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
281 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
282   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
283   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
284 { DLG_IndexNumber, IDC_Index }, \r
285 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
286 { DLG_TypeInName, IDOK, IDCANCEL }, \r
287 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
288   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
289 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
290   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
291   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
292   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
293   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
294   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
295   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
296 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
297   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
298   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
299   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
300   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
301   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
302   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
303   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
304   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
305 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
306   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
307   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
308   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
309   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
310   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
311   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
312   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
313 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
314   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
315   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
316   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
317   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
318   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
319   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
320   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
321   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
322 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
323   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
324   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
325   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
326   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
327 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
328 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
329   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
330 { DLG_MoveHistory }, \r
331 { DLG_EvalGraph }, \r
332 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
333 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
334 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
335   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
336   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
337   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
338 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
339   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
340   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
341 { 0 }\r
342 };\r
343 \r
344 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
345 static int lastChecked;\r
346 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
347 extern int tinyLayout;\r
348 extern char * menuBarText[][10];\r
349 \r
350 void\r
351 LoadLanguageFile(char *name)\r
352 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
353     FILE *f;\r
354     int i=0, j=0, n=0, k;\r
355     char buf[MSG_SIZ];\r
356 \r
357     if(!name || name[0] == NULLCHAR) return;\r
358       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
359     appData.language = oldLanguage;\r
360     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
361     if((f = fopen(buf, "r")) == NULL) return;\r
362     while((k = fgetc(f)) != EOF) {\r
363         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
364         languageBuf[i] = k;\r
365         if(k == '\n') {\r
366             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
367                 char *p;\r
368                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
369                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
370                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
371                         english[j] = languageBuf + n + 1; *p = 0;\r
372                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
373 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
374                     }\r
375                 }\r
376             }\r
377             n = i + 1;\r
378         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
379             switch(k) {\r
380               case 'n': k = '\n'; break;\r
381               case 'r': k = '\r'; break;\r
382               case 't': k = '\t'; break;\r
383             }\r
384             languageBuf[--i] = k;\r
385         }\r
386         i++;\r
387     }\r
388     fclose(f);\r
389     barbaric = (j != 0);\r
390     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
391 }\r
392 \r
393 char *\r
394 T_(char *s)\r
395 {   // return the translation of the given string\r
396     // efficiency can be improved a lot...\r
397     int i=0;\r
398     static char buf[MSG_SIZ];\r
399 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
400     if(!barbaric) return s;\r
401     if(!s) return ""; // sanity\r
402     while(english[i]) {\r
403         if(!strcmp(s, english[i])) return foreign[i];\r
404         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
405             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
406             return buf;\r
407         }\r
408         i++;\r
409     }\r
410     return s;\r
411 }\r
412 \r
413 void\r
414 Translate(HWND hDlg, int dialogID)\r
415 {   // translate all text items in the given dialog\r
416     int i=0, j, k;\r
417     char buf[MSG_SIZ], *s;\r
418     if(!barbaric) return;\r
419     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
420     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
421     GetWindowText( hDlg, buf, MSG_SIZ );\r
422     s = T_(buf);\r
423     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
424     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
425         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
426         if(strlen(buf) == 0) continue;\r
427         s = T_(buf);\r
428         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
429     }\r
430 }\r
431 \r
432 HMENU\r
433 TranslateOneMenu(int i, HMENU subMenu)\r
434 {\r
435     int j;\r
436     static MENUITEMINFO info;\r
437 \r
438     info.cbSize = sizeof(MENUITEMINFO);\r
439     info.fMask = MIIM_STATE | MIIM_TYPE;\r
440           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
441             char buf[MSG_SIZ];\r
442             info.dwTypeData = buf;\r
443             info.cch = sizeof(buf);\r
444             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
445             if(i < 10) {\r
446                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
447                 else menuText[i][j] = strdup(buf); // remember original on first change\r
448             }\r
449             if(buf[0] == NULLCHAR) continue;\r
450             info.dwTypeData = T_(buf);\r
451             info.cch = strlen(buf)+1;\r
452             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
453           }\r
454     return subMenu;\r
455 }\r
456 \r
457 void\r
458 TranslateMenus(int addLanguage)\r
459 {\r
460     int i;\r
461     WIN32_FIND_DATA fileData;\r
462     HANDLE hFind;\r
463 #define IDM_English 1970\r
464     if(1) {\r
465         HMENU mainMenu = GetMenu(hwndMain);\r
466         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
467           HMENU subMenu = GetSubMenu(mainMenu, i);\r
468           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
469                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
470           TranslateOneMenu(i, subMenu);\r
471         }\r
472         DrawMenuBar(hwndMain);\r
473     }\r
474 \r
475     if(!addLanguage) return;\r
476     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
477         HMENU mainMenu = GetMenu(hwndMain);\r
478         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
479         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
480         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
481         i = 0; lastChecked = IDM_English;\r
482         do {\r
483             char *p, *q = fileData.cFileName;\r
484             int checkFlag = MF_UNCHECKED;\r
485             languageFile[i] = strdup(q);\r
486             if(barbaric && !strcmp(oldLanguage, q)) {\r
487                 checkFlag = MF_CHECKED;\r
488                 lastChecked = IDM_English + i + 1;\r
489                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
490             }\r
491             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
492             p = strstr(fileData.cFileName, ".lng");\r
493             if(p) *p = 0;\r
494             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
495         } while(FindNextFile(hFind, &fileData));\r
496         FindClose(hFind);\r
497     }\r
498 }\r
499 \r
500 #endif\r
501 \r
502 #define IDM_RecentEngines 3000\r
503 \r
504 void\r
505 RecentEngineMenu (char *s)\r
506 {\r
507     if(appData.icsActive) return;\r
508     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
509         HMENU mainMenu = GetMenu(hwndMain);\r
510         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
511         int i=IDM_RecentEngines;\r
512         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
513         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
514         while(*s) {\r
515           char *p = strchr(s, '\n');\r
516           if(p == NULL) return; // malformed!\r
517           *p = NULLCHAR;\r
518           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
519           *p = '\n';\r
520           s = p+1;\r
521         }\r
522     }\r
523 }\r
524 \r
525 \r
526 typedef struct {\r
527   char *name;\r
528   int squareSize;\r
529   int lineGap;\r
530   int smallLayout;\r
531   int tinyLayout;\r
532   int cliWidth, cliHeight;\r
533 } SizeInfo;\r
534 \r
535 SizeInfo sizeInfo[] = \r
536 {\r
537   { "tiny",     21, 0, 1, 1, 0, 0 },\r
538   { "teeny",    25, 1, 1, 1, 0, 0 },\r
539   { "dinky",    29, 1, 1, 1, 0, 0 },\r
540   { "petite",   33, 1, 1, 1, 0, 0 },\r
541   { "slim",     37, 2, 1, 0, 0, 0 },\r
542   { "small",    40, 2, 1, 0, 0, 0 },\r
543   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
544   { "middling", 49, 2, 0, 0, 0, 0 },\r
545   { "average",  54, 2, 0, 0, 0, 0 },\r
546   { "moderate", 58, 3, 0, 0, 0, 0 },\r
547   { "medium",   64, 3, 0, 0, 0, 0 },\r
548   { "bulky",    72, 3, 0, 0, 0, 0 },\r
549   { "large",    80, 3, 0, 0, 0, 0 },\r
550   { "big",      87, 3, 0, 0, 0, 0 },\r
551   { "huge",     95, 3, 0, 0, 0, 0 },\r
552   { "giant",    108, 3, 0, 0, 0, 0 },\r
553   { "colossal", 116, 4, 0, 0, 0, 0 },\r
554   { "titanic",  129, 4, 0, 0, 0, 0 },\r
555   { NULL, 0, 0, 0, 0, 0, 0 }\r
556 };\r
557 \r
558 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
559 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
560 {\r
561   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
562   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
563   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
564   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
565   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
566   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
567   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
568   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
569   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
570   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
571   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
572   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
573   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
574   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL),  MF(GAMELIST_FONT_ALL) },\r
575   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
576   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
577   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },\r
578   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },\r
579 };\r
580 \r
581 MyFont *font[NUM_SIZES][NUM_FONTS];\r
582 \r
583 typedef struct {\r
584   char *label;\r
585   int id;\r
586   HWND hwnd;\r
587   WNDPROC wndproc;\r
588 } MyButtonDesc;\r
589 \r
590 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
591 #define N_BUTTONS 5\r
592 \r
593 MyButtonDesc buttonDesc[N_BUTTONS] =\r
594 {\r
595   {"<<", IDM_ToStart, NULL, NULL},\r
596   {"<", IDM_Backward, NULL, NULL},\r
597   {"P", IDM_Pause, NULL, NULL},\r
598   {">", IDM_Forward, NULL, NULL},\r
599   {">>", IDM_ToEnd, NULL, NULL},\r
600 };\r
601 \r
602 int tinyLayout = 0, smallLayout = 0;\r
603 #define MENU_BAR_ITEMS 9\r
604 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
605   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
606   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
607 };\r
608 \r
609 \r
610 MySound sounds[(int)NSoundClasses];\r
611 MyTextAttribs textAttribs[(int)NColorClasses];\r
612 \r
613 MyColorizeAttribs colorizeAttribs[] = {\r
614   { (COLORREF)0, 0, N_("Shout Text") },\r
615   { (COLORREF)0, 0, N_("SShout/CShout") },\r
616   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
617   { (COLORREF)0, 0, N_("Channel Text") },\r
618   { (COLORREF)0, 0, N_("Kibitz Text") },\r
619   { (COLORREF)0, 0, N_("Tell Text") },\r
620   { (COLORREF)0, 0, N_("Challenge Text") },\r
621   { (COLORREF)0, 0, N_("Request Text") },\r
622   { (COLORREF)0, 0, N_("Seek Text") },\r
623   { (COLORREF)0, 0, N_("Normal Text") },\r
624   { (COLORREF)0, 0, N_("None") }\r
625 };\r
626 \r
627 \r
628 \r
629 static char *commentTitle;\r
630 static char *commentText;\r
631 static int commentIndex;\r
632 static Boolean editComment = FALSE;\r
633 \r
634 \r
635 char errorTitle[MSG_SIZ];\r
636 char errorMessage[2*MSG_SIZ];\r
637 HWND errorDialog = NULL;\r
638 BOOLEAN moveErrorMessageUp = FALSE;\r
639 BOOLEAN consoleEcho = TRUE;\r
640 CHARFORMAT consoleCF;\r
641 COLORREF consoleBackgroundColor;\r
642 \r
643 char *programVersion;\r
644 \r
645 #define CPReal 1\r
646 #define CPComm 2\r
647 #define CPSock 3\r
648 #define CPRcmd 4\r
649 typedef int CPKind;\r
650 \r
651 typedef struct {\r
652   CPKind kind;\r
653   HANDLE hProcess;\r
654   DWORD pid;\r
655   HANDLE hTo;\r
656   HANDLE hFrom;\r
657   SOCKET sock;\r
658   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
659 } ChildProc;\r
660 \r
661 #define INPUT_SOURCE_BUF_SIZE 4096\r
662 \r
663 typedef struct _InputSource {\r
664   CPKind kind;\r
665   HANDLE hFile;\r
666   SOCKET sock;\r
667   int lineByLine;\r
668   HANDLE hThread;\r
669   DWORD id;\r
670   char buf[INPUT_SOURCE_BUF_SIZE];\r
671   char *next;\r
672   DWORD count;\r
673   int error;\r
674   InputCallback func;\r
675   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
676   VOIDSTAR closure;\r
677 } InputSource;\r
678 \r
679 InputSource *consoleInputSource;\r
680 \r
681 DCB dcb;\r
682 \r
683 /* forward */\r
684 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
685 VOID ConsoleCreate();\r
686 LRESULT CALLBACK\r
687   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
688 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
689 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
690 VOID ParseCommSettings(char *arg, DCB *dcb);\r
691 LRESULT CALLBACK\r
692   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
693 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
694 void ParseIcsTextMenu(char *icsTextMenuString);\r
695 VOID PopUpNameDialog(char firstchar);\r
696 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
697 \r
698 /* [AS] */\r
699 int NewGameFRC();\r
700 int GameListOptions();\r
701 \r
702 int dummy; // [HGM] for obsolete args\r
703 \r
704 HWND hwndMain = NULL;        /* root window*/\r
705 HWND hwndConsole = NULL;\r
706 HWND commentDialog = NULL;\r
707 HWND moveHistoryDialog = NULL;\r
708 HWND evalGraphDialog = NULL;\r
709 HWND engineOutputDialog = NULL;\r
710 HWND gameListDialog = NULL;\r
711 HWND editTagsDialog = NULL;\r
712 \r
713 int commentUp = FALSE;\r
714 \r
715 WindowPlacement wpMain;\r
716 WindowPlacement wpConsole;\r
717 WindowPlacement wpComment;\r
718 WindowPlacement wpMoveHistory;\r
719 WindowPlacement wpEvalGraph;\r
720 WindowPlacement wpEngineOutput;\r
721 WindowPlacement wpGameList;\r
722 WindowPlacement wpTags;\r
723 \r
724 VOID EngineOptionsPopup(); // [HGM] settings\r
725 \r
726 VOID GothicPopUp(char *title, VariantClass variant);\r
727 /*\r
728  * Setting "frozen" should disable all user input other than deleting\r
729  * the window.  We do this while engines are initializing themselves.\r
730  */\r
731 static int frozen = 0;\r
732 static int oldMenuItemState[MENU_BAR_ITEMS];\r
733 void FreezeUI()\r
734 {\r
735   HMENU hmenu;\r
736   int i;\r
737 \r
738   if (frozen) return;\r
739   frozen = 1;\r
740   hmenu = GetMenu(hwndMain);\r
741   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
742     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
743   }\r
744   DrawMenuBar(hwndMain);\r
745 }\r
746 \r
747 /* Undo a FreezeUI */\r
748 void ThawUI()\r
749 {\r
750   HMENU hmenu;\r
751   int i;\r
752 \r
753   if (!frozen) return;\r
754   frozen = 0;\r
755   hmenu = GetMenu(hwndMain);\r
756   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
757     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
758   }\r
759   DrawMenuBar(hwndMain);\r
760 }\r
761 \r
762 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
763 \r
764 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
765 #ifdef JAWS\r
766 #include "jaws.c"\r
767 #else\r
768 #define JAWS_INIT\r
769 #define JAWS_ARGS\r
770 #define JAWS_ALT_INTERCEPT\r
771 #define JAWS_KBUP_NAVIGATION\r
772 #define JAWS_KBDOWN_NAVIGATION\r
773 #define JAWS_MENU_ITEMS\r
774 #define JAWS_SILENCE\r
775 #define JAWS_REPLAY\r
776 #define JAWS_ACCEL\r
777 #define JAWS_COPYRIGHT\r
778 #define JAWS_DELETE(X) X\r
779 #define SAYMACHINEMOVE()\r
780 #define SAY(X)\r
781 #endif\r
782 \r
783 /*---------------------------------------------------------------------------*\\r
784  *\r
785  * WinMain\r
786  *\r
787 \*---------------------------------------------------------------------------*/\r
788 \r
789 static void HandleMessage P((MSG *message));\r
790 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
791 \r
792 int APIENTRY\r
793 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
794         LPSTR lpCmdLine, int nCmdShow)\r
795 {\r
796   MSG msg;\r
797 //  INITCOMMONCONTROLSEX ex;\r
798 \r
799   debugFP = stderr;\r
800 \r
801   LoadLibrary("RICHED32.DLL");\r
802   consoleCF.cbSize = sizeof(CHARFORMAT);\r
803 \r
804   if (!InitApplication(hInstance)) {\r
805     return (FALSE);\r
806   }\r
807   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
808     return (FALSE);\r
809   }\r
810 \r
811   JAWS_INIT\r
812   TranslateMenus(1);\r
813 \r
814 //  InitCommonControlsEx(&ex);\r
815   InitCommonControls();\r
816 \r
817   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
818   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
819   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
820 \r
821   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
822 \r
823   while (GetMessage(&msg, /* message structure */\r
824                     NULL, /* handle of window receiving the message */\r
825                     0,    /* lowest message to examine */\r
826                     0))   /* highest message to examine */\r
827     {\r
828         HandleMessage(&msg);\r
829     }\r
830 \r
831 \r
832   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
833 }\r
834 \r
835 static void\r
836 HandleMessage (MSG *message)\r
837 {\r
838     MSG msg = *message;\r
839 \r
840       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
841         // [HGM] navigate: switch between all windows with tab\r
842         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
843         int i, currentElement = 0;\r
844 \r
845         // first determine what element of the chain we come from (if any)\r
846         if(appData.icsActive) {\r
847             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
848             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
849         }\r
850         if(engineOutputDialog && EngineOutputIsUp()) {\r
851             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
852             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
853         }\r
854         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
855             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
856         }\r
857         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
858         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
859         if(msg.hwnd == e1)                 currentElement = 2; else\r
860         if(msg.hwnd == e2)                 currentElement = 3; else\r
861         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
862         if(msg.hwnd == mh)                currentElement = 4; else\r
863         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
864         if(msg.hwnd == hText)  currentElement = 5; else\r
865         if(msg.hwnd == hInput) currentElement = 6; else\r
866         for (i = 0; i < N_BUTTONS; i++) {\r
867             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
868         }\r
869 \r
870         // determine where to go to\r
871         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
872           do {\r
873             currentElement = (currentElement + direction) % 7;\r
874             switch(currentElement) {\r
875                 case 0:\r
876                   h = hwndMain; break; // passing this case always makes the loop exit\r
877                 case 1:\r
878                   h = buttonDesc[0].hwnd; break; // could be NULL\r
879                 case 2:\r
880                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
881                   h = e1; break;\r
882                 case 3:\r
883                   if(!EngineOutputIsUp()) continue;\r
884                   h = e2; break;\r
885                 case 4:\r
886                   if(!MoveHistoryIsUp()) continue;\r
887                   h = mh; break;\r
888 //              case 6: // input to eval graph does not seem to get here!\r
889 //                if(!EvalGraphIsUp()) continue;\r
890 //                h = evalGraphDialog; break;\r
891                 case 5:\r
892                   if(!appData.icsActive) continue;\r
893                   SAY("display");\r
894                   h = hText; break;\r
895                 case 6:\r
896                   if(!appData.icsActive) continue;\r
897                   SAY("input");\r
898                   h = hInput; break;\r
899             }\r
900           } while(h == 0);\r
901 \r
902           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
903           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
904           SetFocus(h);\r
905 \r
906           return; // this message now has been processed\r
907         }\r
908       }\r
909 \r
910       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
911           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
912           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
913           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
914           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
915           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
916           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
917           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
918           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
919           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
920         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
921         for(i=0; i<MAX_CHAT; i++) \r
922             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
923                 done = 1; break;\r
924         }\r
925         if(done) return; // [HGM] chat: end patch\r
926         TranslateMessage(&msg); /* Translates virtual key codes */\r
927         DispatchMessage(&msg);  /* Dispatches message to window */\r
928       }\r
929 }\r
930 \r
931 void\r
932 DoEvents ()\r
933 { /* Dispatch pending messages */\r
934   MSG msg;\r
935   while (PeekMessage(&msg, /* message structure */\r
936                      NULL, /* handle of window receiving the message */\r
937                      0,    /* lowest message to examine */\r
938                      0,    /* highest message to examine */\r
939                      PM_REMOVE))\r
940     {\r
941         HandleMessage(&msg);\r
942     }\r
943 }\r
944 \r
945 /*---------------------------------------------------------------------------*\\r
946  *\r
947  * Initialization functions\r
948  *\r
949 \*---------------------------------------------------------------------------*/\r
950 \r
951 void\r
952 SetUserLogo()\r
953 {   // update user logo if necessary\r
954     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
955 \r
956     if(appData.autoLogo) {\r
957           curName = UserName();\r
958           if(strcmp(curName, oldUserName)) {\r
959                 GetCurrentDirectory(MSG_SIZ, dir);\r
960                 SetCurrentDirectory(installDir);\r
961                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
962                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
963                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
964                 if(userLogo == NULL)\r
965                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
966                 SetCurrentDirectory(dir); /* return to prev directory */\r
967           }\r
968     }\r
969 }\r
970 \r
971 BOOL\r
972 InitApplication(HINSTANCE hInstance)\r
973 {\r
974   WNDCLASS wc;\r
975 \r
976   /* Fill in window class structure with parameters that describe the */\r
977   /* main window. */\r
978 \r
979   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
980   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
981   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
982   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
983   wc.hInstance     = hInstance;         /* Owner of this class */\r
984   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
985   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
986   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
987   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
988   wc.lpszClassName = szAppName;                 /* Name to register as */\r
989 \r
990   /* Register the window class and return success/failure code. */\r
991   if (!RegisterClass(&wc)) return FALSE;\r
992 \r
993   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
994   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
995   wc.cbClsExtra    = 0;\r
996   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
997   wc.hInstance     = hInstance;\r
998   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
999   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
1000   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
1001   wc.lpszMenuName  = NULL;\r
1002   wc.lpszClassName = szConsoleName;\r
1003 \r
1004   if (!RegisterClass(&wc)) return FALSE;\r
1005   return TRUE;\r
1006 }\r
1007 \r
1008 \r
1009 /* Set by InitInstance, used by EnsureOnScreen */\r
1010 int screenHeight, screenWidth;\r
1011 RECT screenGeometry;\r
1012 \r
1013 void\r
1014 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
1015 {\r
1016 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
1017   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
1018   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
1019   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
1020   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
1021   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
1022 }\r
1023 \r
1024 VOID\r
1025 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1026 {\r
1027   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1028   GetCurrentDirectory(MSG_SIZ, dir);\r
1029   SetCurrentDirectory(installDir);\r
1030   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1031       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1032 \r
1033       if (cps->programLogo == NULL && appData.debugMode) {\r
1034           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1035       }\r
1036   } else if(appData.autoLogo) {\r
1037       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1038         char *opponent = "";\r
1039         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1040         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1041         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1042         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1043             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1044             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1045         }\r
1046       } else\r
1047       if(appData.directory[n] && appData.directory[n][0]) {\r
1048         SetCurrentDirectory(appData.directory[n]);\r
1049         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1050       }\r
1051   }\r
1052   SetCurrentDirectory(dir); /* return to prev directory */\r
1053 }\r
1054 \r
1055 VOID\r
1056 InitTextures()\r
1057 {\r
1058   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1059   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1060   \r
1061   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1062       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1063       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1064       liteBackTextureMode = appData.liteBackTextureMode;\r
1065 \r
1066       if (liteBackTexture == NULL && appData.debugMode) {\r
1067           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1068       }\r
1069   }\r
1070   \r
1071   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1072       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1073       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1074       darkBackTextureMode = appData.darkBackTextureMode;\r
1075 \r
1076       if (darkBackTexture == NULL && appData.debugMode) {\r
1077           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1078       }\r
1079   }\r
1080 }\r
1081 \r
1082 #ifndef SM_CXVIRTUALSCREEN\r
1083 #define SM_CXVIRTUALSCREEN 78\r
1084 #endif\r
1085 #ifndef SM_CYVIRTUALSCREEN\r
1086 #define SM_CYVIRTUALSCREEN 79\r
1087 #endif\r
1088 #ifndef SM_XVIRTUALSCREEN \r
1089 #define SM_XVIRTUALSCREEN 76\r
1090 #endif\r
1091 #ifndef SM_YVIRTUALSCREEN \r
1092 #define SM_YVIRTUALSCREEN 77\r
1093 #endif\r
1094 \r
1095 VOID\r
1096 InitGeometry()\r
1097 {\r
1098   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1099   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1100   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1101   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1102   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1103   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1104   screenGeometry.right = screenGeometry.left + screenWidth;\r
1105   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1106 }\r
1107 \r
1108 BOOL\r
1109 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1110 {\r
1111   HWND hwnd; /* Main window handle. */\r
1112   int ibs;\r
1113   WINDOWPLACEMENT wp;\r
1114   char *filepart;\r
1115 \r
1116   hInst = hInstance;    /* Store instance handle in our global variable */\r
1117   programName = szAppName;\r
1118 \r
1119   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1120     *filepart = NULLCHAR;\r
1121     SetCurrentDirectory(installDir);\r
1122   } else {\r
1123     GetCurrentDirectory(MSG_SIZ, installDir);\r
1124   }\r
1125   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1126   InitGeometry();\r
1127   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1128   /* xboard, and older WinBoards, controlled the move sound with the\r
1129      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1130      always turn the option on (so that the backend will call us),\r
1131      then let the user turn the sound off by setting it to silence if\r
1132      desired.  To accommodate old winboard.ini files saved by old\r
1133      versions of WinBoard, we also turn off the sound if the option\r
1134      was initially set to false. [HGM] taken out of InitAppData */\r
1135   if (!appData.ringBellAfterMoves) {\r
1136     sounds[(int)SoundMove].name = strdup("");\r
1137     appData.ringBellAfterMoves = TRUE;\r
1138   }\r
1139   if (appData.debugMode) {\r
1140     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1141     setbuf(debugFP, NULL);\r
1142   }\r
1143 \r
1144   LoadLanguageFile(appData.language);\r
1145 \r
1146   InitBackEnd1();\r
1147 \r
1148 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1149 //  InitEngineUCI( installDir, &second );\r
1150 \r
1151   /* Create a main window for this application instance. */\r
1152   hwnd = CreateWindow(szAppName, szTitle,\r
1153                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1154                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1155                       NULL, NULL, hInstance, NULL);\r
1156   hwndMain = hwnd;\r
1157 \r
1158   /* If window could not be created, return "failure" */\r
1159   if (!hwnd) {\r
1160     return (FALSE);\r
1161   }\r
1162 \r
1163   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1164   LoadLogo(&first, 0, FALSE);\r
1165   LoadLogo(&second, 1, appData.icsActive);\r
1166 \r
1167   SetUserLogo();\r
1168 \r
1169   iconWhite = LoadIcon(hInstance, "icon_white");\r
1170   iconBlack = LoadIcon(hInstance, "icon_black");\r
1171   iconCurrent = iconWhite;\r
1172   InitDrawingColors();\r
1173 \r
1174   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1175   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1176     /* Compute window size for each board size, and use the largest\r
1177        size that fits on this screen as the default. */\r
1178     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1179     if (boardSize == (BoardSize)-1 &&\r
1180         winH <= screenHeight\r
1181            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1182         && winW <= screenWidth) {\r
1183       boardSize = (BoardSize)ibs;\r
1184     }\r
1185   }\r
1186 \r
1187   InitDrawingSizes(boardSize, 0);\r
1188   RecentEngineMenu(appData.recentEngineList);\r
1189   InitMenuChecks();\r
1190   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1191 \r
1192   /* [AS] Load textures if specified */\r
1193   InitTextures();\r
1194 \r
1195   mysrandom( (unsigned) time(NULL) );\r
1196 \r
1197   /* [AS] Restore layout */\r
1198   if( wpMoveHistory.visible ) {\r
1199       MoveHistoryPopUp();\r
1200   }\r
1201 \r
1202   if( wpEvalGraph.visible ) {\r
1203       EvalGraphPopUp();\r
1204   }\r
1205 \r
1206   if( wpEngineOutput.visible ) {\r
1207       EngineOutputPopUp();\r
1208   }\r
1209 \r
1210   /* Make the window visible; update its client area; and return "success" */\r
1211   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1212   wp.length = sizeof(WINDOWPLACEMENT);\r
1213   wp.flags = 0;\r
1214   wp.showCmd = nCmdShow;\r
1215   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1216   wp.rcNormalPosition.left = wpMain.x;\r
1217   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1218   wp.rcNormalPosition.top = wpMain.y;\r
1219   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1220   SetWindowPlacement(hwndMain, &wp);\r
1221 \r
1222   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1223 \r
1224   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1225                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1226 \r
1227   if (hwndConsole) {\r
1228 #if AOT_CONSOLE\r
1229     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1230                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1231 #endif\r
1232     ShowWindow(hwndConsole, nCmdShow);\r
1233     SetActiveWindow(hwndConsole);\r
1234   }\r
1235   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1236   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1237 \r
1238   return TRUE;\r
1239 \r
1240 }\r
1241 \r
1242 VOID\r
1243 InitMenuChecks()\r
1244 {\r
1245   HMENU hmenu = GetMenu(hwndMain);\r
1246 \r
1247   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1248                         MF_BYCOMMAND|((appData.icsActive &&\r
1249                                        *appData.icsCommPort != NULLCHAR) ?\r
1250                                       MF_ENABLED : MF_GRAYED));\r
1251   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1252                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1253                                      MF_CHECKED : MF_UNCHECKED));\r
1254   EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);\r
1255 }\r
1256 \r
1257 //---------------------------------------------------------------------------------------------------------\r
1258 \r
1259 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1260 #define XBOARD FALSE\r
1261 \r
1262 #define OPTCHAR "/"\r
1263 #define SEPCHAR "="\r
1264 #define TOPLEVEL 0\r
1265 \r
1266 #include "args.h"\r
1267 \r
1268 // front-end part of option handling\r
1269 \r
1270 VOID\r
1271 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1272 {\r
1273   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1274   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1275   DeleteDC(hdc);\r
1276   lf->lfWidth = 0;\r
1277   lf->lfEscapement = 0;\r
1278   lf->lfOrientation = 0;\r
1279   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1280   lf->lfItalic = mfp->italic;\r
1281   lf->lfUnderline = mfp->underline;\r
1282   lf->lfStrikeOut = mfp->strikeout;\r
1283   lf->lfCharSet = mfp->charset;\r
1284   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1285 \r
1286   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1287   lf->lfQuality = DEFAULT_QUALITY;\r
1288   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1289     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1290 }\r
1291 \r
1292 void\r
1293 CreateFontInMF(MyFont *mf)\r
1294\r
1295   LFfromMFP(&mf->lf, &mf->mfp);\r
1296   if (mf->hf) DeleteObject(mf->hf);\r
1297   mf->hf = CreateFontIndirect(&mf->lf);\r
1298 }\r
1299 \r
1300 // [HGM] This platform-dependent table provides the location for storing the color info\r
1301 void *\r
1302 colorVariable[] = {\r
1303   &whitePieceColor, \r
1304   &blackPieceColor, \r
1305   &lightSquareColor,\r
1306   &darkSquareColor, \r
1307   &highlightSquareColor,\r
1308   &premoveHighlightColor,\r
1309   NULL,\r
1310   &consoleBackgroundColor,\r
1311   &appData.fontForeColorWhite,\r
1312   &appData.fontBackColorWhite,\r
1313   &appData.fontForeColorBlack,\r
1314   &appData.fontBackColorBlack,\r
1315   &appData.evalHistColorWhite,\r
1316   &appData.evalHistColorBlack,\r
1317   &appData.highlightArrowColor,\r
1318 };\r
1319 \r
1320 /* Command line font name parser.  NULL name means do nothing.\r
1321    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1322    For backward compatibility, syntax without the colon is also\r
1323    accepted, but font names with digits in them won't work in that case.\r
1324 */\r
1325 VOID\r
1326 ParseFontName(char *name, MyFontParams *mfp)\r
1327 {\r
1328   char *p, *q;\r
1329   if (name == NULL) return;\r
1330   p = name;\r
1331   q = strchr(p, ':');\r
1332   if (q) {\r
1333     if (q - p >= sizeof(mfp->faceName))\r
1334       ExitArgError(_("Font name too long:"), name, TRUE);\r
1335     memcpy(mfp->faceName, p, q - p);\r
1336     mfp->faceName[q - p] = NULLCHAR;\r
1337     p = q + 1;\r
1338   } else {\r
1339     q = mfp->faceName;\r
1340 \r
1341     while (*p && !isdigit(*p)) {\r
1342       *q++ = *p++;\r
1343       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1344         ExitArgError(_("Font name too long:"), name, TRUE);\r
1345     }\r
1346     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1347     *q = NULLCHAR;\r
1348   }\r
1349   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1350   mfp->pointSize = (float) atof(p);\r
1351   mfp->bold = (strchr(p, 'b') != NULL);\r
1352   mfp->italic = (strchr(p, 'i') != NULL);\r
1353   mfp->underline = (strchr(p, 'u') != NULL);\r
1354   mfp->strikeout = (strchr(p, 's') != NULL);\r
1355   mfp->charset = DEFAULT_CHARSET;\r
1356   q = strchr(p, 'c');\r
1357   if (q)\r
1358     mfp->charset = (BYTE) atoi(q+1);\r
1359 }\r
1360 \r
1361 void\r
1362 ParseFont(char *name, int number)\r
1363 { // wrapper to shield back-end from 'font'\r
1364   ParseFontName(name, &font[boardSize][number]->mfp);\r
1365 }\r
1366 \r
1367 void\r
1368 SetFontDefaults()\r
1369 { // in WB  we have a 2D array of fonts; this initializes their description\r
1370   int i, j;\r
1371   /* Point font array elements to structures and\r
1372      parse default font names */\r
1373   for (i=0; i<NUM_FONTS; i++) {\r
1374     for (j=0; j<NUM_SIZES; j++) {\r
1375       font[j][i] = &fontRec[j][i];\r
1376       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1377     }\r
1378   }\r
1379 }\r
1380 \r
1381 void\r
1382 CreateFonts()\r
1383 { // here we create the actual fonts from the selected descriptions\r
1384   int i, j;\r
1385   for (i=0; i<NUM_FONTS; i++) {\r
1386     for (j=0; j<NUM_SIZES; j++) {\r
1387       CreateFontInMF(font[j][i]);\r
1388     }\r
1389   }\r
1390 }\r
1391 /* Color name parser.\r
1392    X version accepts X color names, but this one\r
1393    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1394 COLORREF\r
1395 ParseColorName(char *name)\r
1396 {\r
1397   int red, green, blue, count;\r
1398   char buf[MSG_SIZ];\r
1399 \r
1400   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1401   if (count != 3) {\r
1402     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1403       &red, &green, &blue);\r
1404   }\r
1405   if (count != 3) {\r
1406     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1407     DisplayError(buf, 0);\r
1408     return RGB(0, 0, 0);\r
1409   }\r
1410   return PALETTERGB(red, green, blue);\r
1411 }\r
1412 \r
1413 void\r
1414 ParseColor(int n, char *name)\r
1415 { // for WinBoard the color is an int, which needs to be derived from the string\r
1416   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1417 }\r
1418 \r
1419 void\r
1420 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1421 {\r
1422   char *e = argValue;\r
1423   int eff = 0;\r
1424 \r
1425   while (*e) {\r
1426     if (*e == 'b')      eff |= CFE_BOLD;\r
1427     else if (*e == 'i') eff |= CFE_ITALIC;\r
1428     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1429     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1430     else if (*e == '#' || isdigit(*e)) break;\r
1431     e++;\r
1432   }\r
1433   *effects = eff;\r
1434   *color   = ParseColorName(e);\r
1435 }\r
1436 \r
1437 void\r
1438 ParseTextAttribs(ColorClass cc, char *s)\r
1439 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1440     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1441     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1442 }\r
1443 \r
1444 void\r
1445 ParseBoardSize(void *addr, char *name)\r
1446 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1447   BoardSize bs = SizeTiny;\r
1448   while (sizeInfo[bs].name != NULL) {\r
1449     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1450         *(BoardSize *)addr = bs;\r
1451         return;\r
1452     }\r
1453     bs++;\r
1454   }\r
1455   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1456 }\r
1457 \r
1458 void\r
1459 LoadAllSounds()\r
1460 { // [HGM] import name from appData first\r
1461   ColorClass cc;\r
1462   SoundClass sc;\r
1463   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1464     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1465     textAttribs[cc].sound.data = NULL;\r
1466     MyLoadSound(&textAttribs[cc].sound);\r
1467   }\r
1468   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1469     textAttribs[cc].sound.name = strdup("");\r
1470     textAttribs[cc].sound.data = NULL;\r
1471   }\r
1472   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1473     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1474     sounds[sc].data = NULL;\r
1475     MyLoadSound(&sounds[sc]);\r
1476   }\r
1477 }\r
1478 \r
1479 void\r
1480 SetCommPortDefaults()\r
1481 {\r
1482    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1483   dcb.DCBlength = sizeof(DCB);\r
1484   dcb.BaudRate = 9600;\r
1485   dcb.fBinary = TRUE;\r
1486   dcb.fParity = FALSE;\r
1487   dcb.fOutxCtsFlow = FALSE;\r
1488   dcb.fOutxDsrFlow = FALSE;\r
1489   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1490   dcb.fDsrSensitivity = FALSE;\r
1491   dcb.fTXContinueOnXoff = TRUE;\r
1492   dcb.fOutX = FALSE;\r
1493   dcb.fInX = FALSE;\r
1494   dcb.fNull = FALSE;\r
1495   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1496   dcb.fAbortOnError = FALSE;\r
1497   dcb.ByteSize = 7;\r
1498   dcb.Parity = SPACEPARITY;\r
1499   dcb.StopBits = ONESTOPBIT;\r
1500 }\r
1501 \r
1502 // [HGM] args: these three cases taken out to stay in front-end\r
1503 void\r
1504 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1505 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1506         // while the curent board size determines the element. This system should be ported to XBoard.\r
1507         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1508         int bs;\r
1509         for (bs=0; bs<NUM_SIZES; bs++) {\r
1510           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1511           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1512           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1513             ad->argName, mfp->faceName, mfp->pointSize,\r
1514             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1515             mfp->bold ? "b" : "",\r
1516             mfp->italic ? "i" : "",\r
1517             mfp->underline ? "u" : "",\r
1518             mfp->strikeout ? "s" : "",\r
1519             (int)mfp->charset);\r
1520         }\r
1521       }\r
1522 \r
1523 void\r
1524 ExportSounds()\r
1525 { // [HGM] copy the names from the internal WB variables to appData\r
1526   ColorClass cc;\r
1527   SoundClass sc;\r
1528   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1529     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1530   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1531     (&appData.soundMove)[sc] = sounds[sc].name;\r
1532 }\r
1533 \r
1534 void\r
1535 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1536 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1537         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1538         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1539           (ta->effects & CFE_BOLD) ? "b" : "",\r
1540           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1541           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1542           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1543           (ta->effects) ? " " : "",\r
1544           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1545       }\r
1546 \r
1547 void\r
1548 SaveColor(FILE *f, ArgDescriptor *ad)\r
1549 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1550         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1551         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1552           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1553 }\r
1554 \r
1555 void\r
1556 SaveBoardSize(FILE *f, char *name, void *addr)\r
1557 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1558   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1559 }\r
1560 \r
1561 void\r
1562 ParseCommPortSettings(char *s)\r
1563 { // wrapper to keep dcb from back-end\r
1564   ParseCommSettings(s, &dcb);\r
1565 }\r
1566 \r
1567 void\r
1568 GetWindowCoords()\r
1569 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1570   GetActualPlacement(hwndMain, &wpMain);\r
1571   GetActualPlacement(hwndConsole, &wpConsole);\r
1572   GetActualPlacement(commentDialog, &wpComment);\r
1573   GetActualPlacement(editTagsDialog, &wpTags);\r
1574   GetActualPlacement(gameListDialog, &wpGameList);\r
1575   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1576   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1577   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1578 }\r
1579 \r
1580 void\r
1581 PrintCommPortSettings(FILE *f, char *name)\r
1582 { // wrapper to shield back-end from DCB\r
1583       PrintCommSettings(f, name, &dcb);\r
1584 }\r
1585 \r
1586 int\r
1587 MySearchPath(char *installDir, char *name, char *fullname)\r
1588 {\r
1589   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1590   if(name[0]== '%') {\r
1591     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1592     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1593       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1594       *strchr(buf, '%') = 0;\r
1595       strcat(fullname, getenv(buf));\r
1596       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1597     }\r
1598     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1599     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1600     return (int) strlen(fullname);\r
1601   }\r
1602   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1603 }\r
1604 \r
1605 int\r
1606 MyGetFullPathName(char *name, char *fullname)\r
1607 {\r
1608   char *dummy;\r
1609   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1610 }\r
1611 \r
1612 int\r
1613 MainWindowUp()\r
1614 { // [HGM] args: allows testing if main window is realized from back-end\r
1615   return hwndMain != NULL;\r
1616 }\r
1617 \r
1618 void\r
1619 PopUpStartupDialog()\r
1620 {\r
1621     FARPROC lpProc;\r
1622     \r
1623     LoadLanguageFile(appData.language);\r
1624     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1625     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1626     FreeProcInstance(lpProc);\r
1627 }\r
1628 \r
1629 /*---------------------------------------------------------------------------*\\r
1630  *\r
1631  * GDI board drawing routines\r
1632  *\r
1633 \*---------------------------------------------------------------------------*/\r
1634 \r
1635 /* [AS] Draw square using background texture */\r
1636 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1637 {\r
1638     XFORM   x;\r
1639 \r
1640     if( mode == 0 ) {\r
1641         return; /* Should never happen! */\r
1642     }\r
1643 \r
1644     SetGraphicsMode( dst, GM_ADVANCED );\r
1645 \r
1646     switch( mode ) {\r
1647     case 1:\r
1648         /* Identity */\r
1649         break;\r
1650     case 2:\r
1651         /* X reflection */\r
1652         x.eM11 = -1.0;\r
1653         x.eM12 = 0;\r
1654         x.eM21 = 0;\r
1655         x.eM22 = 1.0;\r
1656         x.eDx = (FLOAT) dw + dx - 1;\r
1657         x.eDy = 0;\r
1658         dx = 0;\r
1659         SetWorldTransform( dst, &x );\r
1660         break;\r
1661     case 3:\r
1662         /* Y reflection */\r
1663         x.eM11 = 1.0;\r
1664         x.eM12 = 0;\r
1665         x.eM21 = 0;\r
1666         x.eM22 = -1.0;\r
1667         x.eDx = 0;\r
1668         x.eDy = (FLOAT) dh + dy - 1;\r
1669         dy = 0;\r
1670         SetWorldTransform( dst, &x );\r
1671         break;\r
1672     case 4:\r
1673         /* X/Y flip */\r
1674         x.eM11 = 0;\r
1675         x.eM12 = 1.0;\r
1676         x.eM21 = 1.0;\r
1677         x.eM22 = 0;\r
1678         x.eDx = (FLOAT) dx;\r
1679         x.eDy = (FLOAT) dy;\r
1680         dx = 0;\r
1681         dy = 0;\r
1682         SetWorldTransform( dst, &x );\r
1683         break;\r
1684     }\r
1685 \r
1686     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1687 \r
1688     x.eM11 = 1.0;\r
1689     x.eM12 = 0;\r
1690     x.eM21 = 0;\r
1691     x.eM22 = 1.0;\r
1692     x.eDx = 0;\r
1693     x.eDy = 0;\r
1694     SetWorldTransform( dst, &x );\r
1695 \r
1696     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1697 }\r
1698 \r
1699 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1700 enum {\r
1701     PM_WP = (int) WhitePawn, \r
1702     PM_WN = (int) WhiteKnight, \r
1703     PM_WB = (int) WhiteBishop, \r
1704     PM_WR = (int) WhiteRook, \r
1705     PM_WQ = (int) WhiteQueen, \r
1706     PM_WF = (int) WhiteFerz, \r
1707     PM_WW = (int) WhiteWazir, \r
1708     PM_WE = (int) WhiteAlfil, \r
1709     PM_WM = (int) WhiteMan, \r
1710     PM_WO = (int) WhiteCannon, \r
1711     PM_WU = (int) WhiteUnicorn, \r
1712     PM_WH = (int) WhiteNightrider, \r
1713     PM_WA = (int) WhiteAngel, \r
1714     PM_WC = (int) WhiteMarshall, \r
1715     PM_WAB = (int) WhiteCardinal, \r
1716     PM_WD = (int) WhiteDragon, \r
1717     PM_WL = (int) WhiteLance, \r
1718     PM_WS = (int) WhiteCobra, \r
1719     PM_WV = (int) WhiteFalcon, \r
1720     PM_WSG = (int) WhiteSilver, \r
1721     PM_WG = (int) WhiteGrasshopper, \r
1722     PM_WK = (int) WhiteKing,\r
1723     PM_BP = (int) BlackPawn, \r
1724     PM_BN = (int) BlackKnight, \r
1725     PM_BB = (int) BlackBishop, \r
1726     PM_BR = (int) BlackRook, \r
1727     PM_BQ = (int) BlackQueen, \r
1728     PM_BF = (int) BlackFerz, \r
1729     PM_BW = (int) BlackWazir, \r
1730     PM_BE = (int) BlackAlfil, \r
1731     PM_BM = (int) BlackMan,\r
1732     PM_BO = (int) BlackCannon, \r
1733     PM_BU = (int) BlackUnicorn, \r
1734     PM_BH = (int) BlackNightrider, \r
1735     PM_BA = (int) BlackAngel, \r
1736     PM_BC = (int) BlackMarshall, \r
1737     PM_BG = (int) BlackGrasshopper, \r
1738     PM_BAB = (int) BlackCardinal,\r
1739     PM_BD = (int) BlackDragon,\r
1740     PM_BL = (int) BlackLance,\r
1741     PM_BS = (int) BlackCobra,\r
1742     PM_BV = (int) BlackFalcon,\r
1743     PM_BSG = (int) BlackSilver,\r
1744     PM_BK = (int) BlackKing\r
1745 };\r
1746 \r
1747 static HFONT hPieceFont = NULL;\r
1748 static HBITMAP hPieceMask[(int) EmptySquare];\r
1749 static HBITMAP hPieceFace[(int) EmptySquare];\r
1750 static int fontBitmapSquareSize = 0;\r
1751 static char pieceToFontChar[(int) EmptySquare] =\r
1752                               { 'p', 'n', 'b', 'r', 'q', \r
1753                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1754                       'k', 'o', 'm', 'v', 't', 'w', \r
1755                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1756                                                               'l' };\r
1757 \r
1758 extern BOOL SetCharTable( char *table, const char * map );\r
1759 /* [HGM] moved to backend.c */\r
1760 \r
1761 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1762 {\r
1763     HBRUSH hbrush;\r
1764     BYTE r1 = GetRValue( color );\r
1765     BYTE g1 = GetGValue( color );\r
1766     BYTE b1 = GetBValue( color );\r
1767     BYTE r2 = r1 / 2;\r
1768     BYTE g2 = g1 / 2;\r
1769     BYTE b2 = b1 / 2;\r
1770     RECT rc;\r
1771 \r
1772     /* Create a uniform background first */\r
1773     hbrush = CreateSolidBrush( color );\r
1774     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1775     FillRect( hdc, &rc, hbrush );\r
1776     DeleteObject( hbrush );\r
1777     \r
1778     if( mode == 1 ) {\r
1779         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1780         int steps = squareSize / 2;\r
1781         int i;\r
1782 \r
1783         for( i=0; i<steps; i++ ) {\r
1784             BYTE r = r1 - (r1-r2) * i / steps;\r
1785             BYTE g = g1 - (g1-g2) * i / steps;\r
1786             BYTE b = b1 - (b1-b2) * i / steps;\r
1787 \r
1788             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1789             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1790             FillRect( hdc, &rc, hbrush );\r
1791             DeleteObject(hbrush);\r
1792         }\r
1793     }\r
1794     else if( mode == 2 ) {\r
1795         /* Diagonal gradient, good more or less for every piece */\r
1796         POINT triangle[3];\r
1797         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1798         HBRUSH hbrush_old;\r
1799         int steps = squareSize;\r
1800         int i;\r
1801 \r
1802         triangle[0].x = squareSize - steps;\r
1803         triangle[0].y = squareSize;\r
1804         triangle[1].x = squareSize;\r
1805         triangle[1].y = squareSize;\r
1806         triangle[2].x = squareSize;\r
1807         triangle[2].y = squareSize - steps;\r
1808 \r
1809         for( i=0; i<steps; i++ ) {\r
1810             BYTE r = r1 - (r1-r2) * i / steps;\r
1811             BYTE g = g1 - (g1-g2) * i / steps;\r
1812             BYTE b = b1 - (b1-b2) * i / steps;\r
1813 \r
1814             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1815             hbrush_old = SelectObject( hdc, hbrush );\r
1816             Polygon( hdc, triangle, 3 );\r
1817             SelectObject( hdc, hbrush_old );\r
1818             DeleteObject(hbrush);\r
1819             triangle[0].x++;\r
1820             triangle[2].y++;\r
1821         }\r
1822 \r
1823         SelectObject( hdc, hpen );\r
1824     }\r
1825 }\r
1826 \r
1827 /*\r
1828     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1829     seems to work ok. The main problem here is to find the "inside" of a chess\r
1830     piece: follow the steps as explained below.\r
1831 */\r
1832 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1833 {\r
1834     HBITMAP hbm;\r
1835     HBITMAP hbm_old;\r
1836     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1837     RECT rc;\r
1838     SIZE sz;\r
1839 \r
1840 \r
1841     POINT pt;\r
1842     int backColor = whitePieceColor; \r
1843     int foreColor = blackPieceColor;\r
1844     \r
1845     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1846         backColor = appData.fontBackColorWhite;\r
1847         foreColor = appData.fontForeColorWhite;\r
1848     }\r
1849     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1850         backColor = appData.fontBackColorBlack;\r
1851         foreColor = appData.fontForeColorBlack;\r
1852     }\r
1853 \r
1854     /* Mask */\r
1855     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1856 \r
1857     hbm_old = SelectObject( hdc, hbm );\r
1858 \r
1859     rc.left = 0;\r
1860     rc.top = 0;\r
1861     rc.right = squareSize;\r
1862     rc.bottom = squareSize;\r
1863 \r
1864     /* Step 1: background is now black */\r
1865     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1866 \r
1867     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1868 \r
1869     pt.x = (squareSize - sz.cx) / 2;\r
1870     pt.y = (squareSize - sz.cy) / 2;\r
1871 \r
1872     SetBkMode( hdc, TRANSPARENT );\r
1873     SetTextColor( hdc, chroma );\r
1874     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1875     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1876 \r
1877     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1878     /* Step 3: the area outside the piece is filled with white */\r
1879 //    FloodFill( hdc, 0, 0, chroma );\r
1880     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1881     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1882     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1883     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1884     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1885     /* \r
1886         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1887         but if the start point is not inside the piece we're lost!\r
1888         There should be a better way to do this... if we could create a region or path\r
1889         from the fill operation we would be fine for example.\r
1890     */\r
1891 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1892     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1893 \r
1894     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1895         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1896         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1897 \r
1898         SelectObject( dc2, bm2 );\r
1899         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1900         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1901         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1902         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1903         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1904 \r
1905         DeleteDC( dc2 );\r
1906         DeleteObject( bm2 );\r
1907     }\r
1908 \r
1909     SetTextColor( hdc, 0 );\r
1910     /* \r
1911         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1912         draw the piece again in black for safety.\r
1913     */\r
1914     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1915 \r
1916     SelectObject( hdc, hbm_old );\r
1917 \r
1918     if( hPieceMask[index] != NULL ) {\r
1919         DeleteObject( hPieceMask[index] );\r
1920     }\r
1921 \r
1922     hPieceMask[index] = hbm;\r
1923 \r
1924     /* Face */\r
1925     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1926 \r
1927     SelectObject( hdc, hbm );\r
1928 \r
1929     {\r
1930         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1931         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1932         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1933 \r
1934         SelectObject( dc1, hPieceMask[index] );\r
1935         SelectObject( dc2, bm2 );\r
1936         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1937         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1938         \r
1939         /* \r
1940             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1941             the piece background and deletes (makes transparent) the rest.\r
1942             Thanks to that mask, we are free to paint the background with the greates\r
1943             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1944             We use this, to make gradients and give the pieces a "roundish" look.\r
1945         */\r
1946         SetPieceBackground( hdc, backColor, 2 );\r
1947         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1948 \r
1949         DeleteDC( dc2 );\r
1950         DeleteDC( dc1 );\r
1951         DeleteObject( bm2 );\r
1952     }\r
1953 \r
1954     SetTextColor( hdc, foreColor );\r
1955     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1956 \r
1957     SelectObject( hdc, hbm_old );\r
1958 \r
1959     if( hPieceFace[index] != NULL ) {\r
1960         DeleteObject( hPieceFace[index] );\r
1961     }\r
1962 \r
1963     hPieceFace[index] = hbm;\r
1964 }\r
1965 \r
1966 static int TranslatePieceToFontPiece( int piece )\r
1967 {\r
1968     switch( piece ) {\r
1969     case BlackPawn:\r
1970         return PM_BP;\r
1971     case BlackKnight:\r
1972         return PM_BN;\r
1973     case BlackBishop:\r
1974         return PM_BB;\r
1975     case BlackRook:\r
1976         return PM_BR;\r
1977     case BlackQueen:\r
1978         return PM_BQ;\r
1979     case BlackKing:\r
1980         return PM_BK;\r
1981     case WhitePawn:\r
1982         return PM_WP;\r
1983     case WhiteKnight:\r
1984         return PM_WN;\r
1985     case WhiteBishop:\r
1986         return PM_WB;\r
1987     case WhiteRook:\r
1988         return PM_WR;\r
1989     case WhiteQueen:\r
1990         return PM_WQ;\r
1991     case WhiteKing:\r
1992         return PM_WK;\r
1993 \r
1994     case BlackAngel:\r
1995         return PM_BA;\r
1996     case BlackMarshall:\r
1997         return PM_BC;\r
1998     case BlackFerz:\r
1999         return PM_BF;\r
2000     case BlackNightrider:\r
2001         return PM_BH;\r
2002     case BlackAlfil:\r
2003         return PM_BE;\r
2004     case BlackWazir:\r
2005         return PM_BW;\r
2006     case BlackUnicorn:\r
2007         return PM_BU;\r
2008     case BlackCannon:\r
2009         return PM_BO;\r
2010     case BlackGrasshopper:\r
2011         return PM_BG;\r
2012     case BlackMan:\r
2013         return PM_BM;\r
2014     case BlackSilver:\r
2015         return PM_BSG;\r
2016     case BlackLance:\r
2017         return PM_BL;\r
2018     case BlackFalcon:\r
2019         return PM_BV;\r
2020     case BlackCobra:\r
2021         return PM_BS;\r
2022     case BlackCardinal:\r
2023         return PM_BAB;\r
2024     case BlackDragon:\r
2025         return PM_BD;\r
2026 \r
2027     case WhiteAngel:\r
2028         return PM_WA;\r
2029     case WhiteMarshall:\r
2030         return PM_WC;\r
2031     case WhiteFerz:\r
2032         return PM_WF;\r
2033     case WhiteNightrider:\r
2034         return PM_WH;\r
2035     case WhiteAlfil:\r
2036         return PM_WE;\r
2037     case WhiteWazir:\r
2038         return PM_WW;\r
2039     case WhiteUnicorn:\r
2040         return PM_WU;\r
2041     case WhiteCannon:\r
2042         return PM_WO;\r
2043     case WhiteGrasshopper:\r
2044         return PM_WG;\r
2045     case WhiteMan:\r
2046         return PM_WM;\r
2047     case WhiteSilver:\r
2048         return PM_WSG;\r
2049     case WhiteLance:\r
2050         return PM_WL;\r
2051     case WhiteFalcon:\r
2052         return PM_WV;\r
2053     case WhiteCobra:\r
2054         return PM_WS;\r
2055     case WhiteCardinal:\r
2056         return PM_WAB;\r
2057     case WhiteDragon:\r
2058         return PM_WD;\r
2059     }\r
2060 \r
2061     return 0;\r
2062 }\r
2063 \r
2064 void CreatePiecesFromFont()\r
2065 {\r
2066     LOGFONT lf;\r
2067     HDC hdc_window = NULL;\r
2068     HDC hdc = NULL;\r
2069     HFONT hfont_old;\r
2070     int fontHeight;\r
2071     int i;\r
2072 \r
2073     if( fontBitmapSquareSize < 0 ) {\r
2074         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2075         return;\r
2076     }\r
2077 \r
2078     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2079             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2080         fontBitmapSquareSize = -1;\r
2081         return;\r
2082     }\r
2083 \r
2084     if( fontBitmapSquareSize != squareSize ) {\r
2085         hdc_window = GetDC( hwndMain );\r
2086         hdc = CreateCompatibleDC( hdc_window );\r
2087 \r
2088         if( hPieceFont != NULL ) {\r
2089             DeleteObject( hPieceFont );\r
2090         }\r
2091         else {\r
2092             for( i=0; i<=(int)BlackKing; i++ ) {\r
2093                 hPieceMask[i] = NULL;\r
2094                 hPieceFace[i] = NULL;\r
2095             }\r
2096         }\r
2097 \r
2098         fontHeight = 75;\r
2099 \r
2100         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2101             fontHeight = appData.fontPieceSize;\r
2102         }\r
2103 \r
2104         fontHeight = (fontHeight * squareSize) / 100;\r
2105 \r
2106         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2107         lf.lfWidth = 0;\r
2108         lf.lfEscapement = 0;\r
2109         lf.lfOrientation = 0;\r
2110         lf.lfWeight = FW_NORMAL;\r
2111         lf.lfItalic = 0;\r
2112         lf.lfUnderline = 0;\r
2113         lf.lfStrikeOut = 0;\r
2114         lf.lfCharSet = DEFAULT_CHARSET;\r
2115         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2116         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2117         lf.lfQuality = PROOF_QUALITY;\r
2118         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2119         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2120         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2121 \r
2122         hPieceFont = CreateFontIndirect( &lf );\r
2123 \r
2124         if( hPieceFont == NULL ) {\r
2125             fontBitmapSquareSize = -2;\r
2126         }\r
2127         else {\r
2128             /* Setup font-to-piece character table */\r
2129             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2130                 /* No (or wrong) global settings, try to detect the font */\r
2131                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2132                     /* Alpha */\r
2133                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2134                 }\r
2135                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2136                     /* DiagramTT* family */\r
2137                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2138                 }\r
2139                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2140                     /* Fairy symbols */\r
2141                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2142                 }\r
2143                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2144                     /* Good Companion (Some characters get warped as literal :-( */\r
2145                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2146                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2147                     SetCharTable(pieceToFontChar, s);\r
2148                 }\r
2149                 else {\r
2150                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2151                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2152                 }\r
2153             }\r
2154 \r
2155             /* Create bitmaps */\r
2156             hfont_old = SelectObject( hdc, hPieceFont );\r
2157             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2158                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2159                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2160 \r
2161             SelectObject( hdc, hfont_old );\r
2162 \r
2163             fontBitmapSquareSize = squareSize;\r
2164         }\r
2165     }\r
2166 \r
2167     if( hdc != NULL ) {\r
2168         DeleteDC( hdc );\r
2169     }\r
2170 \r
2171     if( hdc_window != NULL ) {\r
2172         ReleaseDC( hwndMain, hdc_window );\r
2173     }\r
2174 }\r
2175 \r
2176 HBITMAP\r
2177 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2178 {\r
2179   char name[128], buf[MSG_SIZ];\r
2180 \r
2181     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2182   if(appData.pieceDirectory[0]) {\r
2183     HBITMAP res;\r
2184     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2185     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2186     if(res) return res;\r
2187   }\r
2188   if (gameInfo.event &&\r
2189       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2190       strcmp(name, "k80s") == 0) {\r
2191     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2192   }\r
2193   return LoadBitmap(hinst, name);\r
2194 }\r
2195 \r
2196 \r
2197 /* Insert a color into the program's logical palette\r
2198    structure.  This code assumes the given color is\r
2199    the result of the RGB or PALETTERGB macro, and it\r
2200    knows how those macros work (which is documented).\r
2201 */\r
2202 VOID\r
2203 InsertInPalette(COLORREF color)\r
2204 {\r
2205   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2206 \r
2207   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2208     DisplayFatalError(_("Too many colors"), 0, 1);\r
2209     pLogPal->palNumEntries--;\r
2210     return;\r
2211   }\r
2212 \r
2213   pe->peFlags = (char) 0;\r
2214   pe->peRed = (char) (0xFF & color);\r
2215   pe->peGreen = (char) (0xFF & (color >> 8));\r
2216   pe->peBlue = (char) (0xFF & (color >> 16));\r
2217   return;\r
2218 }\r
2219 \r
2220 \r
2221 VOID\r
2222 InitDrawingColors()\r
2223 {\r
2224   int i;\r
2225   if (pLogPal == NULL) {\r
2226     /* Allocate enough memory for a logical palette with\r
2227      * PALETTESIZE entries and set the size and version fields\r
2228      * of the logical palette structure.\r
2229      */\r
2230     pLogPal = (NPLOGPALETTE)\r
2231       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2232                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2233     pLogPal->palVersion    = 0x300;\r
2234   }\r
2235   pLogPal->palNumEntries = 0;\r
2236 \r
2237   InsertInPalette(lightSquareColor);\r
2238   InsertInPalette(darkSquareColor);\r
2239   InsertInPalette(whitePieceColor);\r
2240   InsertInPalette(blackPieceColor);\r
2241   InsertInPalette(highlightSquareColor);\r
2242   InsertInPalette(premoveHighlightColor);\r
2243 \r
2244   /*  create a logical color palette according the information\r
2245    *  in the LOGPALETTE structure.\r
2246    */\r
2247   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2248 \r
2249   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2250   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2251   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2252   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2253   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2254   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2255   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2256     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2257 \r
2258    /* [AS] Force rendering of the font-based pieces */\r
2259   if( fontBitmapSquareSize > 0 ) {\r
2260     fontBitmapSquareSize = 0;\r
2261   }\r
2262 }\r
2263 \r
2264 \r
2265 int\r
2266 BoardWidth(int boardSize, int n)\r
2267 { /* [HGM] argument n added to allow different width and height */\r
2268   int lineGap = sizeInfo[boardSize].lineGap;\r
2269 \r
2270   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2271       lineGap = appData.overrideLineGap;\r
2272   }\r
2273 \r
2274   return (n + 1) * lineGap +\r
2275           n * sizeInfo[boardSize].squareSize;\r
2276 }\r
2277 \r
2278 /* Respond to board resize by dragging edge */\r
2279 VOID\r
2280 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2281 {\r
2282   BoardSize newSize = NUM_SIZES - 1;\r
2283   static int recurse = 0;\r
2284   if (IsIconic(hwndMain)) return;\r
2285   if (recurse > 0) return;\r
2286   recurse++;\r
2287   while (newSize > 0) {\r
2288         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2289         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2290            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2291     newSize--;\r
2292   } \r
2293   boardSize = newSize;\r
2294   InitDrawingSizes(boardSize, flags);\r
2295   recurse--;\r
2296 }\r
2297 \r
2298 \r
2299 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2300 \r
2301 VOID\r
2302 InitDrawingSizes(BoardSize boardSize, int flags)\r
2303 {\r
2304   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2305   ChessSquare piece;\r
2306   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2307   HDC hdc;\r
2308   SIZE clockSize, messageSize;\r
2309   HFONT oldFont;\r
2310   char buf[MSG_SIZ];\r
2311   char *str;\r
2312   HMENU hmenu = GetMenu(hwndMain);\r
2313   RECT crect, wrect, oldRect;\r
2314   int offby;\r
2315   LOGBRUSH logbrush;\r
2316   VariantClass v = gameInfo.variant;\r
2317 \r
2318   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2319   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2320 \r
2321   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2322   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2323   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2324   oldBoardSize = boardSize;\r
2325 \r
2326   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2327   { // correct board size to one where built-in pieces exist\r
2328     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2329        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2330 \r
2331       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2332       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2333       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2334       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2335       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2336                                    boardSize = SizeMiddling;\r
2337     }\r
2338   }\r
2339   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2340 \r
2341   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2342   oldRect.top = wpMain.y;\r
2343   oldRect.right = wpMain.x + wpMain.width;\r
2344   oldRect.bottom = wpMain.y + wpMain.height;\r
2345 \r
2346   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2347   smallLayout = sizeInfo[boardSize].smallLayout;\r
2348   squareSize = sizeInfo[boardSize].squareSize;\r
2349   lineGap = sizeInfo[boardSize].lineGap;\r
2350   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2351   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2352 \r
2353   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2354       lineGap = appData.overrideLineGap;\r
2355   }\r
2356 \r
2357   if (tinyLayout != oldTinyLayout) {\r
2358     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2359     if (tinyLayout) {\r
2360       style &= ~WS_SYSMENU;\r
2361       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2362                  "&Minimize\tCtrl+F4");\r
2363     } else {\r
2364       style |= WS_SYSMENU;\r
2365       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2366     }\r
2367     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2368 \r
2369     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2370       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2371         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2372     }\r
2373     DrawMenuBar(hwndMain);\r
2374   }\r
2375 \r
2376   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2377   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2378 \r
2379   /* Get text area sizes */\r
2380   hdc = GetDC(hwndMain);\r
2381   if (appData.clockMode) {\r
2382     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2383   } else {\r
2384     snprintf(buf, MSG_SIZ, _("White"));\r
2385   }\r
2386   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2387   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2388   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2389   str = _("We only care about the height here");\r
2390   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2391   SelectObject(hdc, oldFont);\r
2392   ReleaseDC(hwndMain, hdc);\r
2393 \r
2394   /* Compute where everything goes */\r
2395   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2396         /* [HGM] logo: if either logo is on, reserve space for it */\r
2397         logoHeight =  2*clockSize.cy;\r
2398         leftLogoRect.left   = OUTER_MARGIN;\r
2399         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2400         leftLogoRect.top    = OUTER_MARGIN;\r
2401         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2402 \r
2403         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2404         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2405         rightLogoRect.top    = OUTER_MARGIN;\r
2406         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2407 \r
2408 \r
2409     whiteRect.left = leftLogoRect.right;\r
2410     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2411     whiteRect.top = OUTER_MARGIN;\r
2412     whiteRect.bottom = whiteRect.top + logoHeight;\r
2413 \r
2414     blackRect.right = rightLogoRect.left;\r
2415     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2416     blackRect.top = whiteRect.top;\r
2417     blackRect.bottom = whiteRect.bottom;\r
2418   } else {\r
2419     whiteRect.left = OUTER_MARGIN;\r
2420     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2421     whiteRect.top = OUTER_MARGIN;\r
2422     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2423 \r
2424     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2425     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2426     blackRect.top = whiteRect.top;\r
2427     blackRect.bottom = whiteRect.bottom;\r
2428 \r
2429     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2430   }\r
2431 \r
2432   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2433   if (appData.showButtonBar) {\r
2434     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2435       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2436   } else {\r
2437     messageRect.right = OUTER_MARGIN + boardWidth;\r
2438   }\r
2439   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2440   messageRect.bottom = messageRect.top + messageSize.cy;\r
2441 \r
2442   boardRect.left = OUTER_MARGIN;\r
2443   boardRect.right = boardRect.left + boardWidth;\r
2444   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2445   boardRect.bottom = boardRect.top + boardHeight;\r
2446 \r
2447   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2448   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2449   oldTinyLayout = tinyLayout;\r
2450   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2451   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2452     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2453   winW *= 1 + twoBoards;\r
2454   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2455   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2456   wpMain.height = winH; //       without disturbing window attachments\r
2457   GetWindowRect(hwndMain, &wrect);\r
2458   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2459                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2460 \r
2461   // [HGM] placement: let attached windows follow size change.\r
2462   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2463   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2464   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2465   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2466   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2467 \r
2468   /* compensate if menu bar wrapped */\r
2469   GetClientRect(hwndMain, &crect);\r
2470   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2471   wpMain.height += offby;\r
2472   switch (flags) {\r
2473   case WMSZ_TOPLEFT:\r
2474     SetWindowPos(hwndMain, NULL, \r
2475                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2476                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2477     break;\r
2478 \r
2479   case WMSZ_TOPRIGHT:\r
2480   case WMSZ_TOP:\r
2481     SetWindowPos(hwndMain, NULL, \r
2482                  wrect.left, wrect.bottom - wpMain.height, \r
2483                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2484     break;\r
2485 \r
2486   case WMSZ_BOTTOMLEFT:\r
2487   case WMSZ_LEFT:\r
2488     SetWindowPos(hwndMain, NULL, \r
2489                  wrect.right - wpMain.width, wrect.top, \r
2490                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2491     break;\r
2492 \r
2493   case WMSZ_BOTTOMRIGHT:\r
2494   case WMSZ_BOTTOM:\r
2495   case WMSZ_RIGHT:\r
2496   default:\r
2497     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2498                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2499     break;\r
2500   }\r
2501 \r
2502   hwndPause = NULL;\r
2503   for (i = 0; i < N_BUTTONS; i++) {\r
2504     if (buttonDesc[i].hwnd != NULL) {\r
2505       DestroyWindow(buttonDesc[i].hwnd);\r
2506       buttonDesc[i].hwnd = NULL;\r
2507     }\r
2508     if (appData.showButtonBar) {\r
2509       buttonDesc[i].hwnd =\r
2510         CreateWindow("BUTTON", buttonDesc[i].label,\r
2511                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2512                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2513                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2514                      (HMENU) buttonDesc[i].id,\r
2515                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2516       if (tinyLayout) {\r
2517         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2518                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2519                     MAKELPARAM(FALSE, 0));\r
2520       }\r
2521       if (buttonDesc[i].id == IDM_Pause)\r
2522         hwndPause = buttonDesc[i].hwnd;\r
2523       buttonDesc[i].wndproc = (WNDPROC)\r
2524         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2525     }\r
2526   }\r
2527   if (gridPen != NULL) DeleteObject(gridPen);\r
2528   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2529   if (premovePen != NULL) DeleteObject(premovePen);\r
2530   if (lineGap != 0) {\r
2531     logbrush.lbStyle = BS_SOLID;\r
2532     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2533     gridPen =\r
2534       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2535                    lineGap, &logbrush, 0, NULL);\r
2536     logbrush.lbColor = highlightSquareColor;\r
2537     highlightPen =\r
2538       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2539                    lineGap, &logbrush, 0, NULL);\r
2540 \r
2541     logbrush.lbColor = premoveHighlightColor; \r
2542     premovePen =\r
2543       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2544                    lineGap, &logbrush, 0, NULL);\r
2545 \r
2546     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2547     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2548       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2549       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2550         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2551       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2552         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2553       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2554     }\r
2555     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2556       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2557       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2558         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2559         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2560       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2561         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2562       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2563     }\r
2564   }\r
2565 \r
2566   /* [HGM] Licensing requirement */\r
2567 #ifdef GOTHIC\r
2568   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2569 #endif\r
2570 #ifdef FALCON\r
2571   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2572 #endif\r
2573   GothicPopUp( "", VariantNormal);\r
2574 \r
2575 \r
2576 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2577 \r
2578   /* Load piece bitmaps for this board size */\r
2579   for (i=0; i<=2; i++) {\r
2580     for (piece = WhitePawn;\r
2581          (int) piece < (int) BlackPawn;\r
2582          piece = (ChessSquare) ((int) piece + 1)) {\r
2583       if (pieceBitmap[i][piece] != NULL)\r
2584         DeleteObject(pieceBitmap[i][piece]);\r
2585     }\r
2586   }\r
2587 \r
2588   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2589   // Orthodox Chess pieces\r
2590   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2591   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2592   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2593   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2594   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2595   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2596   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2597   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2598   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2599   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2600   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2601   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2602   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2603   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2604   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2605   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2606     // in Shogi, Hijack the unused Queen for Lance\r
2607     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2608     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2609     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2610   } else {\r
2611     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2612     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2613     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2614   }\r
2615 \r
2616   if(squareSize <= 72 && squareSize >= 33) { \r
2617     /* A & C are available in most sizes now */\r
2618     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2619       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2620       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2621       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2622       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2623       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2624       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2625       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2626       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2627       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2628       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2629       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2630       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2631     } else { // Smirf-like\r
2632       if(gameInfo.variant == VariantSChess) {\r
2633         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2634         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2635         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2636       } else {\r
2637         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2638         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2639         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2640       }\r
2641     }\r
2642     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2643       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2644       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2645       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2646     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2647       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2648       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2649       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2650     } else { // WinBoard standard\r
2651       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2652       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2653       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2654     }\r
2655   }\r
2656 \r
2657 \r
2658   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2659     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2660     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2661     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2662     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2663     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2664     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2665     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2666     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2667     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2668     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2669     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2670     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2671     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2672     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2673     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2674     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2675     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2676     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2677     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2678     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2679     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2680     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2681     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2682     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2683     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2684     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2685     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2686     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2687     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2688     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2689     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2690     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2691     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2692 \r
2693     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2694       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2695       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2696       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2697       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2698       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2699       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2700       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2701       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2702       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2703       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2704       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2705       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2706     } else {\r
2707       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2708       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2709       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2710       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2711       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2712       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2713       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2714       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2715       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2716       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2717       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2718       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2719     }\r
2720 \r
2721   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2722     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2723     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2724     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2725     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2726     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2727     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2728     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2729     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2730     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2731     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2732     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2733     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2734     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2735     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2736   }\r
2737 \r
2738 \r
2739   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2740   /* special Shogi support in this size */\r
2741   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2742       for (piece = WhitePawn;\r
2743            (int) piece < (int) BlackPawn;\r
2744            piece = (ChessSquare) ((int) piece + 1)) {\r
2745         if (pieceBitmap[i][piece] != NULL)\r
2746           DeleteObject(pieceBitmap[i][piece]);\r
2747       }\r
2748     }\r
2749   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2750   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2751   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2752   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2753   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2754   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2755   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2756   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2757   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2758   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2759   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2760   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2761   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2762   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2763   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2764   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2765   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2766   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2767   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2768   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2769   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2770   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2771   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2772   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2773   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2774   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2775   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2776   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2777   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2778   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2779   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2780   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2781   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2782   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2783   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2784   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2785   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2786   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2787   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2788   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2789   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2790   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2791   minorSize = 0;\r
2792   }\r
2793 }\r
2794 \r
2795 HBITMAP\r
2796 PieceBitmap(ChessSquare p, int kind)\r
2797 {\r
2798   if ((int) p >= (int) BlackPawn)\r
2799     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2800 \r
2801   return pieceBitmap[kind][(int) p];\r
2802 }\r
2803 \r
2804 /***************************************************************/\r
2805 \r
2806 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2807 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2808 /*\r
2809 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2810 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2811 */\r
2812 \r
2813 VOID\r
2814 SquareToPos(int row, int column, int * x, int * y)\r
2815 {\r
2816   if (flipView) {\r
2817     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2818     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2819   } else {\r
2820     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2821     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2822   }\r
2823 }\r
2824 \r
2825 VOID\r
2826 DrawCoordsOnDC(HDC hdc)\r
2827 {\r
2828   static char files[] = "0123456789012345678901221098765432109876543210";\r
2829   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2830   char str[2] = { NULLCHAR, NULLCHAR };\r
2831   int oldMode, oldAlign, x, y, start, i;\r
2832   HFONT oldFont;\r
2833   HBRUSH oldBrush;\r
2834 \r
2835   if (!appData.showCoords)\r
2836     return;\r
2837 \r
2838   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2839 \r
2840   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2841   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2842   oldAlign = GetTextAlign(hdc);\r
2843   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2844 \r
2845   y = boardRect.top + lineGap;\r
2846   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2847 \r
2848   if(border) {\r
2849     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2850     x += border - lineGap - 4; y += squareSize - 6;\r
2851   } else\r
2852   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2853   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2854     str[0] = files[start + i];\r
2855     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2856     y += squareSize + lineGap;\r
2857   }\r
2858 \r
2859   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2860 \r
2861   if(border) {\r
2862     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2863     x += -border + 4; y += border - squareSize + 6;\r
2864   } else\r
2865   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2866   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2867     str[0] = ranks[start + i];\r
2868     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2869     x += squareSize + lineGap;\r
2870   }    \r
2871 \r
2872   SelectObject(hdc, oldBrush);\r
2873   SetBkMode(hdc, oldMode);\r
2874   SetTextAlign(hdc, oldAlign);\r
2875   SelectObject(hdc, oldFont);\r
2876 }\r
2877 \r
2878 VOID\r
2879 DrawGridOnDC(HDC hdc)\r
2880 {\r
2881   HPEN oldPen;\r
2882  \r
2883   if (lineGap != 0) {\r
2884     oldPen = SelectObject(hdc, gridPen);\r
2885     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2886     SelectObject(hdc, oldPen);\r
2887   }\r
2888 }\r
2889 \r
2890 #define HIGHLIGHT_PEN 0\r
2891 #define PREMOVE_PEN   1\r
2892 \r
2893 VOID\r
2894 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2895 {\r
2896   int x1, y1;\r
2897   HPEN oldPen, hPen;\r
2898   if (lineGap == 0) return;\r
2899   if (flipView) {\r
2900     x1 = boardRect.left +\r
2901       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2902     y1 = boardRect.top +\r
2903       lineGap/2 + y * (squareSize + lineGap) + border;\r
2904   } else {\r
2905     x1 = boardRect.left +\r
2906       lineGap/2 + x * (squareSize + lineGap) + border;\r
2907     y1 = boardRect.top +\r
2908       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2909   }\r
2910   hPen = pen ? premovePen : highlightPen;\r
2911   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2912   MoveToEx(hdc, x1, y1, NULL);\r
2913   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2914   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2915   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2916   LineTo(hdc, x1, y1);\r
2917   SelectObject(hdc, oldPen);\r
2918 }\r
2919 \r
2920 VOID\r
2921 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2922 {\r
2923   int i;\r
2924   for (i=0; i<2; i++) {\r
2925     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2926       DrawHighlightOnDC(hdc, TRUE,\r
2927                         h->sq[i].x, h->sq[i].y,\r
2928                         pen);\r
2929   }\r
2930 }\r
2931 \r
2932 /* Note: sqcolor is used only in monoMode */\r
2933 /* Note that this code is largely duplicated in woptions.c,\r
2934    function DrawSampleSquare, so that needs to be updated too */\r
2935 VOID\r
2936 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2937 {\r
2938   HBITMAP oldBitmap;\r
2939   HBRUSH oldBrush;\r
2940   int tmpSize;\r
2941 \r
2942   if (appData.blindfold) return;\r
2943 \r
2944   /* [AS] Use font-based pieces if needed */\r
2945   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2946     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2947     CreatePiecesFromFont();\r
2948 \r
2949     if( fontBitmapSquareSize == squareSize ) {\r
2950         int index = TranslatePieceToFontPiece(piece);\r
2951 \r
2952         SelectObject( tmphdc, hPieceMask[ index ] );\r
2953 \r
2954       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2955         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2956       else\r
2957         BitBlt( hdc,\r
2958             x, y,\r
2959             squareSize, squareSize,\r
2960             tmphdc,\r
2961             0, 0,\r
2962             SRCAND );\r
2963 \r
2964         SelectObject( tmphdc, hPieceFace[ index ] );\r
2965 \r
2966       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2967         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2968       else\r
2969         BitBlt( hdc,\r
2970             x, y,\r
2971             squareSize, squareSize,\r
2972             tmphdc,\r
2973             0, 0,\r
2974             SRCPAINT );\r
2975 \r
2976         return;\r
2977     }\r
2978   }\r
2979 \r
2980   if (appData.monoMode) {\r
2981     SelectObject(tmphdc, PieceBitmap(piece, \r
2982       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2983     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2984            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2985   } else {\r
2986     HBRUSH xBrush = whitePieceBrush;\r
2987     tmpSize = squareSize;\r
2988     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2989     if(minorSize &&\r
2990         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2991          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2992       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2993       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2994       x += (squareSize - minorSize)>>1;\r
2995       y += squareSize - minorSize - 2;\r
2996       tmpSize = minorSize;\r
2997     }\r
2998     if (color || appData.allWhite ) {\r
2999       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3000       if( color )\r
3001               oldBrush = SelectObject(hdc, xBrush);\r
3002       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3003       if(appData.upsideDown && color==flipView)\r
3004         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3005       else\r
3006         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3007       /* Use black for outline of white pieces */\r
3008       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3009       if(appData.upsideDown && color==flipView)\r
3010         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3011       else\r
3012         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3013     } else if(appData.pieceDirectory[0]) {\r
3014       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3015       oldBrush = SelectObject(hdc, xBrush);\r
3016       if(appData.upsideDown && color==flipView)\r
3017         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3018       else\r
3019         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3020       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3021       if(appData.upsideDown && color==flipView)\r
3022         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3023       else\r
3024         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3025     } else {\r
3026       /* Use square color for details of black pieces */\r
3027       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3028       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3029       if(appData.upsideDown && !flipView)\r
3030         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3031       else\r
3032         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3033     }\r
3034     SelectObject(hdc, oldBrush);\r
3035     SelectObject(tmphdc, oldBitmap);\r
3036   }\r
3037 }\r
3038 \r
3039 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3040 int GetBackTextureMode( int algo )\r
3041 {\r
3042     int result = BACK_TEXTURE_MODE_DISABLED;\r
3043 \r
3044     switch( algo ) \r
3045     {\r
3046         case BACK_TEXTURE_MODE_PLAIN:\r
3047             result = 1; /* Always use identity map */\r
3048             break;\r
3049         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3050             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3051             break;\r
3052     }\r
3053 \r
3054     return result;\r
3055 }\r
3056 \r
3057 /* \r
3058     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3059     to handle redraws cleanly (as random numbers would always be different).\r
3060 */\r
3061 VOID RebuildTextureSquareInfo()\r
3062 {\r
3063     BITMAP bi;\r
3064     int lite_w = 0;\r
3065     int lite_h = 0;\r
3066     int dark_w = 0;\r
3067     int dark_h = 0;\r
3068     int row;\r
3069     int col;\r
3070 \r
3071     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3072 \r
3073     if( liteBackTexture != NULL ) {\r
3074         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3075             lite_w = bi.bmWidth;\r
3076             lite_h = bi.bmHeight;\r
3077         }\r
3078     }\r
3079 \r
3080     if( darkBackTexture != NULL ) {\r
3081         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3082             dark_w = bi.bmWidth;\r
3083             dark_h = bi.bmHeight;\r
3084         }\r
3085     }\r
3086 \r
3087     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3088         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3089             if( (col + row) & 1 ) {\r
3090                 /* Lite square */\r
3091                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3092                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3093                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3094                   else\r
3095                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3096                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3097                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3098                   else\r
3099                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3100                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3101                 }\r
3102             }\r
3103             else {\r
3104                 /* Dark square */\r
3105                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3106                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3107                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3108                   else\r
3109                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3110                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3111                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3112                   else\r
3113                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3114                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3115                 }\r
3116             }\r
3117         }\r
3118     }\r
3119 }\r
3120 \r
3121 /* [AS] Arrow highlighting support */\r
3122 \r
3123 static double A_WIDTH = 5; /* Width of arrow body */\r
3124 \r
3125 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3126 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3127 \r
3128 static double Sqr( double x )\r
3129 {\r
3130     return x*x;\r
3131 }\r
3132 \r
3133 static int Round( double x )\r
3134 {\r
3135     return (int) (x + 0.5);\r
3136 }\r
3137 \r
3138 /* Draw an arrow between two points using current settings */\r
3139 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3140 {\r
3141     POINT arrow[7];\r
3142     double dx, dy, j, k, x, y;\r
3143 \r
3144     if( d_x == s_x ) {\r
3145         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3146 \r
3147         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3148         arrow[0].y = s_y;\r
3149 \r
3150         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3151         arrow[1].y = d_y - h;\r
3152 \r
3153         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3154         arrow[2].y = d_y - h;\r
3155 \r
3156         arrow[3].x = d_x;\r
3157         arrow[3].y = d_y;\r
3158 \r
3159         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3160         arrow[5].y = d_y - h;\r
3161 \r
3162         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3163         arrow[4].y = d_y - h;\r
3164 \r
3165         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3166         arrow[6].y = s_y;\r
3167     }\r
3168     else if( d_y == s_y ) {\r
3169         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3170 \r
3171         arrow[0].x = s_x;\r
3172         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3173 \r
3174         arrow[1].x = d_x - w;\r
3175         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3176 \r
3177         arrow[2].x = d_x - w;\r
3178         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3179 \r
3180         arrow[3].x = d_x;\r
3181         arrow[3].y = d_y;\r
3182 \r
3183         arrow[5].x = d_x - w;\r
3184         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3185 \r
3186         arrow[4].x = d_x - w;\r
3187         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3188 \r
3189         arrow[6].x = s_x;\r
3190         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3191     }\r
3192     else {\r
3193         /* [AS] Needed a lot of paper for this! :-) */\r
3194         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3195         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3196   \r
3197         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3198 \r
3199         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3200 \r
3201         x = s_x;\r
3202         y = s_y;\r
3203 \r
3204         arrow[0].x = Round(x - j);\r
3205         arrow[0].y = Round(y + j*dx);\r
3206 \r
3207         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3208         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3209 \r
3210         if( d_x > s_x ) {\r
3211             x = (double) d_x - k;\r
3212             y = (double) d_y - k*dy;\r
3213         }\r
3214         else {\r
3215             x = (double) d_x + k;\r
3216             y = (double) d_y + k*dy;\r
3217         }\r
3218 \r
3219         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3220 \r
3221         arrow[6].x = Round(x - j);\r
3222         arrow[6].y = Round(y + j*dx);\r
3223 \r
3224         arrow[2].x = Round(arrow[6].x + 2*j);\r
3225         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3226 \r
3227         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3228         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3229 \r
3230         arrow[4].x = d_x;\r
3231         arrow[4].y = d_y;\r
3232 \r
3233         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3234         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3235     }\r
3236 \r
3237     Polygon( hdc, arrow, 7 );\r
3238 }\r
3239 \r
3240 /* [AS] Draw an arrow between two squares */\r
3241 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3242 {\r
3243     int s_x, s_y, d_x, d_y;\r
3244     HPEN hpen;\r
3245     HPEN holdpen;\r
3246     HBRUSH hbrush;\r
3247     HBRUSH holdbrush;\r
3248     LOGBRUSH stLB;\r
3249 \r
3250     if( s_col == d_col && s_row == d_row ) {\r
3251         return;\r
3252     }\r
3253 \r
3254     /* Get source and destination points */\r
3255     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3256     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3257 \r
3258     if( d_y > s_y ) {\r
3259         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3260     }\r
3261     else if( d_y < s_y ) {\r
3262         d_y += squareSize / 2 + squareSize / 4;\r
3263     }\r
3264     else {\r
3265         d_y += squareSize / 2;\r
3266     }\r
3267 \r
3268     if( d_x > s_x ) {\r
3269         d_x += squareSize / 2 - squareSize / 4;\r
3270     }\r
3271     else if( d_x < s_x ) {\r
3272         d_x += squareSize / 2 + squareSize / 4;\r
3273     }\r
3274     else {\r
3275         d_x += squareSize / 2;\r
3276     }\r
3277 \r
3278     s_x += squareSize / 2;\r
3279     s_y += squareSize / 2;\r
3280 \r
3281     /* Adjust width */\r
3282     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3283 \r
3284     /* Draw */\r
3285     stLB.lbStyle = BS_SOLID;\r
3286     stLB.lbColor = appData.highlightArrowColor;\r
3287     stLB.lbHatch = 0;\r
3288 \r
3289     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3290     holdpen = SelectObject( hdc, hpen );\r
3291     hbrush = CreateBrushIndirect( &stLB );\r
3292     holdbrush = SelectObject( hdc, hbrush );\r
3293 \r
3294     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3295 \r
3296     SelectObject( hdc, holdpen );\r
3297     SelectObject( hdc, holdbrush );\r
3298     DeleteObject( hpen );\r
3299     DeleteObject( hbrush );\r
3300 }\r
3301 \r
3302 BOOL HasHighlightInfo()\r
3303 {\r
3304     BOOL result = FALSE;\r
3305 \r
3306     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3307         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3308     {\r
3309         result = TRUE;\r
3310     }\r
3311 \r
3312     return result;\r
3313 \r
3314 \r
3315 \r
3316 }\r
3317 \r
3318 BOOL IsDrawArrowEnabled()\r
3319 {\r
3320     BOOL result = FALSE;\r
3321 \r
3322     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3323         result = TRUE;\r
3324     }\r
3325 \r
3326     return result;\r
3327 }\r
3328 \r
3329 VOID DrawArrowHighlight( HDC hdc )\r
3330 {\r
3331     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3332         DrawArrowBetweenSquares( hdc,\r
3333             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3334             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3335     }\r
3336 }\r
3337 \r
3338 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3339 {\r
3340     HRGN result = NULL;\r
3341 \r
3342     if( HasHighlightInfo() ) {\r
3343         int x1, y1, x2, y2;\r
3344         int sx, sy, dx, dy;\r
3345 \r
3346         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3347         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3348 \r
3349         sx = MIN( x1, x2 );\r
3350         sy = MIN( y1, y2 );\r
3351         dx = MAX( x1, x2 ) + squareSize;\r
3352         dy = MAX( y1, y2 ) + squareSize;\r
3353 \r
3354         result = CreateRectRgn( sx, sy, dx, dy );\r
3355     }\r
3356 \r
3357     return result;\r
3358 }\r
3359 \r
3360 /*\r
3361     Warning: this function modifies the behavior of several other functions. \r
3362     \r
3363     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3364     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3365     repaint is scattered all over the place, which is not good for features such as\r
3366     "arrow highlighting" that require a full repaint of the board.\r
3367 \r
3368     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3369     user interaction, when speed is not so important) but especially to avoid errors\r
3370     in the displayed graphics.\r
3371 \r
3372     In such patched places, I always try refer to this function so there is a single\r
3373     place to maintain knowledge.\r
3374     \r
3375     To restore the original behavior, just return FALSE unconditionally.\r
3376 */\r
3377 BOOL IsFullRepaintPreferrable()\r
3378 {\r
3379     BOOL result = FALSE;\r
3380 \r
3381     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3382         /* Arrow may appear on the board */\r
3383         result = TRUE;\r
3384     }\r
3385 \r
3386     return result;\r
3387 }\r
3388 \r
3389 /* \r
3390     This function is called by DrawPosition to know whether a full repaint must\r
3391     be forced or not.\r
3392 \r
3393     Only DrawPosition may directly call this function, which makes use of \r
3394     some state information. Other function should call DrawPosition specifying \r
3395     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3396 */\r
3397 BOOL DrawPositionNeedsFullRepaint()\r
3398 {\r
3399     BOOL result = FALSE;\r
3400 \r
3401     /* \r
3402         Probably a slightly better policy would be to trigger a full repaint\r
3403         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3404         but animation is fast enough that it's difficult to notice.\r
3405     */\r
3406     if( animInfo.piece == EmptySquare ) {\r
3407         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3408             result = TRUE;\r
3409         }\r
3410     }\r
3411 \r
3412     return result;\r
3413 }\r
3414 \r
3415 static HBITMAP borderBitmap;\r
3416 \r
3417 VOID\r
3418 DrawBackgroundOnDC(HDC hdc)\r
3419 {\r
3420   \r
3421   BITMAP bi;\r
3422   HDC tmphdc;\r
3423   HBITMAP hbm;\r
3424   static char oldBorder[MSG_SIZ];\r
3425   int w = 600, h = 600, mode;\r
3426 \r
3427   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3428     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3429     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3430   }\r
3431   if(borderBitmap == NULL) { // loading failed, use white\r
3432     FillRect( hdc, &boardRect, whitePieceBrush );\r
3433     return;\r
3434   }\r
3435   tmphdc = CreateCompatibleDC(hdc);\r
3436   hbm = SelectObject(tmphdc, borderBitmap);\r
3437   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3438             w = bi.bmWidth;\r
3439             h = bi.bmHeight;\r
3440   }\r
3441   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3442   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3443                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3444   SetStretchBltMode(hdc, mode);\r
3445   SelectObject(tmphdc, hbm);\r
3446   DeleteDC(tmphdc);\r
3447 }\r
3448 \r
3449 VOID\r
3450 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3451 {\r
3452   int row, column, x, y, square_color, piece_color;\r
3453   ChessSquare piece;\r
3454   HBRUSH oldBrush;\r
3455   HDC texture_hdc = NULL;\r
3456 \r
3457   /* [AS] Initialize background textures if needed */\r
3458   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3459       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3460       if( backTextureSquareSize != squareSize \r
3461        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3462           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3463           backTextureSquareSize = squareSize;\r
3464           RebuildTextureSquareInfo();\r
3465       }\r
3466 \r
3467       texture_hdc = CreateCompatibleDC( hdc );\r
3468   }\r
3469 \r
3470   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3471     for (column = 0; column < BOARD_WIDTH; column++) {\r
3472   \r
3473       SquareToPos(row, column, &x, &y);\r
3474 \r
3475       piece = board[row][column];\r
3476 \r
3477       square_color = ((column + row) % 2) == 1;\r
3478       if( gameInfo.variant == VariantXiangqi ) {\r
3479           square_color = !InPalace(row, column);\r
3480           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3481           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3482       }\r
3483       piece_color = (int) piece < (int) BlackPawn;\r
3484 \r
3485 \r
3486       /* [HGM] holdings file: light square or black */\r
3487       if(column == BOARD_LEFT-2) {\r
3488             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3489                 square_color = 1;\r
3490             else {\r
3491                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3492                 continue;\r
3493             }\r
3494       } else\r
3495       if(column == BOARD_RGHT + 1 ) {\r
3496             if( row < gameInfo.holdingsSize )\r
3497                 square_color = 1;\r
3498             else {\r
3499                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3500                 continue;\r
3501             }\r
3502       }\r
3503       if(column == BOARD_LEFT-1 ) /* left align */\r
3504             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3505       else if( column == BOARD_RGHT) /* right align */\r
3506             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3507       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3508       else\r
3509       if (appData.monoMode) {\r
3510         if (piece == EmptySquare) {\r
3511           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3512                  square_color ? WHITENESS : BLACKNESS);\r
3513         } else {\r
3514           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3515         }\r
3516       } \r
3517       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3518           /* [AS] Draw the square using a texture bitmap */\r
3519           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3520           int r = row, c = column; // [HGM] do not flip board in flipView\r
3521           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3522 \r
3523           DrawTile( x, y, \r
3524               squareSize, squareSize, \r
3525               hdc, \r
3526               texture_hdc,\r
3527               backTextureSquareInfo[r][c].mode,\r
3528               backTextureSquareInfo[r][c].x,\r
3529               backTextureSquareInfo[r][c].y );\r
3530 \r
3531           SelectObject( texture_hdc, hbm );\r
3532 \r
3533           if (piece != EmptySquare) {\r
3534               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3535           }\r
3536       }\r
3537       else {\r
3538         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3539 \r
3540         oldBrush = SelectObject(hdc, brush );\r
3541         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3542         SelectObject(hdc, oldBrush);\r
3543         if (piece != EmptySquare)\r
3544           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3545       }\r
3546     }\r
3547   }\r
3548 \r
3549   if( texture_hdc != NULL ) {\r
3550     DeleteDC( texture_hdc );\r
3551   }\r
3552 }\r
3553 \r
3554 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3555 void fputDW(FILE *f, int x)\r
3556 {\r
3557         fputc(x     & 255, f);\r
3558         fputc(x>>8  & 255, f);\r
3559         fputc(x>>16 & 255, f);\r
3560         fputc(x>>24 & 255, f);\r
3561 }\r
3562 \r
3563 #define MAX_CLIPS 200   /* more than enough */\r
3564 \r
3565 VOID\r
3566 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3567 {\r
3568 //  HBITMAP bufferBitmap;\r
3569   BITMAP bi;\r
3570 //  RECT Rect;\r
3571   HDC tmphdc;\r
3572   HBITMAP hbm;\r
3573   int w = 100, h = 50;\r
3574 \r
3575   if(logo == NULL) {\r
3576     if(!logoHeight) return;\r
3577     FillRect( hdc, &logoRect, whitePieceBrush );\r
3578   }\r
3579 //  GetClientRect(hwndMain, &Rect);\r
3580 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3581 //                                      Rect.bottom-Rect.top+1);\r
3582   tmphdc = CreateCompatibleDC(hdc);\r
3583   hbm = SelectObject(tmphdc, logo);\r
3584   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3585             w = bi.bmWidth;\r
3586             h = bi.bmHeight;\r
3587   }\r
3588   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3589                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3590   SelectObject(tmphdc, hbm);\r
3591   DeleteDC(tmphdc);\r
3592 }\r
3593 \r
3594 VOID\r
3595 DisplayLogos()\r
3596 {\r
3597   if(logoHeight) {\r
3598         HDC hdc = GetDC(hwndMain);\r
3599         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3600         if(appData.autoLogo) {\r
3601           \r
3602           switch(gameMode) { // pick logos based on game mode\r
3603             case IcsObserving:\r
3604                 whiteLogo = second.programLogo; // ICS logo\r
3605                 blackLogo = second.programLogo;\r
3606             default:\r
3607                 break;\r
3608             case IcsPlayingWhite:\r
3609                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3610                 blackLogo = second.programLogo; // ICS logo\r
3611                 break;\r
3612             case IcsPlayingBlack:\r
3613                 whiteLogo = second.programLogo; // ICS logo\r
3614                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3615                 break;\r
3616             case TwoMachinesPlay:\r
3617                 if(first.twoMachinesColor[0] == 'b') {\r
3618                     whiteLogo = second.programLogo;\r
3619                     blackLogo = first.programLogo;\r
3620                 }\r
3621                 break;\r
3622             case MachinePlaysWhite:\r
3623                 blackLogo = userLogo;\r
3624                 break;\r
3625             case MachinePlaysBlack:\r
3626                 whiteLogo = userLogo;\r
3627                 blackLogo = first.programLogo;\r
3628           }\r
3629         }\r
3630         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3631         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3632         ReleaseDC(hwndMain, hdc);\r
3633   }\r
3634 }\r
3635 \r
3636 void\r
3637 UpdateLogos(int display)\r
3638 { // called after loading new engine(s), in tourney or from menu\r
3639   LoadLogo(&first, 0, FALSE);\r
3640   LoadLogo(&second, 1, appData.icsActive);\r
3641   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3642   if(display) DisplayLogos();\r
3643 }\r
3644 \r
3645 static HDC hdcSeek;\r
3646 \r
3647 // [HGM] seekgraph\r
3648 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3649 {\r
3650     POINT stPt;\r
3651     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3652     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3653     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3654     SelectObject( hdcSeek, hp );\r
3655 }\r
3656 \r
3657 // front-end wrapper for drawing functions to do rectangles\r
3658 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3659 {\r
3660     HPEN hp;\r
3661     RECT rc;\r
3662 \r
3663     if (hdcSeek == NULL) {\r
3664     hdcSeek = GetDC(hwndMain);\r
3665       if (!appData.monoMode) {\r
3666         SelectPalette(hdcSeek, hPal, FALSE);\r
3667         RealizePalette(hdcSeek);\r
3668       }\r
3669     }\r
3670     hp = SelectObject( hdcSeek, gridPen );\r
3671     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3672     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3673     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3674     SelectObject( hdcSeek, hp );\r
3675 }\r
3676 \r
3677 // front-end wrapper for putting text in graph\r
3678 void DrawSeekText(char *buf, int x, int y)\r
3679 {\r
3680         SIZE stSize;\r
3681         SetBkMode( hdcSeek, TRANSPARENT );\r
3682         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3683         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3684 }\r
3685 \r
3686 void DrawSeekDot(int x, int y, int color)\r
3687 {\r
3688         int square = color & 0x80;\r
3689         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3690                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3691         color &= 0x7F;\r
3692         if(square)\r
3693             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3694                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3695         else\r
3696             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3697                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3698             SelectObject(hdcSeek, oldBrush);\r
3699 }\r
3700 \r
3701 void DrawSeekOpen()\r
3702 {\r
3703 }\r
3704 \r
3705 void DrawSeekClose()\r
3706 {\r
3707 }\r
3708 \r
3709 VOID\r
3710 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3711 {\r
3712   static Board lastReq[2], lastDrawn[2];\r
3713   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3714   static int lastDrawnFlipView = 0;\r
3715   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3716   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3717   HDC tmphdc;\r
3718   HDC hdcmem;\r
3719   HBITMAP bufferBitmap;\r
3720   HBITMAP oldBitmap;\r
3721   RECT Rect;\r
3722   HRGN clips[MAX_CLIPS];\r
3723   ChessSquare dragged_piece = EmptySquare;\r
3724   int nr = twoBoards*partnerUp;\r
3725 \r
3726   /* I'm undecided on this - this function figures out whether a full\r
3727    * repaint is necessary on its own, so there's no real reason to have the\r
3728    * caller tell it that.  I think this can safely be set to FALSE - but\r
3729    * if we trust the callers not to request full repaints unnessesarily, then\r
3730    * we could skip some clipping work.  In other words, only request a full\r
3731    * redraw when the majority of pieces have changed positions (ie. flip, \r
3732    * gamestart and similar)  --Hawk\r
3733    */\r
3734   Boolean fullrepaint = repaint;\r
3735 \r
3736   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3737 \r
3738   if( DrawPositionNeedsFullRepaint() ) {\r
3739       fullrepaint = TRUE;\r
3740   }\r
3741 \r
3742   if (board == NULL) {\r
3743     if (!lastReqValid[nr]) {\r
3744       return;\r
3745     }\r
3746     board = lastReq[nr];\r
3747   } else {\r
3748     CopyBoard(lastReq[nr], board);\r
3749     lastReqValid[nr] = 1;\r
3750   }\r
3751 \r
3752   if (doingSizing) {\r
3753     return;\r
3754   }\r
3755 \r
3756   if (IsIconic(hwndMain)) {\r
3757     return;\r
3758   }\r
3759 \r
3760   if (hdc == NULL) {\r
3761     hdc = GetDC(hwndMain);\r
3762     if (!appData.monoMode) {\r
3763       SelectPalette(hdc, hPal, FALSE);\r
3764       RealizePalette(hdc);\r
3765     }\r
3766     releaseDC = TRUE;\r
3767   } else {\r
3768     releaseDC = FALSE;\r
3769   }\r
3770 \r
3771   /* Create some work-DCs */\r
3772   hdcmem = CreateCompatibleDC(hdc);\r
3773   tmphdc = CreateCompatibleDC(hdc);\r
3774 \r
3775   /* If dragging is in progress, we temporarely remove the piece */\r
3776   /* [HGM] or temporarily decrease count if stacked              */\r
3777   /*       !! Moved to before board compare !!                   */\r
3778   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3779     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3780     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3781             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3782         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3783     } else \r
3784     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3785             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3786         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3787     } else \r
3788         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3789   }\r
3790 \r
3791   /* Figure out which squares need updating by comparing the \r
3792    * newest board with the last drawn board and checking if\r
3793    * flipping has changed.\r
3794    */\r
3795   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3796     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3797       for (column = 0; column < BOARD_WIDTH; column++) {\r
3798         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3799           SquareToPos(row, column, &x, &y);\r
3800           clips[num_clips++] =\r
3801             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3802         }\r
3803       }\r
3804     }\r
3805    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3806     for (i=0; i<2; i++) {\r
3807       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3808           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3809         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3810             lastDrawnHighlight.sq[i].y >= 0) {\r
3811           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3812                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3813           clips[num_clips++] =\r
3814             CreateRectRgn(x - lineGap, y - lineGap, \r
3815                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3816         }\r
3817         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3818           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3819           clips[num_clips++] =\r
3820             CreateRectRgn(x - lineGap, y - lineGap, \r
3821                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3822         }\r
3823       }\r
3824     }\r
3825     for (i=0; i<2; i++) {\r
3826       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3827           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3828         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3829             lastDrawnPremove.sq[i].y >= 0) {\r
3830           SquareToPos(lastDrawnPremove.sq[i].y,\r
3831                       lastDrawnPremove.sq[i].x, &x, &y);\r
3832           clips[num_clips++] =\r
3833             CreateRectRgn(x - lineGap, y - lineGap, \r
3834                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3835         }\r
3836         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3837             premoveHighlightInfo.sq[i].y >= 0) {\r
3838           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3839                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3840           clips[num_clips++] =\r
3841             CreateRectRgn(x - lineGap, y - lineGap, \r
3842                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3843         }\r
3844       }\r
3845     }\r
3846    } else { // nr == 1\r
3847         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3848         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3849         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3850         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3851       for (i=0; i<2; i++) {\r
3852         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3853             partnerHighlightInfo.sq[i].y >= 0) {\r
3854           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3855                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3856           clips[num_clips++] =\r
3857             CreateRectRgn(x - lineGap, y - lineGap, \r
3858                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3859         }\r
3860         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3861             oldPartnerHighlight.sq[i].y >= 0) {\r
3862           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3863                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3864           clips[num_clips++] =\r
3865             CreateRectRgn(x - lineGap, y - lineGap, \r
3866                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3867         }\r
3868       }\r
3869    }\r
3870   } else {\r
3871     fullrepaint = TRUE;\r
3872   }\r
3873 \r
3874   /* Create a buffer bitmap - this is the actual bitmap\r
3875    * being written to.  When all the work is done, we can\r
3876    * copy it to the real DC (the screen).  This avoids\r
3877    * the problems with flickering.\r
3878    */\r
3879   GetClientRect(hwndMain, &Rect);\r
3880   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3881                                         Rect.bottom-Rect.top+1);\r
3882   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3883   if (!appData.monoMode) {\r
3884     SelectPalette(hdcmem, hPal, FALSE);\r
3885   }\r
3886 \r
3887   /* Create clips for dragging */\r
3888   if (!fullrepaint) {\r
3889     if (dragInfo.from.x >= 0) {\r
3890       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3891       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3892     }\r
3893     if (dragInfo.start.x >= 0) {\r
3894       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3895       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3896     }\r
3897     if (dragInfo.pos.x >= 0) {\r
3898       x = dragInfo.pos.x - squareSize / 2;\r
3899       y = dragInfo.pos.y - squareSize / 2;\r
3900       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3901     }\r
3902     if (dragInfo.lastpos.x >= 0) {\r
3903       x = dragInfo.lastpos.x - squareSize / 2;\r
3904       y = dragInfo.lastpos.y - squareSize / 2;\r
3905       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3906     }\r
3907   }\r
3908 \r
3909   /* Are we animating a move?  \r
3910    * If so, \r
3911    *   - remove the piece from the board (temporarely)\r
3912    *   - calculate the clipping region\r
3913    */\r
3914   if (!fullrepaint) {\r
3915     if (animInfo.piece != EmptySquare) {\r
3916       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3917       x = boardRect.left + animInfo.lastpos.x;\r
3918       y = boardRect.top + animInfo.lastpos.y;\r
3919       x2 = boardRect.left + animInfo.pos.x;\r
3920       y2 = boardRect.top + animInfo.pos.y;\r
3921       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3922       /* Slight kludge.  The real problem is that after AnimateMove is\r
3923          done, the position on the screen does not match lastDrawn.\r
3924          This currently causes trouble only on e.p. captures in\r
3925          atomic, where the piece moves to an empty square and then\r
3926          explodes.  The old and new positions both had an empty square\r
3927          at the destination, but animation has drawn a piece there and\r
3928          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3929       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3930     }\r
3931   }\r
3932 \r
3933   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3934   if (num_clips == 0)\r
3935     fullrepaint = TRUE;\r
3936 \r
3937   /* Set clipping on the memory DC */\r
3938   if (!fullrepaint) {\r
3939     SelectClipRgn(hdcmem, clips[0]);\r
3940     for (x = 1; x < num_clips; x++) {\r
3941       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3942         abort();  // this should never ever happen!\r
3943     }\r
3944   }\r
3945 \r
3946   /* Do all the drawing to the memory DC */\r
3947   if(explodeInfo.radius) { // [HGM] atomic\r
3948         HBRUSH oldBrush;\r
3949         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3950         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3951         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3952         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3953         x += squareSize/2;\r
3954         y += squareSize/2;\r
3955         if(!fullrepaint) {\r
3956           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3957           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3958         }\r
3959         DrawGridOnDC(hdcmem);\r
3960         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3961         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3962         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3963         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3964         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3965         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3966         SelectObject(hdcmem, oldBrush);\r
3967   } else {\r
3968     if(border) DrawBackgroundOnDC(hdcmem);\r
3969     DrawGridOnDC(hdcmem);\r
3970     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3971         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3972         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3973     } else {\r
3974         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3975         oldPartnerHighlight = partnerHighlightInfo;\r
3976     }\r
3977     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3978   }\r
3979   if(nr == 0) // [HGM] dual: markers only on left board\r
3980   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3981     for (column = 0; column < BOARD_WIDTH; column++) {\r
3982         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3983             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3984             SquareToPos(row, column, &x, &y);\r
3985             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3986                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3987             SelectObject(hdcmem, oldBrush);\r
3988         }\r
3989     }\r
3990   }\r
3991 \r
3992   if( appData.highlightMoveWithArrow ) {\r
3993     DrawArrowHighlight(hdcmem);\r
3994   }\r
3995 \r
3996   DrawCoordsOnDC(hdcmem);\r
3997 \r
3998   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3999                  /* to make sure lastDrawn contains what is actually drawn */\r
4000 \r
4001   /* Put the dragged piece back into place and draw it (out of place!) */\r
4002     if (dragged_piece != EmptySquare) {\r
4003     /* [HGM] or restack */\r
4004     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4005                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4006     else\r
4007     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4008                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4009 \r
4010     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4011     x = dragInfo.pos.x - squareSize / 2;\r
4012     y = dragInfo.pos.y - squareSize / 2;\r
4013     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
4014                   ((int) dragInfo.piece < (int) BlackPawn), \r
4015                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4016   }   \r
4017   \r
4018   /* Put the animated piece back into place and draw it */\r
4019   if (animInfo.piece != EmptySquare) {\r
4020     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4021     x = boardRect.left + animInfo.pos.x;\r
4022     y = boardRect.top + animInfo.pos.y;\r
4023     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4024                   ((int) animInfo.piece < (int) BlackPawn),\r
4025                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4026   }\r
4027 \r
4028   /* Release the bufferBitmap by selecting in the old bitmap \r
4029    * and delete the memory DC\r
4030    */\r
4031   SelectObject(hdcmem, oldBitmap);\r
4032   DeleteDC(hdcmem);\r
4033 \r
4034   /* Set clipping on the target DC */\r
4035   if (!fullrepaint) {\r
4036     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4037         RECT rect;\r
4038         GetRgnBox(clips[x], &rect);\r
4039         DeleteObject(clips[x]);\r
4040         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4041                           rect.right + wpMain.width/2, rect.bottom);\r
4042     }\r
4043     SelectClipRgn(hdc, clips[0]);\r
4044     for (x = 1; x < num_clips; x++) {\r
4045       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4046         abort();   // this should never ever happen!\r
4047     } \r
4048   }\r
4049 \r
4050   /* Copy the new bitmap onto the screen in one go.\r
4051    * This way we avoid any flickering\r
4052    */\r
4053   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4054   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4055          boardRect.right - boardRect.left,\r
4056          boardRect.bottom - boardRect.top,\r
4057          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4058   if(saveDiagFlag) { \r
4059     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4060     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4061 \r
4062     GetObject(bufferBitmap, sizeof(b), &b);\r
4063     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4064         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4065         bih.biWidth = b.bmWidth;\r
4066         bih.biHeight = b.bmHeight;\r
4067         bih.biPlanes = 1;\r
4068         bih.biBitCount = b.bmBitsPixel;\r
4069         bih.biCompression = 0;\r
4070         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4071         bih.biXPelsPerMeter = 0;\r
4072         bih.biYPelsPerMeter = 0;\r
4073         bih.biClrUsed = 0;\r
4074         bih.biClrImportant = 0;\r
4075 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4076 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4077         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4078 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4079 \r
4080         wb = b.bmWidthBytes;\r
4081         // count colors\r
4082         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4083                 int k = ((int*) pData)[i];\r
4084                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4085                 if(j >= 16) break;\r
4086                 color[j] = k;\r
4087                 if(j >= nrColors) nrColors = j+1;\r
4088         }\r
4089         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4090                 INT p = 0;\r
4091                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4092                     for(w=0; w<(wb>>2); w+=2) {\r
4093                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4094                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4095                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4096                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4097                         pData[p++] = m | j<<4;\r
4098                     }\r
4099                     while(p&3) pData[p++] = 0;\r
4100                 }\r
4101                 fac = 3;\r
4102                 wb = ((wb+31)>>5)<<2;\r
4103         }\r
4104         // write BITMAPFILEHEADER\r
4105         fprintf(diagFile, "BM");\r
4106         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4107         fputDW(diagFile, 0);\r
4108         fputDW(diagFile, 0x36 + (fac?64:0));\r
4109         // write BITMAPINFOHEADER\r
4110         fputDW(diagFile, 40);\r
4111         fputDW(diagFile, b.bmWidth);\r
4112         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4113         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4114         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4115         fputDW(diagFile, 0);\r
4116         fputDW(diagFile, 0);\r
4117         fputDW(diagFile, 0);\r
4118         fputDW(diagFile, 0);\r
4119         fputDW(diagFile, 0);\r
4120         fputDW(diagFile, 0);\r
4121         // write color table\r
4122         if(fac)\r
4123         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4124         // write bitmap data\r
4125         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4126                 fputc(pData[i], diagFile);\r
4127         free(pData);\r
4128      }\r
4129   }\r
4130 \r
4131   SelectObject(tmphdc, oldBitmap);\r
4132 \r
4133   /* Massive cleanup */\r
4134   for (x = 0; x < num_clips; x++)\r
4135     DeleteObject(clips[x]);\r
4136 \r
4137   DeleteDC(tmphdc);\r
4138   DeleteObject(bufferBitmap);\r
4139 \r
4140   if (releaseDC) \r
4141     ReleaseDC(hwndMain, hdc);\r
4142   \r
4143   if (lastDrawnFlipView != flipView && nr == 0) {\r
4144     if (flipView)\r
4145       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4146     else\r
4147       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4148   }\r
4149 \r
4150 /*  CopyBoard(lastDrawn, board);*/\r
4151   lastDrawnHighlight = highlightInfo;\r
4152   lastDrawnPremove   = premoveHighlightInfo;\r
4153   lastDrawnFlipView = flipView;\r
4154   lastDrawnValid[nr] = 1;\r
4155 }\r
4156 \r
4157 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4158 int\r
4159 SaveDiagram(f)\r
4160      FILE *f;\r
4161 {\r
4162     saveDiagFlag = 1; diagFile = f;\r
4163     HDCDrawPosition(NULL, TRUE, NULL);\r
4164     saveDiagFlag = 0;\r
4165 \r
4166     fclose(f);\r
4167     return TRUE;\r
4168 }\r
4169 \r
4170 \r
4171 /*---------------------------------------------------------------------------*\\r
4172 | CLIENT PAINT PROCEDURE\r
4173 |   This is the main event-handler for the WM_PAINT message.\r
4174 |\r
4175 \*---------------------------------------------------------------------------*/\r
4176 VOID\r
4177 PaintProc(HWND hwnd)\r
4178 {\r
4179   HDC         hdc;\r
4180   PAINTSTRUCT ps;\r
4181   HFONT       oldFont;\r
4182 \r
4183   if((hdc = BeginPaint(hwnd, &ps))) {\r
4184     if (IsIconic(hwnd)) {\r
4185       DrawIcon(hdc, 2, 2, iconCurrent);\r
4186     } else {\r
4187       if (!appData.monoMode) {\r
4188         SelectPalette(hdc, hPal, FALSE);\r
4189         RealizePalette(hdc);\r
4190       }\r
4191       HDCDrawPosition(hdc, 1, NULL);\r
4192       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4193         flipView = !flipView; partnerUp = !partnerUp;\r
4194         HDCDrawPosition(hdc, 1, NULL);\r
4195         flipView = !flipView; partnerUp = !partnerUp;\r
4196       }\r
4197       oldFont =\r
4198         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4199       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4200                  ETO_CLIPPED|ETO_OPAQUE,\r
4201                  &messageRect, messageText, strlen(messageText), NULL);\r
4202       SelectObject(hdc, oldFont);\r
4203       DisplayBothClocks();\r
4204       DisplayLogos();\r
4205     }\r
4206     EndPaint(hwnd,&ps);\r
4207   }\r
4208 \r
4209   return;\r
4210 }\r
4211 \r
4212 \r
4213 /*\r
4214  * If the user selects on a border boundary, return -1; if off the board,\r
4215  *   return -2.  Otherwise map the event coordinate to the square.\r
4216  * The offset boardRect.left or boardRect.top must already have been\r
4217  *   subtracted from x.\r
4218  */\r
4219 int EventToSquare(x, limit)\r
4220      int x, limit;\r
4221 {\r
4222   if (x <= border)\r
4223     return -2;\r
4224   if (x < lineGap + border)\r
4225     return -1;\r
4226   x -= lineGap + border;\r
4227   if ((x % (squareSize + lineGap)) >= squareSize)\r
4228     return -1;\r
4229   x /= (squareSize + lineGap);\r
4230     if (x >= limit)\r
4231     return -2;\r
4232   return x;\r
4233 }\r
4234 \r
4235 typedef struct {\r
4236   char piece;\r
4237   int command;\r
4238   char* name;\r
4239 } DropEnable;\r
4240 \r
4241 DropEnable dropEnables[] = {\r
4242   { 'P', DP_Pawn, N_("Pawn") },\r
4243   { 'N', DP_Knight, N_("Knight") },\r
4244   { 'B', DP_Bishop, N_("Bishop") },\r
4245   { 'R', DP_Rook, N_("Rook") },\r
4246   { 'Q', DP_Queen, N_("Queen") },\r
4247 };\r
4248 \r
4249 VOID\r
4250 SetupDropMenu(HMENU hmenu)\r
4251 {\r
4252   int i, count, enable;\r
4253   char *p;\r
4254   extern char white_holding[], black_holding[];\r
4255   char item[MSG_SIZ];\r
4256 \r
4257   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4258     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4259                dropEnables[i].piece);\r
4260     count = 0;\r
4261     while (p && *p++ == dropEnables[i].piece) count++;\r
4262       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4263     enable = count > 0 || !appData.testLegality\r
4264       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4265                       && !appData.icsActive);\r
4266     ModifyMenu(hmenu, dropEnables[i].command,\r
4267                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4268                dropEnables[i].command, item);\r
4269   }\r
4270 }\r
4271 \r
4272 void DragPieceBegin(int x, int y, Boolean instantly)\r
4273 {\r
4274       dragInfo.lastpos.x = boardRect.left + x;\r
4275       dragInfo.lastpos.y = boardRect.top + y;\r
4276       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4277       dragInfo.from.x = fromX;\r
4278       dragInfo.from.y = fromY;\r
4279       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4280       dragInfo.start = dragInfo.from;\r
4281       SetCapture(hwndMain);\r
4282 }\r
4283 \r
4284 void DragPieceEnd(int x, int y)\r
4285 {\r
4286     ReleaseCapture();\r
4287     dragInfo.start.x = dragInfo.start.y = -1;\r
4288     dragInfo.from = dragInfo.start;\r
4289     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4290 }\r
4291 \r
4292 void ChangeDragPiece(ChessSquare piece)\r
4293 {\r
4294     dragInfo.piece = piece;\r
4295 }\r
4296 \r
4297 /* Event handler for mouse messages */\r
4298 VOID\r
4299 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4300 {\r
4301   int x, y, menuNr;\r
4302   POINT pt;\r
4303   static int recursive = 0;\r
4304   HMENU hmenu;\r
4305   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4306 \r
4307   if (recursive) {\r
4308     if (message == WM_MBUTTONUP) {\r
4309       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4310          to the middle button: we simulate pressing the left button too!\r
4311          */\r
4312       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4313       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4314     }\r
4315     return;\r
4316   }\r
4317   recursive++;\r
4318   \r
4319   pt.x = LOWORD(lParam);\r
4320   pt.y = HIWORD(lParam);\r
4321   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4322   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4323   if (!flipView && y >= 0) {\r
4324     y = BOARD_HEIGHT - 1 - y;\r
4325   }\r
4326   if (flipView && x >= 0) {\r
4327     x = BOARD_WIDTH - 1 - x;\r
4328   }\r
4329 \r
4330   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4331   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4332 \r
4333   switch (message) {\r
4334   case WM_LBUTTONDOWN:\r
4335       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4336         ClockClick(flipClock); break;\r
4337       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4338         ClockClick(!flipClock); break;\r
4339       }\r
4340     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4341       dragInfo.start.x = dragInfo.start.y = -1;\r
4342       dragInfo.from = dragInfo.start;\r
4343     }\r
4344     if(fromX == -1 && frozen) { // not sure where this is for\r
4345                 fromX = fromY = -1; \r
4346       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4347       break;\r
4348     }\r
4349       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4350       DrawPosition(TRUE, NULL);\r
4351     break;\r
4352 \r
4353   case WM_LBUTTONUP:\r
4354       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4355       DrawPosition(TRUE, NULL);\r
4356     break;\r
4357 \r
4358   case WM_MOUSEMOVE:\r
4359     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4360     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4361     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4362     if ((appData.animateDragging || appData.highlightDragging)\r
4363         && (wParam & MK_LBUTTON || dragging == 2)\r
4364         && dragInfo.from.x >= 0) \r
4365     {\r
4366       BOOL full_repaint = FALSE;\r
4367 \r
4368       if (appData.animateDragging) {\r
4369         dragInfo.pos = pt;\r
4370       }\r
4371       if (appData.highlightDragging) {\r
4372         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4373         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4374             full_repaint = TRUE;\r
4375         }\r
4376       }\r
4377       \r
4378       DrawPosition( full_repaint, NULL);\r
4379       \r
4380       dragInfo.lastpos = dragInfo.pos;\r
4381     }\r
4382     break;\r
4383 \r
4384   case WM_MOUSEWHEEL: // [DM]\r
4385     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4386        /* Mouse Wheel is being rolled forward\r
4387         * Play moves forward\r
4388         */\r
4389        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4390                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4391        /* Mouse Wheel is being rolled backward\r
4392         * Play moves backward\r
4393         */\r
4394        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4395                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4396     }\r
4397     break;\r
4398 \r
4399   case WM_MBUTTONUP:\r
4400   case WM_RBUTTONUP:\r
4401     ReleaseCapture();\r
4402     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4403     break;\r
4404  \r
4405   case WM_MBUTTONDOWN:\r
4406   case WM_RBUTTONDOWN:\r
4407     ErrorPopDown();\r
4408     ReleaseCapture();\r
4409     fromX = fromY = -1;\r
4410     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4411     dragInfo.start.x = dragInfo.start.y = -1;\r
4412     dragInfo.from = dragInfo.start;\r
4413     dragInfo.lastpos = dragInfo.pos;\r
4414     if (appData.highlightDragging) {\r
4415       ClearHighlights();\r
4416     }\r
4417     if(y == -2) {\r
4418       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4419       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4420           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4421       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4422           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4423       }\r
4424       break;\r
4425     }\r
4426     DrawPosition(TRUE, NULL);\r
4427 \r
4428     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4429     switch (menuNr) {\r
4430     case 0:\r
4431       if (message == WM_MBUTTONDOWN) {\r
4432         buttonCount = 3;  /* even if system didn't think so */\r
4433         if (wParam & MK_SHIFT) \r
4434           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4435         else\r
4436           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4437       } else { /* message == WM_RBUTTONDOWN */\r
4438         /* Just have one menu, on the right button.  Windows users don't\r
4439            think to try the middle one, and sometimes other software steals\r
4440            it, or it doesn't really exist. */\r
4441         if(gameInfo.variant != VariantShogi)\r
4442             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4443         else\r
4444             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4445       }\r
4446       break;\r
4447     case 2:\r
4448       SetCapture(hwndMain);\r
4449       break;\r
4450     case 1:\r
4451       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4452       SetupDropMenu(hmenu);\r
4453       MenuPopup(hwnd, pt, hmenu, -1);\r
4454     default:\r
4455       break;\r
4456     }\r
4457     break;\r
4458   }\r
4459 \r
4460   recursive--;\r
4461 }\r
4462 \r
4463 /* Preprocess messages for buttons in main window */\r
4464 LRESULT CALLBACK\r
4465 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4466 {\r
4467   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4468   int i, dir;\r
4469 \r
4470   for (i=0; i<N_BUTTONS; i++) {\r
4471     if (buttonDesc[i].id == id) break;\r
4472   }\r
4473   if (i == N_BUTTONS) return 0;\r
4474   switch (message) {\r
4475   case WM_KEYDOWN:\r
4476     switch (wParam) {\r
4477     case VK_LEFT:\r
4478     case VK_RIGHT:\r
4479       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4480       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4481       return TRUE;\r
4482     }\r
4483     break;\r
4484   case WM_CHAR:\r
4485     switch (wParam) {\r
4486     case '\r':\r
4487       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4488       return TRUE;\r
4489     default:\r
4490       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4491         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4492         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4493         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4494         SetFocus(h);\r
4495         SendMessage(h, WM_CHAR, wParam, lParam);\r
4496         return TRUE;\r
4497       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4498         TypeInEvent((char)wParam);\r
4499       }\r
4500       break;\r
4501     }\r
4502     break;\r
4503   }\r
4504   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4505 }\r
4506 \r
4507 static int promoStyle;\r
4508 \r
4509 /* Process messages for Promotion dialog box */\r
4510 LRESULT CALLBACK\r
4511 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4512 {\r
4513   char promoChar;\r
4514 \r
4515   switch (message) {\r
4516 \r
4517   case WM_INITDIALOG: /* message: initialize dialog box */\r
4518     /* Center the dialog over the application window */\r
4519     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4520     Translate(hDlg, DLG_PromotionKing);\r
4521     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4522       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4523        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4524        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4525                SW_SHOW : SW_HIDE);\r
4526     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4527     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4528        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4529          PieceToChar(WhiteAngel) != '~') ||\r
4530         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4531          PieceToChar(BlackAngel) != '~')   ) ?\r
4532                SW_SHOW : SW_HIDE);\r
4533     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4534        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4535          PieceToChar(WhiteMarshall) != '~') ||\r
4536         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4537          PieceToChar(BlackMarshall) != '~')   ) ?\r
4538                SW_SHOW : SW_HIDE);\r
4539     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4540     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4541     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4542     if(promoStyle) {\r
4543         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4544         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4545         SetWindowText(hDlg, "Promote?");\r
4546     }\r
4547     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4548        gameInfo.variant == VariantSuper ?\r
4549                SW_SHOW : SW_HIDE);\r
4550     return TRUE;\r
4551 \r
4552   case WM_COMMAND: /* message: received a command */\r
4553     switch (LOWORD(wParam)) {\r
4554     case IDCANCEL:\r
4555       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4556       ClearHighlights();\r
4557       DrawPosition(FALSE, NULL);\r
4558       return TRUE;\r
4559     case PB_King:\r
4560       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4561       break;\r
4562     case PB_Queen:\r
4563       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4564       break;\r
4565     case PB_Rook:\r
4566       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4567       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4568       break;\r
4569     case PB_Bishop:\r
4570       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4571       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4572       break;\r
4573     case PB_Chancellor:\r
4574       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4575       break;\r
4576     case PB_Archbishop:\r
4577       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4578       break;\r
4579     case PB_Knight:\r
4580       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4581                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4582       break;\r
4583     default:\r
4584       return FALSE;\r
4585     }\r
4586     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4587     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4588     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4589     fromX = fromY = -1;\r
4590     if (!appData.highlightLastMove) {\r
4591       ClearHighlights();\r
4592       DrawPosition(FALSE, NULL);\r
4593     }\r
4594     return TRUE;\r
4595   }\r
4596   return FALSE;\r
4597 }\r
4598 \r
4599 /* Pop up promotion dialog */\r
4600 VOID\r
4601 PromotionPopup(HWND hwnd)\r
4602 {\r
4603   FARPROC lpProc;\r
4604 \r
4605   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4606   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4607     hwnd, (DLGPROC)lpProc);\r
4608   FreeProcInstance(lpProc);\r
4609 }\r
4610 \r
4611 void\r
4612 PromotionPopUp(char choice)\r
4613 {\r
4614   promoStyle = (choice == '+');\r
4615   DrawPosition(TRUE, NULL);\r
4616   PromotionPopup(hwndMain);\r
4617 }\r
4618 \r
4619 VOID\r
4620 LoadGameDialog(HWND hwnd, char* title)\r
4621 {\r
4622   UINT number = 0;\r
4623   FILE *f;\r
4624   char fileTitle[MSG_SIZ];\r
4625   f = OpenFileDialog(hwnd, "rb", "",\r
4626                      appData.oldSaveStyle ? "gam" : "pgn",\r
4627                      GAME_FILT,\r
4628                      title, &number, fileTitle, NULL);\r
4629   if (f != NULL) {\r
4630     cmailMsgLoaded = FALSE;\r
4631     if (number == 0) {\r
4632       int error = GameListBuild(f);\r
4633       if (error) {\r
4634         DisplayError(_("Cannot build game list"), error);\r
4635       } else if (!ListEmpty(&gameList) &&\r
4636                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4637         GameListPopUp(f, fileTitle);\r
4638         return;\r
4639       }\r
4640       GameListDestroy();\r
4641       number = 1;\r
4642     }\r
4643     LoadGame(f, number, fileTitle, FALSE);\r
4644   }\r
4645 }\r
4646 \r
4647 int get_term_width()\r
4648 {\r
4649     HDC hdc;\r
4650     TEXTMETRIC tm;\r
4651     RECT rc;\r
4652     HFONT hfont, hold_font;\r
4653     LOGFONT lf;\r
4654     HWND hText;\r
4655 \r
4656     if (hwndConsole)\r
4657         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4658     else\r
4659         return 79;\r
4660 \r
4661     // get the text metrics\r
4662     hdc = GetDC(hText);\r
4663     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4664     if (consoleCF.dwEffects & CFE_BOLD)\r
4665         lf.lfWeight = FW_BOLD;\r
4666     if (consoleCF.dwEffects & CFE_ITALIC)\r
4667         lf.lfItalic = TRUE;\r
4668     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4669         lf.lfStrikeOut = TRUE;\r
4670     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4671         lf.lfUnderline = TRUE;\r
4672     hfont = CreateFontIndirect(&lf);\r
4673     hold_font = SelectObject(hdc, hfont);\r
4674     GetTextMetrics(hdc, &tm);\r
4675     SelectObject(hdc, hold_font);\r
4676     DeleteObject(hfont);\r
4677     ReleaseDC(hText, hdc);\r
4678 \r
4679     // get the rectangle\r
4680     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4681 \r
4682     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4683 }\r
4684 \r
4685 void UpdateICSWidth(HWND hText)\r
4686 {\r
4687     LONG old_width, new_width;\r
4688 \r
4689     new_width = get_term_width(hText, FALSE);\r
4690     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4691     if (new_width != old_width)\r
4692     {\r
4693         ics_update_width(new_width);\r
4694         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4695     }\r
4696 }\r
4697 \r
4698 VOID\r
4699 ChangedConsoleFont()\r
4700 {\r
4701   CHARFORMAT cfmt;\r
4702   CHARRANGE tmpsel, sel;\r
4703   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4704   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4705   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4706   PARAFORMAT paraf;\r
4707 \r
4708   cfmt.cbSize = sizeof(CHARFORMAT);\r
4709   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4710     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4711                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4712   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4713    * size.  This was undocumented in the version of MSVC++ that I had\r
4714    * when I wrote the code, but is apparently documented now.\r
4715    */\r
4716   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4717   cfmt.bCharSet = f->lf.lfCharSet;\r
4718   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4719   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4720   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4721   /* Why are the following seemingly needed too? */\r
4722   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4723   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4724   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4725   tmpsel.cpMin = 0;\r
4726   tmpsel.cpMax = -1; /*999999?*/\r
4727   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4728   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4729   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4730    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4731    */\r
4732   paraf.cbSize = sizeof(paraf);\r
4733   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4734   paraf.dxStartIndent = 0;\r
4735   paraf.dxOffset = WRAP_INDENT;\r
4736   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4737   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4738   UpdateICSWidth(hText);\r
4739 }\r
4740 \r
4741 /*---------------------------------------------------------------------------*\\r
4742  *\r
4743  * Window Proc for main window\r
4744  *\r
4745 \*---------------------------------------------------------------------------*/\r
4746 \r
4747 /* Process messages for main window, etc. */\r
4748 LRESULT CALLBACK\r
4749 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4750 {\r
4751   FARPROC lpProc;\r
4752   int wmId;\r
4753   char *defName;\r
4754   FILE *f;\r
4755   UINT number;\r
4756   char fileTitle[MSG_SIZ];\r
4757   static SnapData sd;\r
4758   static int peek=0;\r
4759 \r
4760   switch (message) {\r
4761 \r
4762   case WM_PAINT: /* message: repaint portion of window */\r
4763     PaintProc(hwnd);\r
4764     break;\r
4765 \r
4766   case WM_ERASEBKGND:\r
4767     if (IsIconic(hwnd)) {\r
4768       /* Cheat; change the message */\r
4769       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4770     } else {\r
4771       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4772     }\r
4773     break;\r
4774 \r
4775   case WM_LBUTTONDOWN:\r
4776   case WM_MBUTTONDOWN:\r
4777   case WM_RBUTTONDOWN:\r
4778   case WM_LBUTTONUP:\r
4779   case WM_MBUTTONUP:\r
4780   case WM_RBUTTONUP:\r
4781   case WM_MOUSEMOVE:\r
4782   case WM_MOUSEWHEEL:\r
4783     MouseEvent(hwnd, message, wParam, lParam);\r
4784     break;\r
4785 \r
4786   case WM_KEYUP:\r
4787     if((char)wParam == '\b') {\r
4788       ForwardEvent(); peek = 0;\r
4789     }\r
4790 \r
4791     JAWS_KBUP_NAVIGATION\r
4792 \r
4793     break;\r
4794 \r
4795   case WM_KEYDOWN:\r
4796     if((char)wParam == '\b') {\r
4797       if(!peek) BackwardEvent(), peek = 1;\r
4798     }\r
4799 \r
4800     JAWS_KBDOWN_NAVIGATION\r
4801 \r
4802     break;\r
4803 \r
4804   case WM_CHAR:\r
4805     \r
4806     JAWS_ALT_INTERCEPT\r
4807 \r
4808     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4809         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4810         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4811         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4812         SetFocus(h);\r
4813         SendMessage(h, message, wParam, lParam);\r
4814     } else if(lParam != KF_REPEAT) {\r
4815         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4816                 TypeInEvent((char)wParam);\r
4817         } else if((char)wParam == 003) CopyGameToClipboard();\r
4818          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4819     }\r
4820 \r
4821     break;\r
4822 \r
4823   case WM_PALETTECHANGED:\r
4824     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4825       int nnew;\r
4826       HDC hdc = GetDC(hwndMain);\r
4827       SelectPalette(hdc, hPal, TRUE);\r
4828       nnew = RealizePalette(hdc);\r
4829       if (nnew > 0) {\r
4830         paletteChanged = TRUE;\r
4831 \r
4832         InvalidateRect(hwnd, &boardRect, FALSE);\r
4833       }\r
4834       ReleaseDC(hwnd, hdc);\r
4835     }\r
4836     break;\r
4837 \r
4838   case WM_QUERYNEWPALETTE:\r
4839     if (!appData.monoMode /*&& paletteChanged*/) {\r
4840       int nnew;\r
4841       HDC hdc = GetDC(hwndMain);\r
4842       paletteChanged = FALSE;\r
4843       SelectPalette(hdc, hPal, FALSE);\r
4844       nnew = RealizePalette(hdc);\r
4845       if (nnew > 0) {\r
4846         InvalidateRect(hwnd, &boardRect, FALSE);\r
4847       }\r
4848       ReleaseDC(hwnd, hdc);\r
4849       return TRUE;\r
4850     }\r
4851     return FALSE;\r
4852 \r
4853   case WM_COMMAND: /* message: command from application menu */\r
4854     wmId    = LOWORD(wParam);\r
4855 \r
4856     switch (wmId) {\r
4857     case IDM_NewGame:\r
4858       ResetGameEvent();\r
4859       SAY("new game enter a move to play against the computer with white");\r
4860       break;\r
4861 \r
4862     case IDM_NewGameFRC:\r
4863       if( NewGameFRC() == 0 ) {\r
4864         ResetGameEvent();\r
4865       }\r
4866       break;\r
4867 \r
4868     case IDM_NewVariant:\r
4869       NewVariantPopup(hwnd);\r
4870       break;\r
4871 \r
4872     case IDM_LoadGame:\r
4873       LoadGameDialog(hwnd, _("Load Game from File"));\r
4874       break;\r
4875 \r
4876     case IDM_LoadNextGame:\r
4877       ReloadGame(1);\r
4878       break;\r
4879 \r
4880     case IDM_LoadPrevGame:\r
4881       ReloadGame(-1);\r
4882       break;\r
4883 \r
4884     case IDM_ReloadGame:\r
4885       ReloadGame(0);\r
4886       break;\r
4887 \r
4888     case IDM_LoadPosition:\r
4889       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4890         Reset(FALSE, TRUE);\r
4891       }\r
4892       number = 1;\r
4893       f = OpenFileDialog(hwnd, "rb", "",\r
4894                          appData.oldSaveStyle ? "pos" : "fen",\r
4895                          POSITION_FILT,\r
4896                          _("Load Position from File"), &number, fileTitle, NULL);\r
4897       if (f != NULL) {\r
4898         LoadPosition(f, number, fileTitle);\r
4899       }\r
4900       break;\r
4901 \r
4902     case IDM_LoadNextPosition:\r
4903       ReloadPosition(1);\r
4904       break;\r
4905 \r
4906     case IDM_LoadPrevPosition:\r
4907       ReloadPosition(-1);\r
4908       break;\r
4909 \r
4910     case IDM_ReloadPosition:\r
4911       ReloadPosition(0);\r
4912       break;\r
4913 \r
4914     case IDM_SaveGame:\r
4915       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4916       f = OpenFileDialog(hwnd, "a", defName,\r
4917                          appData.oldSaveStyle ? "gam" : "pgn",\r
4918                          GAME_FILT,\r
4919                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4920       if (f != NULL) {\r
4921         SaveGame(f, 0, "");\r
4922       }\r
4923       break;\r
4924 \r
4925     case IDM_SavePosition:\r
4926       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4927       f = OpenFileDialog(hwnd, "a", defName,\r
4928                          appData.oldSaveStyle ? "pos" : "fen",\r
4929                          POSITION_FILT,\r
4930                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4931       if (f != NULL) {\r
4932         SavePosition(f, 0, "");\r
4933       }\r
4934       break;\r
4935 \r
4936     case IDM_SaveDiagram:\r
4937       defName = "diagram";\r
4938       f = OpenFileDialog(hwnd, "wb", defName,\r
4939                          "bmp",\r
4940                          DIAGRAM_FILT,\r
4941                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4942       if (f != NULL) {\r
4943         SaveDiagram(f);\r
4944       }\r
4945       break;\r
4946 \r
4947     case IDM_SaveSelected:\r
4948       f = OpenFileDialog(hwnd, "a", "",\r
4949                          "pgn",\r
4950                          GAME_FILT,\r
4951                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4952       if (f != NULL) {\r
4953         SaveSelected(f, 0, "");\r
4954       }\r
4955       break;\r
4956 \r
4957     case IDM_CreateBook:\r
4958       CreateBookEvent();\r
4959       break;\r
4960 \r
4961     case IDM_CopyGame:\r
4962       CopyGameToClipboard();\r
4963       break;\r
4964 \r
4965     case IDM_PasteGame:\r
4966       PasteGameFromClipboard();\r
4967       break;\r
4968 \r
4969     case IDM_CopyGameListToClipboard:\r
4970       CopyGameListToClipboard();\r
4971       break;\r
4972 \r
4973     /* [AS] Autodetect FEN or PGN data */\r
4974     case IDM_PasteAny:\r
4975       PasteGameOrFENFromClipboard();\r
4976       break;\r
4977 \r
4978     /* [AS] Move history */\r
4979     case IDM_ShowMoveHistory:\r
4980         if( MoveHistoryIsUp() ) {\r
4981             MoveHistoryPopDown();\r
4982         }\r
4983         else {\r
4984             MoveHistoryPopUp();\r
4985         }\r
4986         break;\r
4987 \r
4988     /* [AS] Eval graph */\r
4989     case IDM_ShowEvalGraph:\r
4990         if( EvalGraphIsUp() ) {\r
4991             EvalGraphPopDown();\r
4992         }\r
4993         else {\r
4994             EvalGraphPopUp();\r
4995             SetFocus(hwndMain);\r
4996         }\r
4997         break;\r
4998 \r
4999     /* [AS] Engine output */\r
5000     case IDM_ShowEngineOutput:\r
5001         if( EngineOutputIsUp() ) {\r
5002             EngineOutputPopDown();\r
5003         }\r
5004         else {\r
5005             EngineOutputPopUp();\r
5006         }\r
5007         break;\r
5008 \r
5009     /* [AS] User adjudication */\r
5010     case IDM_UserAdjudication_White:\r
5011         UserAdjudicationEvent( +1 );\r
5012         break;\r
5013 \r
5014     case IDM_UserAdjudication_Black:\r
5015         UserAdjudicationEvent( -1 );\r
5016         break;\r
5017 \r
5018     case IDM_UserAdjudication_Draw:\r
5019         UserAdjudicationEvent( 0 );\r
5020         break;\r
5021 \r
5022     /* [AS] Game list options dialog */\r
5023     case IDM_GameListOptions:\r
5024       GameListOptions();\r
5025       break;\r
5026 \r
5027     case IDM_NewChat:\r
5028       ChatPopUp(NULL);\r
5029       break;\r
5030 \r
5031     case IDM_CopyPosition:\r
5032       CopyFENToClipboard();\r
5033       break;\r
5034 \r
5035     case IDM_PastePosition:\r
5036       PasteFENFromClipboard();\r
5037       break;\r
5038 \r
5039     case IDM_MailMove:\r
5040       MailMoveEvent();\r
5041       break;\r
5042 \r
5043     case IDM_ReloadCMailMsg:\r
5044       Reset(TRUE, TRUE);\r
5045       ReloadCmailMsgEvent(FALSE);\r
5046       break;\r
5047 \r
5048     case IDM_Minimize:\r
5049       ShowWindow(hwnd, SW_MINIMIZE);\r
5050       break;\r
5051 \r
5052     case IDM_Exit:\r
5053       ExitEvent(0);\r
5054       break;\r
5055 \r
5056     case IDM_MachineWhite:\r
5057       MachineWhiteEvent();\r
5058       /*\r
5059        * refresh the tags dialog only if it's visible\r
5060        */\r
5061       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5062           char *tags;\r
5063           tags = PGNTags(&gameInfo);\r
5064           TagsPopUp(tags, CmailMsg());\r
5065           free(tags);\r
5066       }\r
5067       SAY("computer starts playing white");\r
5068       break;\r
5069 \r
5070     case IDM_MachineBlack:\r
5071       MachineBlackEvent();\r
5072       /*\r
5073        * refresh the tags dialog only if it's visible\r
5074        */\r
5075       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5076           char *tags;\r
5077           tags = PGNTags(&gameInfo);\r
5078           TagsPopUp(tags, CmailMsg());\r
5079           free(tags);\r
5080       }\r
5081       SAY("computer starts playing black");\r
5082       break;\r
5083 \r
5084     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5085       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5086       break;\r
5087 \r
5088     case IDM_TwoMachines:\r
5089       TwoMachinesEvent();\r
5090       /*\r
5091        * refresh the tags dialog only if it's visible\r
5092        */\r
5093       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5094           char *tags;\r
5095           tags = PGNTags(&gameInfo);\r
5096           TagsPopUp(tags, CmailMsg());\r
5097           free(tags);\r
5098       }\r
5099       SAY("computer starts playing both sides");\r
5100       break;\r
5101 \r
5102     case IDM_AnalysisMode:\r
5103       if(AnalyzeModeEvent()) {\r
5104         SAY("analyzing current position");\r
5105       }\r
5106       break;\r
5107 \r
5108     case IDM_AnalyzeFile:\r
5109       AnalyzeFileEvent();\r
5110       break;\r
5111 \r
5112     case IDM_IcsClient:\r
5113       IcsClientEvent();\r
5114       break;\r
5115 \r
5116     case IDM_EditGame:\r
5117     case IDM_EditGame2:\r
5118       EditGameEvent();\r
5119       SAY("edit game");\r
5120       break;\r
5121 \r
5122     case IDM_EditPosition:\r
5123     case IDM_EditPosition2:\r
5124       EditPositionEvent();\r
5125       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5126       break;\r
5127 \r
5128     case IDM_Training:\r
5129       TrainingEvent();\r
5130       break;\r
5131 \r
5132     case IDM_ShowGameList:\r
5133       ShowGameListProc();\r
5134       break;\r
5135 \r
5136     case IDM_EditProgs1:\r
5137       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5138       break;\r
5139 \r
5140     case IDM_LoadProg1:\r
5141      LoadEnginePopUp(hwndMain, 0);\r
5142       break;\r
5143 \r
5144     case IDM_LoadProg2:\r
5145      LoadEnginePopUp(hwndMain, 1);\r
5146       break;\r
5147 \r
5148     case IDM_EditServers:\r
5149       EditTagsPopUp(icsNames, &icsNames);\r
5150       break;\r
5151 \r
5152     case IDM_EditTags:\r
5153     case IDM_Tags:\r
5154       EditTagsProc();\r
5155       break;\r
5156 \r
5157     case IDM_EditBook:\r
5158       EditBookEvent();\r
5159       break;\r
5160 \r
5161     case IDM_EditComment:\r
5162     case IDM_Comment:\r
5163       if (commentUp && editComment) {\r
5164         CommentPopDown();\r
5165       } else {\r
5166         EditCommentEvent();\r
5167       }\r
5168       break;\r
5169 \r
5170     case IDM_Pause:\r
5171       PauseEvent();\r
5172       break;\r
5173 \r
5174     case IDM_Accept:\r
5175       AcceptEvent();\r
5176       break;\r
5177 \r
5178     case IDM_Decline:\r
5179       DeclineEvent();\r
5180       break;\r
5181 \r
5182     case IDM_Rematch:\r
5183 \r
5184       RematchEvent();\r
5185       break;\r
5186 \r
5187     case IDM_CallFlag:\r
5188       CallFlagEvent();\r
5189       break;\r
5190 \r
5191     case IDM_Draw:\r
5192       DrawEvent();\r
5193       break;\r
5194 \r
5195     case IDM_Adjourn:\r
5196       AdjournEvent();\r
5197       break;\r
5198 \r
5199     case IDM_Abort:\r
5200       AbortEvent();\r
5201       break;\r
5202 \r
5203     case IDM_Resign:\r
5204       ResignEvent();\r
5205       break;\r
5206 \r
5207     case IDM_StopObserving:\r
5208       StopObservingEvent();\r
5209       break;\r
5210 \r
5211     case IDM_StopExamining:\r
5212       StopExaminingEvent();\r
5213       break;\r
5214 \r
5215     case IDM_Upload:\r
5216       UploadGameEvent();\r
5217       break;\r
5218 \r
5219     case IDM_TypeInMove:\r
5220       TypeInEvent('\000');\r
5221       break;\r
5222 \r
5223     case IDM_TypeInName:\r
5224       PopUpNameDialog('\000');\r
5225       break;\r
5226 \r
5227     case IDM_Backward:\r
5228       BackwardEvent();\r
5229       SetFocus(hwndMain);\r
5230       break;\r
5231 \r
5232     JAWS_MENU_ITEMS\r
5233 \r
5234     case IDM_Forward:\r
5235       ForwardEvent();\r
5236       SetFocus(hwndMain);\r
5237       break;\r
5238 \r
5239     case IDM_ToStart:\r
5240       ToStartEvent();\r
5241       SetFocus(hwndMain);\r
5242       break;\r
5243 \r
5244     case IDM_ToEnd:\r
5245       ToEndEvent();\r
5246       SetFocus(hwndMain);\r
5247       break;\r
5248 \r
5249     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5250     case OPT_GameListPrev:\r
5251       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5252       break;\r
5253 \r
5254     case IDM_Revert:\r
5255       RevertEvent(FALSE);\r
5256       break;\r
5257 \r
5258     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5259       RevertEvent(TRUE);\r
5260       break;\r
5261 \r
5262     case IDM_TruncateGame:\r
5263       TruncateGameEvent();\r
5264       break;\r
5265 \r
5266     case IDM_MoveNow:\r
5267       MoveNowEvent();\r
5268       break;\r
5269 \r
5270     case IDM_RetractMove:\r
5271       RetractMoveEvent();\r
5272       break;\r
5273 \r
5274     case IDM_FlipView:\r
5275       flipView = !flipView;\r
5276       DrawPosition(FALSE, NULL);\r
5277       break;\r
5278 \r
5279     case IDM_FlipClock:\r
5280       flipClock = !flipClock;\r
5281       DisplayBothClocks();\r
5282       DisplayLogos();\r
5283       break;\r
5284 \r
5285     case IDM_MuteSounds:\r
5286       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5287       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5288                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5289       break;\r
5290 \r
5291     case IDM_GeneralOptions:\r
5292       GeneralOptionsPopup(hwnd);\r
5293       DrawPosition(TRUE, NULL);\r
5294       break;\r
5295 \r
5296     case IDM_BoardOptions:\r
5297       BoardOptionsPopup(hwnd);\r
5298       break;\r
5299 \r
5300     case IDM_ThemeOptions:\r
5301       ThemeOptionsPopup(hwnd);\r
5302       break;\r
5303 \r
5304     case IDM_EnginePlayOptions:\r
5305       EnginePlayOptionsPopup(hwnd);\r
5306       break;\r
5307 \r
5308     case IDM_Engine1Options:\r
5309       EngineOptionsPopup(hwnd, &first);\r
5310       break;\r
5311 \r
5312     case IDM_Engine2Options:\r
5313       savedHwnd = hwnd;\r
5314       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5315       EngineOptionsPopup(hwnd, &second);\r
5316       break;\r
5317 \r
5318     case IDM_OptionsUCI:\r
5319       UciOptionsPopup(hwnd);\r
5320       break;\r
5321 \r
5322     case IDM_Tourney:\r
5323       TourneyPopup(hwnd);\r
5324       break;\r
5325 \r
5326     case IDM_IcsOptions:\r
5327       IcsOptionsPopup(hwnd);\r
5328       break;\r
5329 \r
5330     case IDM_Fonts:\r
5331       FontsOptionsPopup(hwnd);\r
5332       break;\r
5333 \r
5334     case IDM_Sounds:\r
5335       SoundOptionsPopup(hwnd);\r
5336       break;\r
5337 \r
5338     case IDM_CommPort:\r
5339       CommPortOptionsPopup(hwnd);\r
5340       break;\r
5341 \r
5342     case IDM_LoadOptions:\r
5343       LoadOptionsPopup(hwnd);\r
5344       break;\r
5345 \r
5346     case IDM_SaveOptions:\r
5347       SaveOptionsPopup(hwnd);\r
5348       break;\r
5349 \r
5350     case IDM_TimeControl:\r
5351       TimeControlOptionsPopup(hwnd);\r
5352       break;\r
5353 \r
5354     case IDM_SaveSettings:\r
5355       SaveSettings(settingsFileName);\r
5356       break;\r
5357 \r
5358     case IDM_SaveSettingsOnExit:\r
5359       saveSettingsOnExit = !saveSettingsOnExit;\r
5360       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5361                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5362                                          MF_CHECKED : MF_UNCHECKED));\r
5363       break;\r
5364 \r
5365     case IDM_Hint:\r
5366       HintEvent();\r
5367       break;\r
5368 \r
5369     case IDM_Book:\r
5370       BookEvent();\r
5371       break;\r
5372 \r
5373     case IDM_AboutGame:\r
5374       AboutGameEvent();\r
5375       break;\r
5376 \r
5377     case IDM_Debug:\r
5378       appData.debugMode = !appData.debugMode;\r
5379       if (appData.debugMode) {\r
5380         char dir[MSG_SIZ];\r
5381         GetCurrentDirectory(MSG_SIZ, dir);\r
5382         SetCurrentDirectory(installDir);\r
5383         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5384         SetCurrentDirectory(dir);\r
5385         setbuf(debugFP, NULL);\r
5386       } else {\r
5387         fclose(debugFP);\r
5388         debugFP = NULL;\r
5389       }\r
5390       break;\r
5391 \r
5392     case IDM_HELPCONTENTS:\r
5393       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5394           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5395           MessageBox (GetFocus(),\r
5396                     _("Unable to activate help"),\r
5397                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5398       }\r
5399       break;\r
5400 \r
5401     case IDM_HELPSEARCH:\r
5402         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5403             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5404         MessageBox (GetFocus(),\r
5405                     _("Unable to activate help"),\r
5406                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5407       }\r
5408       break;\r
5409 \r
5410     case IDM_HELPHELP:\r
5411       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5412         MessageBox (GetFocus(),\r
5413                     _("Unable to activate help"),\r
5414                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5415       }\r
5416       break;\r
5417 \r
5418     case IDM_ABOUT:\r
5419       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5420       DialogBox(hInst, \r
5421         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5422         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5423       FreeProcInstance(lpProc);\r
5424       break;\r
5425 \r
5426     case IDM_DirectCommand1:\r
5427       AskQuestionEvent(_("Direct Command"),\r
5428                        _("Send to chess program:"), "", "1");\r
5429       break;\r
5430     case IDM_DirectCommand2:\r
5431       AskQuestionEvent(_("Direct Command"),\r
5432                        _("Send to second chess program:"), "", "2");\r
5433       break;\r
5434 \r
5435     case EP_WhitePawn:\r
5436       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5437       fromX = fromY = -1;\r
5438       break;\r
5439 \r
5440     case EP_WhiteKnight:\r
5441       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5442       fromX = fromY = -1;\r
5443       break;\r
5444 \r
5445     case EP_WhiteBishop:\r
5446       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5447       fromX = fromY = -1;\r
5448       break;\r
5449 \r
5450     case EP_WhiteRook:\r
5451       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5452       fromX = fromY = -1;\r
5453       break;\r
5454 \r
5455     case EP_WhiteQueen:\r
5456       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5457       fromX = fromY = -1;\r
5458       break;\r
5459 \r
5460     case EP_WhiteFerz:\r
5461       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5462       fromX = fromY = -1;\r
5463       break;\r
5464 \r
5465     case EP_WhiteWazir:\r
5466       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5467       fromX = fromY = -1;\r
5468       break;\r
5469 \r
5470     case EP_WhiteAlfil:\r
5471       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5472       fromX = fromY = -1;\r
5473       break;\r
5474 \r
5475     case EP_WhiteCannon:\r
5476       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5477       fromX = fromY = -1;\r
5478       break;\r
5479 \r
5480     case EP_WhiteCardinal:\r
5481       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5482       fromX = fromY = -1;\r
5483       break;\r
5484 \r
5485     case EP_WhiteMarshall:\r
5486       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5487       fromX = fromY = -1;\r
5488       break;\r
5489 \r
5490     case EP_WhiteKing:\r
5491       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5492       fromX = fromY = -1;\r
5493       break;\r
5494 \r
5495     case EP_BlackPawn:\r
5496       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5497       fromX = fromY = -1;\r
5498       break;\r
5499 \r
5500     case EP_BlackKnight:\r
5501       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5502       fromX = fromY = -1;\r
5503       break;\r
5504 \r
5505     case EP_BlackBishop:\r
5506       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5507       fromX = fromY = -1;\r
5508       break;\r
5509 \r
5510     case EP_BlackRook:\r
5511       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5512       fromX = fromY = -1;\r
5513       break;\r
5514 \r
5515     case EP_BlackQueen:\r
5516       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5517       fromX = fromY = -1;\r
5518       break;\r
5519 \r
5520     case EP_BlackFerz:\r
5521       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5522       fromX = fromY = -1;\r
5523       break;\r
5524 \r
5525     case EP_BlackWazir:\r
5526       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5527       fromX = fromY = -1;\r
5528       break;\r
5529 \r
5530     case EP_BlackAlfil:\r
5531       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5532       fromX = fromY = -1;\r
5533       break;\r
5534 \r
5535     case EP_BlackCannon:\r
5536       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5537       fromX = fromY = -1;\r
5538       break;\r
5539 \r
5540     case EP_BlackCardinal:\r
5541       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5542       fromX = fromY = -1;\r
5543       break;\r
5544 \r
5545     case EP_BlackMarshall:\r
5546       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5547       fromX = fromY = -1;\r
5548       break;\r
5549 \r
5550     case EP_BlackKing:\r
5551       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5552       fromX = fromY = -1;\r
5553       break;\r
5554 \r
5555     case EP_EmptySquare:\r
5556       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5557       fromX = fromY = -1;\r
5558       break;\r
5559 \r
5560     case EP_ClearBoard:\r
5561       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5562       fromX = fromY = -1;\r
5563       break;\r
5564 \r
5565     case EP_White:\r
5566       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5567       fromX = fromY = -1;\r
5568       break;\r
5569 \r
5570     case EP_Black:\r
5571       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5572       fromX = fromY = -1;\r
5573       break;\r
5574 \r
5575     case EP_Promote:\r
5576       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5577       fromX = fromY = -1;\r
5578       break;\r
5579 \r
5580     case EP_Demote:\r
5581       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5582       fromX = fromY = -1;\r
5583       break;\r
5584 \r
5585     case DP_Pawn:\r
5586       DropMenuEvent(WhitePawn, fromX, fromY);\r
5587       fromX = fromY = -1;\r
5588       break;\r
5589 \r
5590     case DP_Knight:\r
5591       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5592       fromX = fromY = -1;\r
5593       break;\r
5594 \r
5595     case DP_Bishop:\r
5596       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5597       fromX = fromY = -1;\r
5598       break;\r
5599 \r
5600     case DP_Rook:\r
5601       DropMenuEvent(WhiteRook, fromX, fromY);\r
5602       fromX = fromY = -1;\r
5603       break;\r
5604 \r
5605     case DP_Queen:\r
5606       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5607       fromX = fromY = -1;\r
5608       break;\r
5609 \r
5610     case IDM_English:\r
5611       barbaric = 0; appData.language = "";\r
5612       TranslateMenus(0);\r
5613       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5614       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5615       lastChecked = wmId;\r
5616       break;\r
5617 \r
5618     default:\r
5619       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5620           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5621       else\r
5622       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5623           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5624           TranslateMenus(0);\r
5625           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5626           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5627           lastChecked = wmId;\r
5628           break;\r
5629       }\r
5630       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5631     }\r
5632     break;\r
5633 \r
5634   case WM_TIMER:\r
5635     switch (wParam) {\r
5636     case CLOCK_TIMER_ID:\r
5637       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5638       clockTimerEvent = 0;\r
5639       DecrementClocks(); /* call into back end */\r
5640       break;\r
5641     case LOAD_GAME_TIMER_ID:\r
5642       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5643       loadGameTimerEvent = 0;\r
5644       AutoPlayGameLoop(); /* call into back end */\r
5645       break;\r
5646     case ANALYSIS_TIMER_ID:\r
5647       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5648                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5649         AnalysisPeriodicEvent(0);\r
5650       } else {\r
5651         KillTimer(hwnd, analysisTimerEvent);\r
5652         analysisTimerEvent = 0;\r
5653       }\r
5654       break;\r
5655     case DELAYED_TIMER_ID:\r
5656       KillTimer(hwnd, delayedTimerEvent);\r
5657       delayedTimerEvent = 0;\r
5658       delayedTimerCallback();\r
5659       break;\r
5660     }\r
5661     break;\r
5662 \r
5663   case WM_USER_Input:\r
5664     InputEvent(hwnd, message, wParam, lParam);\r
5665     break;\r
5666 \r
5667   /* [AS] Also move "attached" child windows */\r
5668   case WM_WINDOWPOSCHANGING:\r
5669 \r
5670     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5671         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5672 \r
5673         if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?\r
5674             /* Window is moving */\r
5675             RECT rcMain;\r
5676 \r
5677 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5678             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5679             rcMain.right  = wpMain.x + wpMain.width;\r
5680             rcMain.top    = wpMain.y;\r
5681             rcMain.bottom = wpMain.y + wpMain.height;\r
5682             \r
5683             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5684             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5685             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5686             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5687             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5688             wpMain.x = lpwp->x;\r
5689             wpMain.y = lpwp->y;\r
5690         }\r
5691     }\r
5692     break;\r
5693 \r
5694   /* [AS] Snapping */\r
5695   case WM_ENTERSIZEMOVE:\r
5696     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5697     if (hwnd == hwndMain) {\r
5698       doingSizing = TRUE;\r
5699       lastSizing = 0;\r
5700     }\r
5701     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5702     break;\r
5703 \r
5704   case WM_SIZING:\r
5705     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5706     if (hwnd == hwndMain) {\r
5707       lastSizing = wParam;\r
5708     }\r
5709     break;\r
5710 \r
5711   case WM_MOVING:\r
5712     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5713       return OnMoving( &sd, hwnd, wParam, lParam );\r
5714 \r
5715   case WM_EXITSIZEMOVE:\r
5716     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5717     if (hwnd == hwndMain) {\r
5718       RECT client;\r
5719       doingSizing = FALSE;\r
5720       InvalidateRect(hwnd, &boardRect, FALSE);\r
5721       GetClientRect(hwnd, &client);\r
5722       ResizeBoard(client.right, client.bottom, lastSizing);\r
5723       lastSizing = 0;\r
5724       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5725     }\r
5726     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5727     break;\r
5728 \r
5729   case WM_DESTROY: /* message: window being destroyed */\r
5730     PostQuitMessage(0);\r
5731     break;\r
5732 \r
5733   case WM_CLOSE:\r
5734     if (hwnd == hwndMain) {\r
5735       ExitEvent(0);\r
5736     }\r
5737     break;\r
5738 \r
5739   default:      /* Passes it on if unprocessed */\r
5740     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5741   }\r
5742 \r
5743   return 0;\r
5744 }\r
5745 \r
5746 /*---------------------------------------------------------------------------*\\r
5747  *\r
5748  * Misc utility routines\r
5749  *\r
5750 \*---------------------------------------------------------------------------*/\r
5751 \r
5752 /*\r
5753  * Decent random number generator, at least not as bad as Windows\r
5754  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5755  */\r
5756 unsigned int randstate;\r
5757 \r
5758 int\r
5759 myrandom(void)\r
5760 {\r
5761   randstate = randstate * 1664525 + 1013904223;\r
5762   return (int) randstate & 0x7fffffff;\r
5763 }\r
5764 \r
5765 void\r
5766 mysrandom(unsigned int seed)\r
5767 {\r
5768   randstate = seed;\r
5769 }\r
5770 \r
5771 \r
5772 /* \r
5773  * returns TRUE if user selects a different color, FALSE otherwise \r
5774  */\r
5775 \r
5776 BOOL\r
5777 ChangeColor(HWND hwnd, COLORREF *which)\r
5778 {\r
5779   static BOOL firstTime = TRUE;\r
5780   static DWORD customColors[16];\r
5781   CHOOSECOLOR cc;\r
5782   COLORREF newcolor;\r
5783   int i;\r
5784   ColorClass ccl;\r
5785 \r
5786   if (firstTime) {\r
5787     /* Make initial colors in use available as custom colors */\r
5788     /* Should we put the compiled-in defaults here instead? */\r
5789     i = 0;\r
5790     customColors[i++] = lightSquareColor & 0xffffff;\r
5791     customColors[i++] = darkSquareColor & 0xffffff;\r
5792     customColors[i++] = whitePieceColor & 0xffffff;\r
5793     customColors[i++] = blackPieceColor & 0xffffff;\r
5794     customColors[i++] = highlightSquareColor & 0xffffff;\r
5795     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5796 \r
5797     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5798       customColors[i++] = textAttribs[ccl].color;\r
5799     }\r
5800     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5801     firstTime = FALSE;\r
5802   }\r
5803 \r
5804   cc.lStructSize = sizeof(cc);\r
5805   cc.hwndOwner = hwnd;\r
5806   cc.hInstance = NULL;\r
5807   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5808   cc.lpCustColors = (LPDWORD) customColors;\r
5809   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5810 \r
5811   if (!ChooseColor(&cc)) return FALSE;\r
5812 \r
5813   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5814   if (newcolor == *which) return FALSE;\r
5815   *which = newcolor;\r
5816   return TRUE;\r
5817 \r
5818   /*\r
5819   InitDrawingColors();\r
5820   InvalidateRect(hwnd, &boardRect, FALSE);\r
5821   */\r
5822 }\r
5823 \r
5824 BOOLEAN\r
5825 MyLoadSound(MySound *ms)\r
5826 {\r
5827   BOOL ok = FALSE;\r
5828   struct stat st;\r
5829   FILE *f;\r
5830 \r
5831   if (ms->data && ms->flag) free(ms->data);\r
5832   ms->data = NULL;\r
5833 \r
5834   switch (ms->name[0]) {\r
5835   case NULLCHAR:\r
5836     /* Silence */\r
5837     ok = TRUE;\r
5838     break;\r
5839   case '$':\r
5840     /* System sound from Control Panel.  Don't preload here. */\r
5841     ok = TRUE;\r
5842     break;\r
5843   case '!':\r
5844     if (ms->name[1] == NULLCHAR) {\r
5845       /* "!" alone = silence */\r
5846       ok = TRUE;\r
5847     } else {\r
5848       /* Builtin wave resource.  Error if not found. */\r
5849       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5850       if (h == NULL) break;\r
5851       ms->data = (void *)LoadResource(hInst, h);\r
5852       ms->flag = 0; // not maloced, so cannot be freed!\r
5853       if (h == NULL) break;\r
5854       ok = TRUE;\r
5855     }\r
5856     break;\r
5857   default:\r
5858     /* .wav file.  Error if not found. */\r
5859     f = fopen(ms->name, "rb");\r
5860     if (f == NULL) break;\r
5861     if (fstat(fileno(f), &st) < 0) break;\r
5862     ms->data = malloc(st.st_size);\r
5863     ms->flag = 1;\r
5864     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5865     fclose(f);\r
5866     ok = TRUE;\r
5867     break;\r
5868   }\r
5869   if (!ok) {\r
5870     char buf[MSG_SIZ];\r
5871       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5872     DisplayError(buf, GetLastError());\r
5873   }\r
5874   return ok;\r
5875 }\r
5876 \r
5877 BOOLEAN\r
5878 MyPlaySound(MySound *ms)\r
5879 {\r
5880   BOOLEAN ok = FALSE;\r
5881 \r
5882   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5883   switch (ms->name[0]) {\r
5884   case NULLCHAR:\r
5885         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5886     /* Silence */\r
5887     ok = TRUE;\r
5888     break;\r
5889   case '$':\r
5890     /* System sound from Control Panel (deprecated feature).\r
5891        "$" alone or an unset sound name gets default beep (still in use). */\r
5892     if (ms->name[1]) {\r
5893       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5894     }\r
5895     if (!ok) ok = MessageBeep(MB_OK);\r
5896     break; \r
5897   case '!':\r
5898     /* Builtin wave resource, or "!" alone for silence */\r
5899     if (ms->name[1]) {\r
5900       if (ms->data == NULL) return FALSE;\r
5901       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5902     } else {\r
5903       ok = TRUE;\r
5904     }\r
5905     break;\r
5906   default:\r
5907     /* .wav file.  Error if not found. */\r
5908     if (ms->data == NULL) return FALSE;\r
5909     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5910     break;\r
5911   }\r
5912   /* Don't print an error: this can happen innocently if the sound driver\r
5913      is busy; for instance, if another instance of WinBoard is playing\r
5914      a sound at about the same time. */\r
5915   return ok;\r
5916 }\r
5917 \r
5918 \r
5919 LRESULT CALLBACK\r
5920 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5921 {\r
5922   BOOL ok;\r
5923   OPENFILENAME *ofn;\r
5924   static UINT *number; /* gross that this is static */\r
5925 \r
5926   switch (message) {\r
5927   case WM_INITDIALOG: /* message: initialize dialog box */\r
5928     /* Center the dialog over the application window */\r
5929     ofn = (OPENFILENAME *) lParam;\r
5930     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5931       number = (UINT *) ofn->lCustData;\r
5932       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5933     } else {\r
5934       number = NULL;\r
5935     }\r
5936     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5937     Translate(hDlg, 1536);\r
5938     return FALSE;  /* Allow for further processing */\r
5939 \r
5940   case WM_COMMAND:\r
5941     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5942       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5943     }\r
5944     return FALSE;  /* Allow for further processing */\r
5945   }\r
5946   return FALSE;\r
5947 }\r
5948 \r
5949 UINT APIENTRY\r
5950 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5951 {\r
5952   static UINT *number;\r
5953   OPENFILENAME *ofname;\r
5954   OFNOTIFY *ofnot;\r
5955   switch (uiMsg) {\r
5956   case WM_INITDIALOG:\r
5957     Translate(hdlg, DLG_IndexNumber);\r
5958     ofname = (OPENFILENAME *)lParam;\r
5959     number = (UINT *)(ofname->lCustData);\r
5960     break;\r
5961   case WM_NOTIFY:\r
5962     ofnot = (OFNOTIFY *)lParam;\r
5963     if (ofnot->hdr.code == CDN_FILEOK) {\r
5964       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5965     }\r
5966     break;\r
5967   }\r
5968   return 0;\r
5969 }\r
5970 \r
5971 \r
5972 FILE *\r
5973 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5974                char *nameFilt, char *dlgTitle, UINT *number,\r
5975                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5976 {\r
5977   OPENFILENAME openFileName;\r
5978   char buf1[MSG_SIZ];\r
5979   FILE *f;\r
5980 \r
5981   if (fileName == NULL) fileName = buf1;\r
5982   if (defName == NULL) {\r
5983     safeStrCpy(fileName, "*.", 3 );\r
5984     strcat(fileName, defExt);\r
5985   } else {\r
5986     safeStrCpy(fileName, defName, MSG_SIZ );\r
5987   }\r
5988     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5989   if (number) *number = 0;\r
5990 \r
5991   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5992   openFileName.hwndOwner         = hwnd;\r
5993   openFileName.hInstance         = (HANDLE) hInst;\r
5994   openFileName.lpstrFilter       = nameFilt;\r
5995   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5996   openFileName.nMaxCustFilter    = 0L;\r
5997   openFileName.nFilterIndex      = 1L;\r
5998   openFileName.lpstrFile         = fileName;\r
5999   openFileName.nMaxFile          = MSG_SIZ;\r
6000   openFileName.lpstrFileTitle    = fileTitle;\r
6001   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6002   openFileName.lpstrInitialDir   = NULL;\r
6003   openFileName.lpstrTitle        = dlgTitle;\r
6004   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6005     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6006     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6007     | (oldDialog ? 0 : OFN_EXPLORER);\r
6008   openFileName.nFileOffset       = 0;\r
6009   openFileName.nFileExtension    = 0;\r
6010   openFileName.lpstrDefExt       = defExt;\r
6011   openFileName.lCustData         = (LONG) number;\r
6012   openFileName.lpfnHook          = oldDialog ?\r
6013     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6014   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6015 \r
6016   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6017                         GetOpenFileName(&openFileName)) {\r
6018     /* open the file */\r
6019     f = fopen(openFileName.lpstrFile, write);\r
6020     if (f == NULL) {\r
6021       MessageBox(hwnd, _("File open failed"), NULL,\r
6022                  MB_OK|MB_ICONEXCLAMATION);\r
6023       return NULL;\r
6024     }\r
6025   } else {\r
6026     int err = CommDlgExtendedError();\r
6027     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
6028     return FALSE;\r
6029   }\r
6030   return f;\r
6031 }\r
6032 \r
6033 \r
6034 \r
6035 VOID APIENTRY\r
6036 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6037 {\r
6038   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6039 \r
6040   /*\r
6041    * Get the first pop-up menu in the menu template. This is the\r
6042    * menu that TrackPopupMenu displays.\r
6043    */\r
6044   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6045   TranslateOneMenu(10, hmenuTrackPopup);\r
6046 \r
6047   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6048 \r
6049   /*\r
6050    * TrackPopup uses screen coordinates, so convert the\r
6051    * coordinates of the mouse click to screen coordinates.\r
6052    */\r
6053   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6054 \r
6055   /* Draw and track the floating pop-up menu. */\r
6056   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6057                  pt.x, pt.y, 0, hwnd, NULL);\r
6058 \r
6059   /* Destroy the menu.*/\r
6060   DestroyMenu(hmenu);\r
6061 }\r
6062    \r
6063 typedef struct {\r
6064   HWND hDlg, hText;\r
6065   int sizeX, sizeY, newSizeX, newSizeY;\r
6066   HDWP hdwp;\r
6067 } ResizeEditPlusButtonsClosure;\r
6068 \r
6069 BOOL CALLBACK\r
6070 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6071 {\r
6072   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6073   RECT rect;\r
6074   POINT pt;\r
6075 \r
6076   if (hChild == cl->hText) return TRUE;\r
6077   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6078   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6079   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6080   ScreenToClient(cl->hDlg, &pt);\r
6081   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6082     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6083   return TRUE;\r
6084 }\r
6085 \r
6086 /* Resize a dialog that has a (rich) edit field filling most of\r
6087    the top, with a row of buttons below */\r
6088 VOID\r
6089 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6090 {\r
6091   RECT rectText;\r
6092   int newTextHeight, newTextWidth;\r
6093   ResizeEditPlusButtonsClosure cl;\r
6094   \r
6095   /*if (IsIconic(hDlg)) return;*/\r
6096   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6097   \r
6098   cl.hdwp = BeginDeferWindowPos(8);\r
6099 \r
6100   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6101   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6102   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6103   if (newTextHeight < 0) {\r
6104     newSizeY += -newTextHeight;\r
6105     newTextHeight = 0;\r
6106   }\r
6107   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6108     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6109 \r
6110   cl.hDlg = hDlg;\r
6111   cl.hText = hText;\r
6112   cl.sizeX = sizeX;\r
6113   cl.sizeY = sizeY;\r
6114   cl.newSizeX = newSizeX;\r
6115   cl.newSizeY = newSizeY;\r
6116   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6117 \r
6118   EndDeferWindowPos(cl.hdwp);\r
6119 }\r
6120 \r
6121 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6122 {\r
6123     RECT    rChild, rParent;\r
6124     int     wChild, hChild, wParent, hParent;\r
6125     int     wScreen, hScreen, xNew, yNew;\r
6126     HDC     hdc;\r
6127 \r
6128     /* Get the Height and Width of the child window */\r
6129     GetWindowRect (hwndChild, &rChild);\r
6130     wChild = rChild.right - rChild.left;\r
6131     hChild = rChild.bottom - rChild.top;\r
6132 \r
6133     /* Get the Height and Width of the parent window */\r
6134     GetWindowRect (hwndParent, &rParent);\r
6135     wParent = rParent.right - rParent.left;\r
6136     hParent = rParent.bottom - rParent.top;\r
6137 \r
6138     /* Get the display limits */\r
6139     hdc = GetDC (hwndChild);\r
6140     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6141     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6142     ReleaseDC(hwndChild, hdc);\r
6143 \r
6144     /* Calculate new X position, then adjust for screen */\r
6145     xNew = rParent.left + ((wParent - wChild) /2);\r
6146     if (xNew < 0) {\r
6147         xNew = 0;\r
6148     } else if ((xNew+wChild) > wScreen) {\r
6149         xNew = wScreen - wChild;\r
6150     }\r
6151 \r
6152     /* Calculate new Y position, then adjust for screen */\r
6153     if( mode == 0 ) {\r
6154         yNew = rParent.top  + ((hParent - hChild) /2);\r
6155     }\r
6156     else {\r
6157         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6158     }\r
6159 \r
6160     if (yNew < 0) {\r
6161         yNew = 0;\r
6162     } else if ((yNew+hChild) > hScreen) {\r
6163         yNew = hScreen - hChild;\r
6164     }\r
6165 \r
6166     /* Set it, and return */\r
6167     return SetWindowPos (hwndChild, NULL,\r
6168                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6169 }\r
6170 \r
6171 /* Center one window over another */\r
6172 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6173 {\r
6174     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6175 }\r
6176 \r
6177 /*---------------------------------------------------------------------------*\\r
6178  *\r
6179  * Startup Dialog functions\r
6180  *\r
6181 \*---------------------------------------------------------------------------*/\r
6182 void\r
6183 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6184 {\r
6185   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6186 \r
6187   while (*cd != NULL) {\r
6188     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6189     cd++;\r
6190   }\r
6191 }\r
6192 \r
6193 void\r
6194 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6195 {\r
6196   char buf1[MAX_ARG_LEN];\r
6197   int len;\r
6198 \r
6199   if (str[0] == '@') {\r
6200     FILE* f = fopen(str + 1, "r");\r
6201     if (f == NULL) {\r
6202       DisplayFatalError(str + 1, errno, 2);\r
6203       return;\r
6204     }\r
6205     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6206     fclose(f);\r
6207     buf1[len] = NULLCHAR;\r
6208     str = buf1;\r
6209   }\r
6210 \r
6211   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6212 \r
6213   for (;;) {\r
6214     char buf[MSG_SIZ];\r
6215     char *end = strchr(str, '\n');\r
6216     if (end == NULL) return;\r
6217     memcpy(buf, str, end - str);\r
6218     buf[end - str] = NULLCHAR;\r
6219     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6220     str = end + 1;\r
6221   }\r
6222 }\r
6223 \r
6224 void\r
6225 SetStartupDialogEnables(HWND hDlg)\r
6226 {\r
6227   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6228     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6229     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6230   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6231     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6232   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6233     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6234   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6235     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6236   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6237     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6238     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6239     IsDlgButtonChecked(hDlg, OPT_View));\r
6240 }\r
6241 \r
6242 char *\r
6243 QuoteForFilename(char *filename)\r
6244 {\r
6245   int dquote, space;\r
6246   dquote = strchr(filename, '"') != NULL;\r
6247   space = strchr(filename, ' ') != NULL;\r
6248   if (dquote || space) {\r
6249     if (dquote) {\r
6250       return "'";\r
6251     } else {\r
6252       return "\"";\r
6253     }\r
6254   } else {\r
6255     return "";\r
6256   }\r
6257 }\r
6258 \r
6259 VOID\r
6260 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6261 {\r
6262   char buf[MSG_SIZ];\r
6263   char *q;\r
6264 \r
6265   InitComboStringsFromOption(hwndCombo, nthnames);\r
6266   q = QuoteForFilename(nthcp);\r
6267     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6268   if (*nthdir != NULLCHAR) {\r
6269     q = QuoteForFilename(nthdir);\r
6270       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6271   }\r
6272   if (*nthcp == NULLCHAR) {\r
6273     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6274   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6275     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6276     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6277   }\r
6278 }\r
6279 \r
6280 LRESULT CALLBACK\r
6281 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6282 {\r
6283   char buf[MSG_SIZ];\r
6284   HANDLE hwndCombo;\r
6285   char *p;\r
6286 \r
6287   switch (message) {\r
6288   case WM_INITDIALOG:\r
6289     /* Center the dialog */\r
6290     CenterWindow (hDlg, GetDesktopWindow());\r
6291     Translate(hDlg, DLG_Startup);\r
6292     /* Initialize the dialog items */\r
6293     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6294                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6295                   firstChessProgramNames);\r
6296     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6297                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6298                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6299     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6300     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6301       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6302     if (*appData.icsHelper != NULLCHAR) {\r
6303       char *q = QuoteForFilename(appData.icsHelper);\r
6304       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6305     }\r
6306     if (*appData.icsHost == NULLCHAR) {\r
6307       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6308       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6309     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6310       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6311       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6312     }\r
6313 \r
6314     if (appData.icsActive) {\r
6315       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6316     }\r
6317     else if (appData.noChessProgram) {\r
6318       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6319     }\r
6320     else {\r
6321       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6322     }\r
6323 \r
6324     SetStartupDialogEnables(hDlg);\r
6325     return TRUE;\r
6326 \r
6327   case WM_COMMAND:\r
6328     switch (LOWORD(wParam)) {\r
6329     case IDOK:\r
6330       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6331         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6332         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6333         p = buf;\r
6334         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6335         ParseArgs(StringGet, &p);\r
6336         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6337         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6338         p = buf;\r
6339         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6340         ParseArgs(StringGet, &p);\r
6341         SwapEngines(singleList); // ... and then make it 'second'\r
6342 \r
6343         appData.noChessProgram = FALSE;\r
6344         appData.icsActive = FALSE;\r
6345       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6346         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6347         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6348         p = buf;\r
6349         ParseArgs(StringGet, &p);\r
6350         if (appData.zippyPlay) {\r
6351           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6352           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6353           p = buf;\r
6354           ParseArgs(StringGet, &p);\r
6355         }\r
6356       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6357         appData.noChessProgram = TRUE;\r
6358         appData.icsActive = FALSE;\r
6359       } else {\r
6360         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6361                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6362         return TRUE;\r
6363       }\r
6364       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6365         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6366         p = buf;\r
6367         ParseArgs(StringGet, &p);\r
6368       }\r
6369       EndDialog(hDlg, TRUE);\r
6370       return TRUE;\r
6371 \r
6372     case IDCANCEL:\r
6373       ExitEvent(0);\r
6374       return TRUE;\r
6375 \r
6376     case IDM_HELPCONTENTS:\r
6377       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6378         MessageBox (GetFocus(),\r
6379                     _("Unable to activate help"),\r
6380                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6381       }\r
6382       break;\r
6383 \r
6384     default:\r
6385       SetStartupDialogEnables(hDlg);\r
6386       break;\r
6387     }\r
6388     break;\r
6389   }\r
6390   return FALSE;\r
6391 }\r
6392 \r
6393 /*---------------------------------------------------------------------------*\\r
6394  *\r
6395  * About box dialog functions\r
6396  *\r
6397 \*---------------------------------------------------------------------------*/\r
6398 \r
6399 /* Process messages for "About" dialog box */\r
6400 LRESULT CALLBACK\r
6401 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6402 {\r
6403   switch (message) {\r
6404   case WM_INITDIALOG: /* message: initialize dialog box */\r
6405     /* Center the dialog over the application window */\r
6406     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6407     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6408     Translate(hDlg, ABOUTBOX);\r
6409     JAWS_COPYRIGHT\r
6410     return (TRUE);\r
6411 \r
6412   case WM_COMMAND: /* message: received a command */\r
6413     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6414         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6415       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6416       return (TRUE);\r
6417     }\r
6418     break;\r
6419   }\r
6420   return (FALSE);\r
6421 }\r
6422 \r
6423 /*---------------------------------------------------------------------------*\\r
6424  *\r
6425  * Comment Dialog functions\r
6426  *\r
6427 \*---------------------------------------------------------------------------*/\r
6428 \r
6429 LRESULT CALLBACK\r
6430 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6431 {\r
6432   static HANDLE hwndText = NULL;\r
6433   int len, newSizeX, newSizeY;\r
6434   static int sizeX, sizeY;\r
6435   char *str;\r
6436   RECT rect;\r
6437   MINMAXINFO *mmi;\r
6438 \r
6439   switch (message) {\r
6440   case WM_INITDIALOG: /* message: initialize dialog box */\r
6441     /* Initialize the dialog items */\r
6442     Translate(hDlg, DLG_EditComment);\r
6443     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6444     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6445     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6446     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6447     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6448     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6449     SetWindowText(hDlg, commentTitle);\r
6450     if (editComment) {\r
6451       SetFocus(hwndText);\r
6452     } else {\r
6453       SetFocus(GetDlgItem(hDlg, IDOK));\r
6454     }\r
6455     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6456                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6457                 MAKELPARAM(FALSE, 0));\r
6458     /* Size and position the dialog */\r
6459     if (!commentDialog) {\r
6460       commentDialog = hDlg;\r
6461       GetClientRect(hDlg, &rect);\r
6462       sizeX = rect.right;\r
6463       sizeY = rect.bottom;\r
6464       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6465           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6466         WINDOWPLACEMENT wp;\r
6467         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6468         wp.length = sizeof(WINDOWPLACEMENT);\r
6469         wp.flags = 0;\r
6470         wp.showCmd = SW_SHOW;\r
6471         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6472         wp.rcNormalPosition.left = wpComment.x;\r
6473         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6474         wp.rcNormalPosition.top = wpComment.y;\r
6475         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6476         SetWindowPlacement(hDlg, &wp);\r
6477 \r
6478         GetClientRect(hDlg, &rect);\r
6479         newSizeX = rect.right;\r
6480         newSizeY = rect.bottom;\r
6481         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6482                               newSizeX, newSizeY);\r
6483         sizeX = newSizeX;\r
6484         sizeY = newSizeY;\r
6485       }\r
6486     }\r
6487     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6488     return FALSE;\r
6489 \r
6490   case WM_COMMAND: /* message: received a command */\r
6491     switch (LOWORD(wParam)) {\r
6492     case IDOK:\r
6493       if (editComment) {\r
6494         char *p, *q;\r
6495         /* Read changed options from the dialog box */\r
6496         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6497         len = GetWindowTextLength(hwndText);\r
6498         str = (char *) malloc(len + 1);\r
6499         GetWindowText(hwndText, str, len + 1);\r
6500         p = q = str;\r
6501         while (*q) {\r
6502           if (*q == '\r')\r
6503             q++;\r
6504           else\r
6505             *p++ = *q++;\r
6506         }\r
6507         *p = NULLCHAR;\r
6508         ReplaceComment(commentIndex, str);\r
6509         free(str);\r
6510       }\r
6511       CommentPopDown();\r
6512       return TRUE;\r
6513 \r
6514     case IDCANCEL:\r
6515     case OPT_CancelComment:\r
6516       CommentPopDown();\r
6517       return TRUE;\r
6518 \r
6519     case OPT_ClearComment:\r
6520       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6521       break;\r
6522 \r
6523     case OPT_EditComment:\r
6524       EditCommentEvent();\r
6525       return TRUE;\r
6526 \r
6527     default:\r
6528       break;\r
6529     }\r
6530     break;\r
6531 \r
6532   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6533         if( wParam == OPT_CommentText ) {\r
6534             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6535 \r
6536             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6537                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6538                 POINTL pt;\r
6539                 LRESULT index;\r
6540 \r
6541                 pt.x = LOWORD( lpMF->lParam );\r
6542                 pt.y = HIWORD( lpMF->lParam );\r
6543 \r
6544                 if(lpMF->msg == WM_CHAR) {\r
6545                         CHARRANGE sel;\r
6546                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6547                         index = sel.cpMin;\r
6548                 } else\r
6549                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6550 \r
6551                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6552                 len = GetWindowTextLength(hwndText);\r
6553                 str = (char *) malloc(len + 1);\r
6554                 GetWindowText(hwndText, str, len + 1);\r
6555                 ReplaceComment(commentIndex, str);\r
6556                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6557                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6558                 free(str);\r
6559 \r
6560                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6561                 lpMF->msg = WM_USER;\r
6562 \r
6563                 return TRUE;\r
6564             }\r
6565         }\r
6566         break;\r
6567 \r
6568   case WM_SIZE:\r
6569     newSizeX = LOWORD(lParam);\r
6570     newSizeY = HIWORD(lParam);\r
6571     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6572     sizeX = newSizeX;\r
6573     sizeY = newSizeY;\r
6574     break;\r
6575 \r
6576   case WM_GETMINMAXINFO:\r
6577     /* Prevent resizing window too small */\r
6578     mmi = (MINMAXINFO *) lParam;\r
6579     mmi->ptMinTrackSize.x = 100;\r
6580     mmi->ptMinTrackSize.y = 100;\r
6581     break;\r
6582   }\r
6583   return FALSE;\r
6584 }\r
6585 \r
6586 VOID\r
6587 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6588 {\r
6589   FARPROC lpProc;\r
6590   char *p, *q;\r
6591 \r
6592   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6593 \r
6594   if (str == NULL) str = "";\r
6595   p = (char *) malloc(2 * strlen(str) + 2);\r
6596   q = p;\r
6597   while (*str) {\r
6598     if (*str == '\n') *q++ = '\r';\r
6599     *q++ = *str++;\r
6600   }\r
6601   *q = NULLCHAR;\r
6602   if (commentText != NULL) free(commentText);\r
6603 \r
6604   commentIndex = index;\r
6605   commentTitle = title;\r
6606   commentText = p;\r
6607   editComment = edit;\r
6608 \r
6609   if (commentDialog) {\r
6610     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6611     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6612   } else {\r
6613     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6614     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6615                  hwndMain, (DLGPROC)lpProc);\r
6616     FreeProcInstance(lpProc);\r
6617   }\r
6618   commentUp = TRUE;\r
6619 }\r
6620 \r
6621 \r
6622 /*---------------------------------------------------------------------------*\\r
6623  *\r
6624  * Type-in move dialog functions\r
6625  * \r
6626 \*---------------------------------------------------------------------------*/\r
6627 \r
6628 LRESULT CALLBACK\r
6629 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6630 {\r
6631   char move[MSG_SIZ];\r
6632   HWND hInput;\r
6633 \r
6634   switch (message) {\r
6635   case WM_INITDIALOG:\r
6636     move[0] = (char) lParam;\r
6637     move[1] = NULLCHAR;\r
6638     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6639     Translate(hDlg, DLG_TypeInMove);\r
6640     hInput = GetDlgItem(hDlg, OPT_Move);\r
6641     SetWindowText(hInput, move);\r
6642     SetFocus(hInput);\r
6643     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6644     return FALSE;\r
6645 \r
6646   case WM_COMMAND:\r
6647     switch (LOWORD(wParam)) {\r
6648     case IDOK:\r
6649 \r
6650       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6651       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6652       TypeInDoneEvent(move);\r
6653       EndDialog(hDlg, TRUE);\r
6654       return TRUE;\r
6655     case IDCANCEL:\r
6656       EndDialog(hDlg, FALSE);\r
6657       return TRUE;\r
6658     default:\r
6659       break;\r
6660     }\r
6661     break;\r
6662   }\r
6663   return FALSE;\r
6664 }\r
6665 \r
6666 VOID\r
6667 PopUpMoveDialog(char firstchar)\r
6668 {\r
6669     FARPROC lpProc;\r
6670 \r
6671       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6672       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6673         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6674       FreeProcInstance(lpProc);\r
6675 }\r
6676 \r
6677 /*---------------------------------------------------------------------------*\\r
6678  *\r
6679  * Type-in name dialog functions\r
6680  * \r
6681 \*---------------------------------------------------------------------------*/\r
6682 \r
6683 LRESULT CALLBACK\r
6684 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6685 {\r
6686   char move[MSG_SIZ];\r
6687   HWND hInput;\r
6688 \r
6689   switch (message) {\r
6690   case WM_INITDIALOG:\r
6691     move[0] = (char) lParam;\r
6692     move[1] = NULLCHAR;\r
6693     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6694     Translate(hDlg, DLG_TypeInName);\r
6695     hInput = GetDlgItem(hDlg, OPT_Name);\r
6696     SetWindowText(hInput, move);\r
6697     SetFocus(hInput);\r
6698     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6699     return FALSE;\r
6700 \r
6701   case WM_COMMAND:\r
6702     switch (LOWORD(wParam)) {\r
6703     case IDOK:\r
6704       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6705       appData.userName = strdup(move);\r
6706       SetUserLogo();\r
6707       SetGameInfo();\r
6708       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6709         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6710         DisplayTitle(move);\r
6711       }\r
6712 \r
6713 \r
6714       EndDialog(hDlg, TRUE);\r
6715       return TRUE;\r
6716     case IDCANCEL:\r
6717       EndDialog(hDlg, FALSE);\r
6718       return TRUE;\r
6719     default:\r
6720       break;\r
6721     }\r
6722     break;\r
6723   }\r
6724   return FALSE;\r
6725 }\r
6726 \r
6727 VOID\r
6728 PopUpNameDialog(char firstchar)\r
6729 {\r
6730     FARPROC lpProc;\r
6731     \r
6732       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6733       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6734         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6735       FreeProcInstance(lpProc);\r
6736 }\r
6737 \r
6738 /*---------------------------------------------------------------------------*\\r
6739  *\r
6740  *  Error dialogs\r
6741  * \r
6742 \*---------------------------------------------------------------------------*/\r
6743 \r
6744 /* Nonmodal error box */\r
6745 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6746                              WPARAM wParam, LPARAM lParam);\r
6747 \r
6748 VOID\r
6749 ErrorPopUp(char *title, char *content)\r
6750 {\r
6751   FARPROC lpProc;\r
6752   char *p, *q;\r
6753   BOOLEAN modal = hwndMain == NULL;\r
6754 \r
6755   p = content;\r
6756   q = errorMessage;\r
6757   while (*p) {\r
6758     if (*p == '\n') {\r
6759       if (modal) {\r
6760         *q++ = ' ';\r
6761         p++;\r
6762       } else {\r
6763         *q++ = '\r';\r
6764         *q++ = *p++;\r
6765       }\r
6766     } else {\r
6767       *q++ = *p++;\r
6768     }\r
6769   }\r
6770   *q = NULLCHAR;\r
6771   strncpy(errorTitle, title, sizeof(errorTitle));\r
6772   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6773   \r
6774   if (modal) {\r
6775     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6776   } else {\r
6777     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6778     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6779                  hwndMain, (DLGPROC)lpProc);\r
6780     FreeProcInstance(lpProc);\r
6781   }\r
6782 }\r
6783 \r
6784 VOID\r
6785 ErrorPopDown()\r
6786 {\r
6787   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6788   if (errorDialog == NULL) return;\r
6789   DestroyWindow(errorDialog);\r
6790   errorDialog = NULL;\r
6791   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6792 }\r
6793 \r
6794 LRESULT CALLBACK\r
6795 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6796 {\r
6797   RECT rChild;\r
6798 \r
6799   switch (message) {\r
6800   case WM_INITDIALOG:\r
6801     GetWindowRect(hDlg, &rChild);\r
6802 \r
6803     /*\r
6804     SetWindowPos(hDlg, NULL, rChild.left,\r
6805       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6806       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6807     */\r
6808 \r
6809     /* \r
6810         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6811         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6812         and it doesn't work when you resize the dialog.\r
6813         For now, just give it a default position.\r
6814     */\r
6815     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6816     Translate(hDlg, DLG_Error);\r
6817 \r
6818     errorDialog = hDlg;\r
6819     SetWindowText(hDlg, errorTitle);\r
6820     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6821     return FALSE;\r
6822 \r
6823   case WM_COMMAND:\r
6824     switch (LOWORD(wParam)) {\r
6825     case IDOK:\r
6826     case IDCANCEL:\r
6827       if (errorDialog == hDlg) errorDialog = NULL;\r
6828       DestroyWindow(hDlg);\r
6829       return TRUE;\r
6830 \r
6831     default:\r
6832       break;\r
6833     }\r
6834     break;\r
6835   }\r
6836   return FALSE;\r
6837 }\r
6838 \r
6839 #ifdef GOTHIC\r
6840 HWND gothicDialog = NULL;\r
6841 \r
6842 LRESULT CALLBACK\r
6843 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6844 {\r
6845   RECT rChild;\r
6846   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6847 \r
6848   switch (message) {\r
6849   case WM_INITDIALOG:\r
6850     GetWindowRect(hDlg, &rChild);\r
6851 \r
6852     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6853                                                              SWP_NOZORDER);\r
6854 \r
6855     /* \r
6856         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6857         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6858         and it doesn't work when you resize the dialog.\r
6859         For now, just give it a default position.\r
6860     */\r
6861     gothicDialog = hDlg;\r
6862     SetWindowText(hDlg, errorTitle);\r
6863     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6864     return FALSE;\r
6865 \r
6866   case WM_COMMAND:\r
6867     switch (LOWORD(wParam)) {\r
6868     case IDOK:\r
6869     case IDCANCEL:\r
6870       if (errorDialog == hDlg) errorDialog = NULL;\r
6871       DestroyWindow(hDlg);\r
6872       return TRUE;\r
6873 \r
6874     default:\r
6875       break;\r
6876     }\r
6877     break;\r
6878   }\r
6879   return FALSE;\r
6880 }\r
6881 \r
6882 VOID\r
6883 GothicPopUp(char *title, VariantClass variant)\r
6884 {\r
6885   FARPROC lpProc;\r
6886   static char *lastTitle;\r
6887 \r
6888   strncpy(errorTitle, title, sizeof(errorTitle));\r
6889   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6890 \r
6891   if(lastTitle != title && gothicDialog != NULL) {\r
6892     DestroyWindow(gothicDialog);\r
6893     gothicDialog = NULL;\r
6894   }\r
6895   if(variant != VariantNormal && gothicDialog == NULL) {\r
6896     title = lastTitle;\r
6897     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6898     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6899                  hwndMain, (DLGPROC)lpProc);\r
6900     FreeProcInstance(lpProc);\r
6901   }\r
6902 }\r
6903 #endif\r
6904 \r
6905 /*---------------------------------------------------------------------------*\\r
6906  *\r
6907  *  Ics Interaction console functions\r
6908  *\r
6909 \*---------------------------------------------------------------------------*/\r
6910 \r
6911 #define HISTORY_SIZE 64\r
6912 static char *history[HISTORY_SIZE];\r
6913 int histIn = 0, histP = 0;\r
6914 \r
6915 \r
6916 VOID\r
6917 SaveInHistory(char *cmd)\r
6918 {\r
6919   if (history[histIn] != NULL) {\r
6920     free(history[histIn]);\r
6921     history[histIn] = NULL;\r
6922   }\r
6923   if (*cmd == NULLCHAR) return;\r
6924   history[histIn] = StrSave(cmd);\r
6925   histIn = (histIn + 1) % HISTORY_SIZE;\r
6926   if (history[histIn] != NULL) {\r
6927     free(history[histIn]);\r
6928 \r
6929     history[histIn] = NULL;\r
6930   }\r
6931   histP = histIn;\r
6932 }\r
6933 \r
6934 char *\r
6935 PrevInHistory(char *cmd)\r
6936 {\r
6937   int newhp;\r
6938   if (histP == histIn) {\r
6939     if (history[histIn] != NULL) free(history[histIn]);\r
6940     history[histIn] = StrSave(cmd);\r
6941   }\r
6942   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6943   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6944   histP = newhp;\r
6945   return history[histP];\r
6946 }\r
6947 \r
6948 char *\r
6949 NextInHistory()\r
6950 {\r
6951   if (histP == histIn) return NULL;\r
6952   histP = (histP + 1) % HISTORY_SIZE;\r
6953   return history[histP];   \r
6954 }\r
6955 \r
6956 HMENU\r
6957 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6958 {\r
6959   HMENU hmenu, h;\r
6960   int i = 0;\r
6961   hmenu = LoadMenu(hInst, "TextMenu");\r
6962   h = GetSubMenu(hmenu, 0);\r
6963   while (e->item) {\r
6964     if (strcmp(e->item, "-") == 0) {\r
6965       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6966     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6967       int flags = MF_STRING, j = 0;\r
6968       if (e->item[0] == '|') {\r
6969         flags |= MF_MENUBARBREAK;\r
6970         j++;\r
6971       }\r
6972       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6973       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6974     }\r
6975     e++;\r
6976     i++;\r
6977   } \r
6978   return hmenu;\r
6979 }\r
6980 \r
6981 WNDPROC consoleTextWindowProc;\r
6982 \r
6983 void\r
6984 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6985 {\r
6986   char buf[MSG_SIZ], name[MSG_SIZ];\r
6987   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6988   CHARRANGE sel;\r
6989 \r
6990   if (!getname) {\r
6991     SetWindowText(hInput, command);\r
6992     if (immediate) {\r
6993       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6994     } else {\r
6995       sel.cpMin = 999999;\r
6996       sel.cpMax = 999999;\r
6997       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6998       SetFocus(hInput);\r
6999     }\r
7000     return;\r
7001   }    \r
7002   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7003   if (sel.cpMin == sel.cpMax) {\r
7004     /* Expand to surrounding word */\r
7005     TEXTRANGE tr;\r
7006     do {\r
7007       tr.chrg.cpMax = sel.cpMin;\r
7008       tr.chrg.cpMin = --sel.cpMin;\r
7009       if (sel.cpMin < 0) break;\r
7010       tr.lpstrText = name;\r
7011       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7012     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7013     sel.cpMin++;\r
7014 \r
7015     do {\r
7016       tr.chrg.cpMin = sel.cpMax;\r
7017       tr.chrg.cpMax = ++sel.cpMax;\r
7018       tr.lpstrText = name;\r
7019       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7020     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7021     sel.cpMax--;\r
7022 \r
7023     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7024       MessageBeep(MB_ICONEXCLAMATION);\r
7025       return;\r
7026     }\r
7027     tr.chrg = sel;\r
7028     tr.lpstrText = name;\r
7029     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7030   } else {\r
7031     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7032       MessageBeep(MB_ICONEXCLAMATION);\r
7033       return;\r
7034     }\r
7035     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7036   }\r
7037   if (immediate) {\r
7038     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7039     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7040     SetWindowText(hInput, buf);\r
7041     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7042   } else {\r
7043     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7044       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7045     SetWindowText(hInput, buf);\r
7046     sel.cpMin = 999999;\r
7047     sel.cpMax = 999999;\r
7048     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7049     SetFocus(hInput);\r
7050   }\r
7051 }\r
7052 \r
7053 LRESULT CALLBACK \r
7054 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7055 {\r
7056   HWND hInput;\r
7057   CHARRANGE sel;\r
7058 \r
7059   switch (message) {\r
7060   case WM_KEYDOWN:\r
7061     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7062     if(wParam=='R') return 0;\r
7063     switch (wParam) {\r
7064     case VK_PRIOR:\r
7065       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7066       return 0;\r
7067     case VK_NEXT:\r
7068       sel.cpMin = 999999;\r
7069       sel.cpMax = 999999;\r
7070       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7071       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7072       return 0;\r
7073     }\r
7074     break;\r
7075   case WM_CHAR:\r
7076    if(wParam != '\022') {\r
7077     if (wParam == '\t') {\r
7078       if (GetKeyState(VK_SHIFT) < 0) {\r
7079         /* shifted */\r
7080         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7081         if (buttonDesc[0].hwnd) {\r
7082           SetFocus(buttonDesc[0].hwnd);\r
7083         } else {\r
7084           SetFocus(hwndMain);\r
7085         }\r
7086       } else {\r
7087         /* unshifted */\r
7088         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7089       }\r
7090     } else {\r
7091       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7092       JAWS_DELETE( SetFocus(hInput); )\r
7093       SendMessage(hInput, message, wParam, lParam);\r
7094     }\r
7095     return 0;\r
7096    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7097    lParam = -1;\r
7098   case WM_RBUTTONDOWN:\r
7099     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7100       /* Move selection here if it was empty */\r
7101       POINT pt;\r
7102       pt.x = LOWORD(lParam);\r
7103       pt.y = HIWORD(lParam);\r
7104       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7105       if (sel.cpMin == sel.cpMax) {\r
7106         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7107         sel.cpMax = sel.cpMin;\r
7108         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7109       }\r
7110       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7111 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7112       POINT pt;\r
7113       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7114       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7115       if (sel.cpMin == sel.cpMax) {\r
7116         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7117         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7118       }\r
7119       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7120         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7121       }\r
7122       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7123       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7124       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7125       MenuPopup(hwnd, pt, hmenu, -1);\r
7126 }\r
7127     }\r
7128     return 0;\r
7129   case WM_RBUTTONUP:\r
7130     if (GetKeyState(VK_SHIFT) & ~1) {\r
7131       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7132         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7133     }\r
7134     return 0;\r
7135   case WM_PASTE:\r
7136     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7137     SetFocus(hInput);\r
7138     return SendMessage(hInput, message, wParam, lParam);\r
7139   case WM_MBUTTONDOWN:\r
7140     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7141   case WM_COMMAND:\r
7142     switch (LOWORD(wParam)) {\r
7143     case IDM_QuickPaste:\r
7144       {\r
7145         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7146         if (sel.cpMin == sel.cpMax) {\r
7147           MessageBeep(MB_ICONEXCLAMATION);\r
7148           return 0;\r
7149         }\r
7150         SendMessage(hwnd, WM_COPY, 0, 0);\r
7151         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7152         SendMessage(hInput, WM_PASTE, 0, 0);\r
7153         SetFocus(hInput);\r
7154         return 0;\r
7155       }\r
7156     case IDM_Cut:\r
7157       SendMessage(hwnd, WM_CUT, 0, 0);\r
7158       return 0;\r
7159     case IDM_Paste:\r
7160       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7161       return 0;\r
7162     case IDM_Copy:\r
7163       SendMessage(hwnd, WM_COPY, 0, 0);\r
7164       return 0;\r
7165     default:\r
7166       {\r
7167         int i = LOWORD(wParam) - IDM_CommandX;\r
7168         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7169             icsTextMenuEntry[i].command != NULL) {\r
7170           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7171                    icsTextMenuEntry[i].getname,\r
7172                    icsTextMenuEntry[i].immediate);\r
7173           return 0;\r
7174         }\r
7175       }\r
7176       break;\r
7177     }\r
7178     break;\r
7179   }\r
7180   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7181 }\r
7182 \r
7183 WNDPROC consoleInputWindowProc;\r
7184 \r
7185 LRESULT CALLBACK\r
7186 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7187 {\r
7188   char buf[MSG_SIZ];\r
7189   char *p;\r
7190   static BOOL sendNextChar = FALSE;\r
7191   static BOOL quoteNextChar = FALSE;\r
7192   InputSource *is = consoleInputSource;\r
7193   CHARFORMAT cf;\r
7194   CHARRANGE sel;\r
7195 \r
7196   switch (message) {\r
7197   case WM_CHAR:\r
7198     if (!appData.localLineEditing || sendNextChar) {\r
7199       is->buf[0] = (CHAR) wParam;\r
7200       is->count = 1;\r
7201       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7202       sendNextChar = FALSE;\r
7203       return 0;\r
7204     }\r
7205     if (quoteNextChar) {\r
7206       buf[0] = (char) wParam;\r
7207       buf[1] = NULLCHAR;\r
7208       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7209       quoteNextChar = FALSE;\r
7210       return 0;\r
7211     }\r
7212     switch (wParam) {\r
7213     case '\r':   /* Enter key */\r
7214       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7215       if (consoleEcho) SaveInHistory(is->buf);\r
7216       is->buf[is->count++] = '\n';\r
7217       is->buf[is->count] = NULLCHAR;\r
7218       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7219       if (consoleEcho) {\r
7220         ConsoleOutput(is->buf, is->count, TRUE);\r
7221       } else if (appData.localLineEditing) {\r
7222         ConsoleOutput("\n", 1, TRUE);\r
7223       }\r
7224       /* fall thru */\r
7225     case '\033': /* Escape key */\r
7226       SetWindowText(hwnd, "");\r
7227       cf.cbSize = sizeof(CHARFORMAT);\r
7228       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7229       if (consoleEcho) {\r
7230         cf.crTextColor = textAttribs[ColorNormal].color;\r
7231       } else {\r
7232         cf.crTextColor = COLOR_ECHOOFF;\r
7233       }\r
7234       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7235       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7236       return 0;\r
7237     case '\t':   /* Tab key */\r
7238       if (GetKeyState(VK_SHIFT) < 0) {\r
7239         /* shifted */\r
7240         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7241       } else {\r
7242         /* unshifted */\r
7243         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7244         if (buttonDesc[0].hwnd) {\r
7245           SetFocus(buttonDesc[0].hwnd);\r
7246         } else {\r
7247           SetFocus(hwndMain);\r
7248         }\r
7249       }\r
7250       return 0;\r
7251     case '\023': /* Ctrl+S */\r
7252       sendNextChar = TRUE;\r
7253       return 0;\r
7254     case '\021': /* Ctrl+Q */\r
7255       quoteNextChar = TRUE;\r
7256       return 0;\r
7257     JAWS_REPLAY\r
7258     default:\r
7259       break;\r
7260     }\r
7261     break;\r
7262   case WM_KEYDOWN:\r
7263     switch (wParam) {\r
7264     case VK_UP:\r
7265       GetWindowText(hwnd, buf, MSG_SIZ);\r
7266       p = PrevInHistory(buf);\r
7267       if (p != NULL) {\r
7268         SetWindowText(hwnd, p);\r
7269         sel.cpMin = 999999;\r
7270         sel.cpMax = 999999;\r
7271         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7272         return 0;\r
7273       }\r
7274       break;\r
7275     case VK_DOWN:\r
7276       p = NextInHistory();\r
7277       if (p != NULL) {\r
7278         SetWindowText(hwnd, p);\r
7279         sel.cpMin = 999999;\r
7280         sel.cpMax = 999999;\r
7281         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7282         return 0;\r
7283       }\r
7284       break;\r
7285     case VK_HOME:\r
7286     case VK_END:\r
7287       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7288       /* fall thru */\r
7289     case VK_PRIOR:\r
7290     case VK_NEXT:\r
7291       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7292       return 0;\r
7293     }\r
7294     break;\r
7295   case WM_MBUTTONDOWN:\r
7296     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7297       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7298     break;\r
7299   case WM_RBUTTONUP:\r
7300     if (GetKeyState(VK_SHIFT) & ~1) {\r
7301       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7302         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7303     } else {\r
7304       POINT pt;\r
7305       HMENU hmenu;\r
7306       hmenu = LoadMenu(hInst, "InputMenu");\r
7307       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7308       if (sel.cpMin == sel.cpMax) {\r
7309         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7310         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7311       }\r
7312       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7313         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7314       }\r
7315       pt.x = LOWORD(lParam);\r
7316       pt.y = HIWORD(lParam);\r
7317       MenuPopup(hwnd, pt, hmenu, -1);\r
7318     }\r
7319     return 0;\r
7320   case WM_COMMAND:\r
7321     switch (LOWORD(wParam)) { \r
7322     case IDM_Undo:\r
7323       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7324       return 0;\r
7325     case IDM_SelectAll:\r
7326       sel.cpMin = 0;\r
7327       sel.cpMax = -1; /*999999?*/\r
7328       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7329       return 0;\r
7330     case IDM_Cut:\r
7331       SendMessage(hwnd, WM_CUT, 0, 0);\r
7332       return 0;\r
7333     case IDM_Paste:\r
7334       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7335       return 0;\r
7336     case IDM_Copy:\r
7337       SendMessage(hwnd, WM_COPY, 0, 0);\r
7338       return 0;\r
7339     }\r
7340     break;\r
7341   }\r
7342   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7343 }\r
7344 \r
7345 #define CO_MAX  100000\r
7346 #define CO_TRIM   1000\r
7347 \r
7348 LRESULT CALLBACK\r
7349 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7350 {\r
7351   static SnapData sd;\r
7352   HWND hText, hInput;\r
7353   RECT rect;\r
7354   static int sizeX, sizeY;\r
7355   int newSizeX, newSizeY;\r
7356   MINMAXINFO *mmi;\r
7357   WORD wMask;\r
7358 \r
7359   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7360   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7361 \r
7362   switch (message) {\r
7363   case WM_NOTIFY:\r
7364     if (((NMHDR*)lParam)->code == EN_LINK)\r
7365     {\r
7366       ENLINK *pLink = (ENLINK*)lParam;\r
7367       if (pLink->msg == WM_LBUTTONUP)\r
7368       {\r
7369         TEXTRANGE tr;\r
7370 \r
7371         tr.chrg = pLink->chrg;\r
7372         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7373         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7374         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7375         free(tr.lpstrText);\r
7376       }\r
7377     }\r
7378     break;\r
7379   case WM_INITDIALOG: /* message: initialize dialog box */\r
7380     hwndConsole = hDlg;\r
7381     SetFocus(hInput);\r
7382     consoleTextWindowProc = (WNDPROC)\r
7383       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7384     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7385     consoleInputWindowProc = (WNDPROC)\r
7386       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7387     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7388     Colorize(ColorNormal, TRUE);\r
7389     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7390     ChangedConsoleFont();\r
7391     GetClientRect(hDlg, &rect);\r
7392     sizeX = rect.right;\r
7393     sizeY = rect.bottom;\r
7394     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7395         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7396       WINDOWPLACEMENT wp;\r
7397       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7398       wp.length = sizeof(WINDOWPLACEMENT);\r
7399       wp.flags = 0;\r
7400       wp.showCmd = SW_SHOW;\r
7401       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7402       wp.rcNormalPosition.left = wpConsole.x;\r
7403       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7404       wp.rcNormalPosition.top = wpConsole.y;\r
7405       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7406       SetWindowPlacement(hDlg, &wp);\r
7407     }\r
7408 \r
7409    // [HGM] Chessknight's change 2004-07-13\r
7410    else { /* Determine Defaults */\r
7411        WINDOWPLACEMENT wp;\r
7412        wpConsole.x = wpMain.width + 1;\r
7413        wpConsole.y = wpMain.y;\r
7414        wpConsole.width = screenWidth -  wpMain.width;\r
7415        wpConsole.height = wpMain.height;\r
7416        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7417        wp.length = sizeof(WINDOWPLACEMENT);\r
7418        wp.flags = 0;\r
7419        wp.showCmd = SW_SHOW;\r
7420        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7421        wp.rcNormalPosition.left = wpConsole.x;\r
7422        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7423        wp.rcNormalPosition.top = wpConsole.y;\r
7424        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7425        SetWindowPlacement(hDlg, &wp);\r
7426     }\r
7427 \r
7428    // Allow hText to highlight URLs and send notifications on them\r
7429    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7430    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7431    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7432    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7433 \r
7434     return FALSE;\r
7435 \r
7436   case WM_SETFOCUS:\r
7437     SetFocus(hInput);\r
7438     return 0;\r
7439 \r
7440   case WM_CLOSE:\r
7441     ExitEvent(0);\r
7442     /* not reached */\r
7443     break;\r
7444 \r
7445   case WM_SIZE:\r
7446     if (IsIconic(hDlg)) break;\r
7447     newSizeX = LOWORD(lParam);\r
7448     newSizeY = HIWORD(lParam);\r
7449     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7450       RECT rectText, rectInput;\r
7451       POINT pt;\r
7452       int newTextHeight, newTextWidth;\r
7453       GetWindowRect(hText, &rectText);\r
7454       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7455       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7456       if (newTextHeight < 0) {\r
7457         newSizeY += -newTextHeight;\r
7458         newTextHeight = 0;\r
7459       }\r
7460       SetWindowPos(hText, NULL, 0, 0,\r
7461         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7462       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7463       pt.x = rectInput.left;\r
7464       pt.y = rectInput.top + newSizeY - sizeY;\r
7465       ScreenToClient(hDlg, &pt);\r
7466       SetWindowPos(hInput, NULL, \r
7467         pt.x, pt.y, /* needs client coords */   \r
7468         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7469         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7470     }\r
7471     sizeX = newSizeX;\r
7472     sizeY = newSizeY;\r
7473     break;\r
7474 \r
7475   case WM_GETMINMAXINFO:\r
7476     /* Prevent resizing window too small */\r
7477     mmi = (MINMAXINFO *) lParam;\r
7478     mmi->ptMinTrackSize.x = 100;\r
7479     mmi->ptMinTrackSize.y = 100;\r
7480     break;\r
7481 \r
7482   /* [AS] Snapping */\r
7483   case WM_ENTERSIZEMOVE:\r
7484     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7485 \r
7486   case WM_SIZING:\r
7487     return OnSizing( &sd, hDlg, wParam, lParam );\r
7488 \r
7489   case WM_MOVING:\r
7490     return OnMoving( &sd, hDlg, wParam, lParam );\r
7491 \r
7492   case WM_EXITSIZEMOVE:\r
7493         UpdateICSWidth(hText);\r
7494     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7495   }\r
7496 \r
7497   return DefWindowProc(hDlg, message, wParam, lParam);\r
7498 }\r
7499 \r
7500 \r
7501 VOID\r
7502 ConsoleCreate()\r
7503 {\r
7504   HWND hCons;\r
7505   if (hwndConsole) return;\r
7506   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7507   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7508 }\r
7509 \r
7510 \r
7511 VOID\r
7512 ConsoleOutput(char* data, int length, int forceVisible)\r
7513 {\r
7514   HWND hText;\r
7515   int trim, exlen;\r
7516   char *p, *q;\r
7517   char buf[CO_MAX+1];\r
7518   POINT pEnd;\r
7519   RECT rect;\r
7520   static int delayLF = 0;\r
7521   CHARRANGE savesel, sel;\r
7522 \r
7523   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7524   p = data;\r
7525   q = buf;\r
7526   if (delayLF) {\r
7527     *q++ = '\r';\r
7528     *q++ = '\n';\r
7529     delayLF = 0;\r
7530   }\r
7531   while (length--) {\r
7532     if (*p == '\n') {\r
7533       if (*++p) {\r
7534         *q++ = '\r';\r
7535         *q++ = '\n';\r
7536       } else {\r
7537         delayLF = 1;\r
7538       }\r
7539     } else if (*p == '\007') {\r
7540        MyPlaySound(&sounds[(int)SoundBell]);\r
7541        p++;\r
7542     } else {\r
7543       *q++ = *p++;\r
7544     }\r
7545   }\r
7546   *q = NULLCHAR;\r
7547   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7548   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7549   /* Save current selection */\r
7550   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7551   exlen = GetWindowTextLength(hText);\r
7552   /* Find out whether current end of text is visible */\r
7553   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7554   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7555   /* Trim existing text if it's too long */\r
7556   if (exlen + (q - buf) > CO_MAX) {\r
7557     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7558     sel.cpMin = 0;\r
7559     sel.cpMax = trim;\r
7560     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7561     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7562     exlen -= trim;\r
7563     savesel.cpMin -= trim;\r
7564     savesel.cpMax -= trim;\r
7565     if (exlen < 0) exlen = 0;\r
7566     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7567     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7568   }\r
7569   /* Append the new text */\r
7570   sel.cpMin = exlen;\r
7571   sel.cpMax = exlen;\r
7572   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7573   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7574   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7575   if (forceVisible || exlen == 0 ||\r
7576       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7577        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7578     /* Scroll to make new end of text visible if old end of text\r
7579        was visible or new text is an echo of user typein */\r
7580     sel.cpMin = 9999999;\r
7581     sel.cpMax = 9999999;\r
7582     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7583     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7584     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7585     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7586   }\r
7587   if (savesel.cpMax == exlen || forceVisible) {\r
7588     /* Move insert point to new end of text if it was at the old\r
7589        end of text or if the new text is an echo of user typein */\r
7590     sel.cpMin = 9999999;\r
7591     sel.cpMax = 9999999;\r
7592     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7593   } else {\r
7594     /* Restore previous selection */\r
7595     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7596   }\r
7597   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7598 }\r
7599 \r
7600 /*---------*/\r
7601 \r
7602 \r
7603 void\r
7604 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7605 {\r
7606   char buf[100];\r
7607   char *str;\r
7608   COLORREF oldFg, oldBg;\r
7609   HFONT oldFont;\r
7610   RECT rect;\r
7611 \r
7612   if(copyNumber > 1)\r
7613     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7614 \r
7615   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7616   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7617   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7618 \r
7619   rect.left = x;\r
7620   rect.right = x + squareSize;\r
7621   rect.top  = y;\r
7622   rect.bottom = y + squareSize;\r
7623   str = buf;\r
7624 \r
7625   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7626                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7627              y, ETO_CLIPPED|ETO_OPAQUE,\r
7628              &rect, str, strlen(str), NULL);\r
7629 \r
7630   (void) SetTextColor(hdc, oldFg);\r
7631   (void) SetBkColor(hdc, oldBg);\r
7632   (void) SelectObject(hdc, oldFont);\r
7633 }\r
7634 \r
7635 void\r
7636 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7637               RECT *rect, char *color, char *flagFell)\r
7638 {\r
7639   char buf[100];\r
7640   char *str;\r
7641   COLORREF oldFg, oldBg;\r
7642   HFONT oldFont;\r
7643 \r
7644   if (twoBoards && partnerUp) return;\r
7645   if (appData.clockMode) {\r
7646     if (tinyLayout)\r
7647       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7648     else\r
7649       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7650     str = buf;\r
7651   } else {\r
7652     str = color;\r
7653   }\r
7654 \r
7655   if (highlight) {\r
7656     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7657     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7658   } else {\r
7659     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7660     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7661   }\r
7662   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7663 \r
7664   JAWS_SILENCE\r
7665 \r
7666   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7667              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7668              rect, str, strlen(str), NULL);\r
7669   if(logoHeight > 0 && appData.clockMode) {\r
7670       RECT r;\r
7671       str += strlen(color)+2;\r
7672       r.top = rect->top + logoHeight/2;\r
7673       r.left = rect->left;\r
7674       r.right = rect->right;\r
7675       r.bottom = rect->bottom;\r
7676       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7677                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7678                  &r, str, strlen(str), NULL);\r
7679   }\r
7680   (void) SetTextColor(hdc, oldFg);\r
7681   (void) SetBkColor(hdc, oldBg);\r
7682   (void) SelectObject(hdc, oldFont);\r
7683 }\r
7684 \r
7685 \r
7686 int\r
7687 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7688            OVERLAPPED *ovl)\r
7689 {\r
7690   int ok, err;\r
7691 \r
7692   /* [AS]  */\r
7693   if( count <= 0 ) {\r
7694     if (appData.debugMode) {\r
7695       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7696     }\r
7697 \r
7698     return ERROR_INVALID_USER_BUFFER;\r
7699   }\r
7700 \r
7701   ResetEvent(ovl->hEvent);\r
7702   ovl->Offset = ovl->OffsetHigh = 0;\r
7703   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7704   if (ok) {\r
7705     err = NO_ERROR;\r
7706   } else {\r
7707     err = GetLastError();\r
7708     if (err == ERROR_IO_PENDING) {\r
7709       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7710       if (ok)\r
7711         err = NO_ERROR;\r
7712       else\r
7713         err = GetLastError();\r
7714     }\r
7715   }\r
7716   return err;\r
7717 }\r
7718 \r
7719 int\r
7720 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7721             OVERLAPPED *ovl)\r
7722 {\r
7723   int ok, err;\r
7724 \r
7725   ResetEvent(ovl->hEvent);\r
7726   ovl->Offset = ovl->OffsetHigh = 0;\r
7727   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7728   if (ok) {\r
7729     err = NO_ERROR;\r
7730   } else {\r
7731     err = GetLastError();\r
7732     if (err == ERROR_IO_PENDING) {\r
7733       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7734       if (ok)\r
7735         err = NO_ERROR;\r
7736       else\r
7737         err = GetLastError();\r
7738     }\r
7739 \r
7740   }\r
7741   return err;\r
7742 }\r
7743 \r
7744 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7745 void CheckForInputBufferFull( InputSource * is )\r
7746 {\r
7747     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7748         /* Look for end of line */\r
7749         char * p = is->buf;\r
7750         \r
7751         while( p < is->next && *p != '\n' ) {\r
7752             p++;\r
7753         }\r
7754 \r
7755         if( p >= is->next ) {\r
7756             if (appData.debugMode) {\r
7757                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7758             }\r
7759 \r
7760             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7761             is->count = (DWORD) -1;\r
7762             is->next = is->buf;\r
7763         }\r
7764     }\r
7765 }\r
7766 \r
7767 DWORD\r
7768 InputThread(LPVOID arg)\r
7769 {\r
7770   InputSource *is;\r
7771   OVERLAPPED ovl;\r
7772 \r
7773   is = (InputSource *) arg;\r
7774   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7775   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7776   while (is->hThread != NULL) {\r
7777     is->error = DoReadFile(is->hFile, is->next,\r
7778                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7779                            &is->count, &ovl);\r
7780     if (is->error == NO_ERROR) {\r
7781       is->next += is->count;\r
7782     } else {\r
7783       if (is->error == ERROR_BROKEN_PIPE) {\r
7784         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7785         is->count = 0;\r
7786       } else {\r
7787         is->count = (DWORD) -1;\r
7788         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7789         break; \r
7790       }\r
7791     }\r
7792 \r
7793     CheckForInputBufferFull( is );\r
7794 \r
7795     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7796 \r
7797     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7798 \r
7799     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7800   }\r
7801 \r
7802   CloseHandle(ovl.hEvent);\r
7803   CloseHandle(is->hFile);\r
7804 \r
7805   if (appData.debugMode) {\r
7806     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7807   }\r
7808 \r
7809   return 0;\r
7810 }\r
7811 \r
7812 \r
7813 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7814 DWORD\r
7815 NonOvlInputThread(LPVOID arg)\r
7816 {\r
7817   InputSource *is;\r
7818   char *p, *q;\r
7819   int i;\r
7820   char prev;\r
7821 \r
7822   is = (InputSource *) arg;\r
7823   while (is->hThread != NULL) {\r
7824     is->error = ReadFile(is->hFile, is->next,\r
7825                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7826                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7827     if (is->error == NO_ERROR) {\r
7828       /* Change CRLF to LF */\r
7829       if (is->next > is->buf) {\r
7830         p = is->next - 1;\r
7831         i = is->count + 1;\r
7832       } else {\r
7833         p = is->next;\r
7834         i = is->count;\r
7835       }\r
7836       q = p;\r
7837       prev = NULLCHAR;\r
7838       while (i > 0) {\r
7839         if (prev == '\r' && *p == '\n') {\r
7840           *(q-1) = '\n';\r
7841           is->count--;\r
7842         } else { \r
7843           *q++ = *p;\r
7844         }\r
7845         prev = *p++;\r
7846         i--;\r
7847       }\r
7848       *q = NULLCHAR;\r
7849       is->next = q;\r
7850     } else {\r
7851       if (is->error == ERROR_BROKEN_PIPE) {\r
7852         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7853         is->count = 0; \r
7854       } else {\r
7855         is->count = (DWORD) -1;\r
7856       }\r
7857     }\r
7858 \r
7859     CheckForInputBufferFull( is );\r
7860 \r
7861     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7862 \r
7863     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7864 \r
7865     if (is->count < 0) break;  /* Quit on error */\r
7866   }\r
7867   CloseHandle(is->hFile);\r
7868   return 0;\r
7869 }\r
7870 \r
7871 DWORD\r
7872 SocketInputThread(LPVOID arg)\r
7873 {\r
7874   InputSource *is;\r
7875 \r
7876   is = (InputSource *) arg;\r
7877   while (is->hThread != NULL) {\r
7878     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7879     if ((int)is->count == SOCKET_ERROR) {\r
7880       is->count = (DWORD) -1;\r
7881       is->error = WSAGetLastError();\r
7882     } else {\r
7883       is->error = NO_ERROR;\r
7884       is->next += is->count;\r
7885       if (is->count == 0 && is->second == is) {\r
7886         /* End of file on stderr; quit with no message */\r
7887         break;\r
7888       }\r
7889     }\r
7890     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7891 \r
7892     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7893 \r
7894     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7895   }\r
7896   return 0;\r
7897 }\r
7898 \r
7899 VOID\r
7900 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7901 {\r
7902   InputSource *is;\r
7903 \r
7904   is = (InputSource *) lParam;\r
7905   if (is->lineByLine) {\r
7906     /* Feed in lines one by one */\r
7907     char *p = is->buf;\r
7908     char *q = p;\r
7909     while (q < is->next) {\r
7910       if (*q++ == '\n') {\r
7911         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7912         p = q;\r
7913       }\r
7914     }\r
7915     \r
7916     /* Move any partial line to the start of the buffer */\r
7917     q = is->buf;\r
7918     while (p < is->next) {\r
7919       *q++ = *p++;\r
7920     }\r
7921     is->next = q;\r
7922 \r
7923     if (is->error != NO_ERROR || is->count == 0) {\r
7924       /* Notify backend of the error.  Note: If there was a partial\r
7925          line at the end, it is not flushed through. */\r
7926       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7927     }\r
7928   } else {\r
7929     /* Feed in the whole chunk of input at once */\r
7930     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7931     is->next = is->buf;\r
7932   }\r
7933 }\r
7934 \r
7935 /*---------------------------------------------------------------------------*\\r
7936  *\r
7937  *  Menu enables. Used when setting various modes.\r
7938  *\r
7939 \*---------------------------------------------------------------------------*/\r
7940 \r
7941 typedef struct {\r
7942   int item;\r
7943   int flags;\r
7944 } Enables;\r
7945 \r
7946 VOID\r
7947 GreyRevert(Boolean grey)\r
7948 { // [HGM] vari: for retracting variations in local mode\r
7949   HMENU hmenu = GetMenu(hwndMain);\r
7950   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7951   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7952 }\r
7953 \r
7954 VOID\r
7955 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7956 {\r
7957   while (enab->item > 0) {\r
7958     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7959     enab++;\r
7960   }\r
7961 }\r
7962 \r
7963 Enables gnuEnables[] = {\r
7964   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7969   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7977 \r
7978   // Needed to switch from ncp to GNU mode on Engine Load\r
7979   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7980   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7981   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7982   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7983   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7984   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7985   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7986   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7989   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7990   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7991   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7992   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7993   { -1, -1 }\r
7994 };\r
7995 \r
7996 Enables icsEnables[] = {\r
7997   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8001   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8002   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8003   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8004   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8005   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8013   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8016   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
8017   { -1, -1 }\r
8018 };\r
8019 \r
8020 #if ZIPPY\r
8021 Enables zippyEnables[] = {\r
8022   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8023   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8024   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8025   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8026   { -1, -1 }\r
8027 };\r
8028 #endif\r
8029 \r
8030 Enables ncpEnables[] = {\r
8031   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8032   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8033   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8034   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8035   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8036   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8037   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8038   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8039   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8040   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8041   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8042   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8043   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8044   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8045   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8046   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8047   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8048   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8049   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8050   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8051   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8052   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8053   { -1, -1 }\r
8054 };\r
8055 \r
8056 Enables trainingOnEnables[] = {\r
8057   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8058   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8059   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8060   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8065   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8066   { -1, -1 }\r
8067 };\r
8068 \r
8069 Enables trainingOffEnables[] = {\r
8070   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8071   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8072   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8073   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8074   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8075   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8076   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8077   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8078   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8079   { -1, -1 }\r
8080 };\r
8081 \r
8082 /* These modify either ncpEnables or gnuEnables */\r
8083 Enables cmailEnables[] = {\r
8084   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8085   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8086   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8087   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8088   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8089   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8090   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8091   { -1, -1 }\r
8092 };\r
8093 \r
8094 Enables machineThinkingEnables[] = {\r
8095   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8096   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8097   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8098   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8099   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8100   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8101   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8102   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8103   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8104   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8105   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8106   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8107   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8108 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8109   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8110   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8111   { -1, -1 }\r
8112 };\r
8113 \r
8114 Enables userThinkingEnables[] = {\r
8115   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8116   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8117   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8118   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8119   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8120   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8121   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8122   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8123   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8124   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8125   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8126   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8127   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8128 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8129   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8130   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8131   { -1, -1 }\r
8132 };\r
8133 \r
8134 /*---------------------------------------------------------------------------*\\r
8135  *\r
8136  *  Front-end interface functions exported by XBoard.\r
8137  *  Functions appear in same order as prototypes in frontend.h.\r
8138  * \r
8139 \*---------------------------------------------------------------------------*/\r
8140 VOID\r
8141 CheckMark(UINT item, int state)\r
8142 {\r
8143     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8144 }\r
8145 \r
8146 VOID\r
8147 ModeHighlight()\r
8148 {\r
8149   static UINT prevChecked = 0;\r
8150   static int prevPausing = 0;\r
8151   UINT nowChecked;\r
8152 \r
8153   if (pausing != prevPausing) {\r
8154     prevPausing = pausing;\r
8155     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8156                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8157     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8158   }\r
8159 \r
8160   switch (gameMode) {\r
8161   case BeginningOfGame:\r
8162     if (appData.icsActive)\r
8163       nowChecked = IDM_IcsClient;\r
8164     else if (appData.noChessProgram)\r
8165       nowChecked = IDM_EditGame;\r
8166     else\r
8167       nowChecked = IDM_MachineBlack;\r
8168     break;\r
8169   case MachinePlaysBlack:\r
8170     nowChecked = IDM_MachineBlack;\r
8171     break;\r
8172   case MachinePlaysWhite:\r
8173     nowChecked = IDM_MachineWhite;\r
8174     break;\r
8175   case TwoMachinesPlay:\r
8176     nowChecked = IDM_TwoMachines;\r
8177     break;\r
8178   case AnalyzeMode:\r
8179     nowChecked = IDM_AnalysisMode;\r
8180     break;\r
8181   case AnalyzeFile:\r
8182     nowChecked = IDM_AnalyzeFile;\r
8183     break;\r
8184   case EditGame:\r
8185     nowChecked = IDM_EditGame;\r
8186     break;\r
8187   case PlayFromGameFile:\r
8188     nowChecked = IDM_LoadGame;\r
8189     break;\r
8190   case EditPosition:\r
8191     nowChecked = IDM_EditPosition;\r
8192     break;\r
8193   case Training:\r
8194     nowChecked = IDM_Training;\r
8195     break;\r
8196   case IcsPlayingWhite:\r
8197   case IcsPlayingBlack:\r
8198   case IcsObserving:\r
8199   case IcsIdle:\r
8200     nowChecked = IDM_IcsClient;\r
8201     break;\r
8202   default:\r
8203   case EndOfGame:\r
8204     nowChecked = 0;\r
8205     break;\r
8206   }\r
8207   CheckMark(prevChecked, MF_UNCHECKED);\r
8208   CheckMark(nowChecked, MF_CHECKED);\r
8209   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8210 \r
8211   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8212     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8213                           MF_BYCOMMAND|MF_ENABLED);\r
8214   } else {\r
8215     (void) EnableMenuItem(GetMenu(hwndMain), \r
8216                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8217   }\r
8218 \r
8219   prevChecked = nowChecked;\r
8220 \r
8221   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8222   if (appData.icsActive) {\r
8223        if (appData.icsEngineAnalyze) {\r
8224                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8225        } else {\r
8226                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8227        }\r
8228   }\r
8229   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8230 }\r
8231 \r
8232 VOID\r
8233 SetICSMode()\r
8234 {\r
8235   HMENU hmenu = GetMenu(hwndMain);\r
8236   SetMenuEnables(hmenu, icsEnables);\r
8237   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8238     MF_BYCOMMAND|MF_ENABLED);\r
8239 #if ZIPPY\r
8240   if (appData.zippyPlay) {\r
8241     SetMenuEnables(hmenu, zippyEnables);\r
8242     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8243          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8244           MF_BYCOMMAND|MF_ENABLED);\r
8245   }\r
8246 #endif\r
8247 }\r
8248 \r
8249 VOID\r
8250 SetGNUMode()\r
8251 {\r
8252   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8253 }\r
8254 \r
8255 VOID\r
8256 SetNCPMode()\r
8257 {\r
8258   HMENU hmenu = GetMenu(hwndMain);\r
8259   SetMenuEnables(hmenu, ncpEnables);\r
8260     DrawMenuBar(hwndMain);\r
8261 }\r
8262 \r
8263 VOID\r
8264 SetCmailMode()\r
8265 {\r
8266   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8267 }\r
8268 \r
8269 VOID \r
8270 SetTrainingModeOn()\r
8271 {\r
8272   int i;\r
8273   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8274   for (i = 0; i < N_BUTTONS; i++) {\r
8275     if (buttonDesc[i].hwnd != NULL)\r
8276       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8277   }\r
8278   CommentPopDown();\r
8279 }\r
8280 \r
8281 VOID SetTrainingModeOff()\r
8282 {\r
8283   int i;\r
8284   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8285   for (i = 0; i < N_BUTTONS; i++) {\r
8286     if (buttonDesc[i].hwnd != NULL)\r
8287       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8288   }\r
8289 }\r
8290 \r
8291 \r
8292 VOID\r
8293 SetUserThinkingEnables()\r
8294 {\r
8295   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8296 }\r
8297 \r
8298 VOID\r
8299 SetMachineThinkingEnables()\r
8300 {\r
8301   HMENU hMenu = GetMenu(hwndMain);\r
8302   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8303 \r
8304   SetMenuEnables(hMenu, machineThinkingEnables);\r
8305 \r
8306   if (gameMode == MachinePlaysBlack) {\r
8307     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8308   } else if (gameMode == MachinePlaysWhite) {\r
8309     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8310   } else if (gameMode == TwoMachinesPlay) {\r
8311     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8312   }\r
8313 }\r
8314 \r
8315 \r
8316 VOID\r
8317 DisplayTitle(char *str)\r
8318 {\r
8319   char title[MSG_SIZ], *host;\r
8320   if (str[0] != NULLCHAR) {\r
8321     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8322   } else if (appData.icsActive) {\r
8323     if (appData.icsCommPort[0] != NULLCHAR)\r
8324       host = "ICS";\r
8325     else \r
8326       host = appData.icsHost;\r
8327       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8328   } else if (appData.noChessProgram) {\r
8329     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8330   } else {\r
8331     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8332     strcat(title, ": ");\r
8333     strcat(title, first.tidy);\r
8334   }\r
8335   SetWindowText(hwndMain, title);\r
8336 }\r
8337 \r
8338 \r
8339 VOID\r
8340 DisplayMessage(char *str1, char *str2)\r
8341 {\r
8342   HDC hdc;\r
8343   HFONT oldFont;\r
8344   int remain = MESSAGE_TEXT_MAX - 1;\r
8345   int len;\r
8346 \r
8347   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8348   messageText[0] = NULLCHAR;\r
8349   if (*str1) {\r
8350     len = strlen(str1);\r
8351     if (len > remain) len = remain;\r
8352     strncpy(messageText, str1, len);\r
8353     messageText[len] = NULLCHAR;\r
8354     remain -= len;\r
8355   }\r
8356   if (*str2 && remain >= 2) {\r
8357     if (*str1) {\r
8358       strcat(messageText, "  ");\r
8359       remain -= 2;\r
8360     }\r
8361     len = strlen(str2);\r
8362     if (len > remain) len = remain;\r
8363     strncat(messageText, str2, len);\r
8364   }\r
8365   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8366   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8367 \r
8368   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8369 \r
8370   SAYMACHINEMOVE();\r
8371 \r
8372   hdc = GetDC(hwndMain);\r
8373   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8374   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8375              &messageRect, messageText, strlen(messageText), NULL);\r
8376   (void) SelectObject(hdc, oldFont);\r
8377   (void) ReleaseDC(hwndMain, hdc);\r
8378 }\r
8379 \r
8380 VOID\r
8381 DisplayError(char *str, int error)\r
8382 {\r
8383   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8384   int len;\r
8385 \r
8386   if (error == 0) {\r
8387     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8388   } else {\r
8389     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8390                         NULL, error, LANG_NEUTRAL,\r
8391                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8392     if (len > 0) {\r
8393       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8394     } else {\r
8395       ErrorMap *em = errmap;\r
8396       while (em->err != 0 && em->err != error) em++;\r
8397       if (em->err != 0) {\r
8398         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8399       } else {\r
8400         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8401       }\r
8402     }\r
8403   }\r
8404   \r
8405   ErrorPopUp(_("Error"), buf);\r
8406 }\r
8407 \r
8408 \r
8409 VOID\r
8410 DisplayMoveError(char *str)\r
8411 {\r
8412   fromX = fromY = -1;\r
8413   ClearHighlights();\r
8414   DrawPosition(FALSE, NULL);\r
8415   if (appData.popupMoveErrors) {\r
8416     ErrorPopUp(_("Error"), str);\r
8417   } else {\r
8418     DisplayMessage(str, "");\r
8419     moveErrorMessageUp = TRUE;\r
8420   }\r
8421 }\r
8422 \r
8423 VOID\r
8424 DisplayFatalError(char *str, int error, int exitStatus)\r
8425 {\r
8426   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8427   int len;\r
8428   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8429 \r
8430   if (error != 0) {\r
8431     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8432                         NULL, error, LANG_NEUTRAL,\r
8433                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8434     if (len > 0) {\r
8435       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8436     } else {\r
8437       ErrorMap *em = errmap;\r
8438       while (em->err != 0 && em->err != error) em++;\r
8439       if (em->err != 0) {\r
8440         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8441       } else {\r
8442         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8443       }\r
8444     }\r
8445     str = buf;\r
8446   }\r
8447   if (appData.debugMode) {\r
8448     fprintf(debugFP, "%s: %s\n", label, str);\r
8449   }\r
8450   if (appData.popupExitMessage) {\r
8451     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8452                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8453   }\r
8454   ExitEvent(exitStatus);\r
8455 }\r
8456 \r
8457 \r
8458 VOID\r
8459 DisplayInformation(char *str)\r
8460 {\r
8461   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8462 }\r
8463 \r
8464 \r
8465 VOID\r
8466 DisplayNote(char *str)\r
8467 {\r
8468   ErrorPopUp(_("Note"), str);\r
8469 }\r
8470 \r
8471 \r
8472 typedef struct {\r
8473   char *title, *question, *replyPrefix;\r
8474   ProcRef pr;\r
8475 } QuestionParams;\r
8476 \r
8477 LRESULT CALLBACK\r
8478 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8479 {\r
8480   static QuestionParams *qp;\r
8481   char reply[MSG_SIZ];\r
8482   int len, err;\r
8483 \r
8484   switch (message) {\r
8485   case WM_INITDIALOG:\r
8486     qp = (QuestionParams *) lParam;\r
8487     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8488     Translate(hDlg, DLG_Question);\r
8489     SetWindowText(hDlg, qp->title);\r
8490     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8491     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8492     return FALSE;\r
8493 \r
8494   case WM_COMMAND:\r
8495     switch (LOWORD(wParam)) {\r
8496     case IDOK:\r
8497       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8498       if (*reply) strcat(reply, " ");\r
8499       len = strlen(reply);\r
8500       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8501       strcat(reply, "\n");\r
8502       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8503       EndDialog(hDlg, TRUE);\r
8504       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8505       return TRUE;\r
8506     case IDCANCEL:\r
8507       EndDialog(hDlg, FALSE);\r
8508       return TRUE;\r
8509     default:\r
8510       break;\r
8511     }\r
8512     break;\r
8513   }\r
8514   return FALSE;\r
8515 }\r
8516 \r
8517 VOID\r
8518 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8519 {\r
8520     QuestionParams qp;\r
8521     FARPROC lpProc;\r
8522     \r
8523     qp.title = title;\r
8524     qp.question = question;\r
8525     qp.replyPrefix = replyPrefix;\r
8526     qp.pr = pr;\r
8527     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8528     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8529       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8530     FreeProcInstance(lpProc);\r
8531 }\r
8532 \r
8533 /* [AS] Pick FRC position */\r
8534 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8535 {\r
8536     static int * lpIndexFRC;\r
8537     BOOL index_is_ok;\r
8538     char buf[16];\r
8539 \r
8540     switch( message )\r
8541     {\r
8542     case WM_INITDIALOG:\r
8543         lpIndexFRC = (int *) lParam;\r
8544 \r
8545         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8546         Translate(hDlg, DLG_NewGameFRC);\r
8547 \r
8548         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8549         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8550         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8551         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8552 \r
8553         break;\r
8554 \r
8555     case WM_COMMAND:\r
8556         switch( LOWORD(wParam) ) {\r
8557         case IDOK:\r
8558             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8559             EndDialog( hDlg, 0 );\r
8560             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8561             return TRUE;\r
8562         case IDCANCEL:\r
8563             EndDialog( hDlg, 1 );   \r
8564             return TRUE;\r
8565         case IDC_NFG_Edit:\r
8566             if( HIWORD(wParam) == EN_CHANGE ) {\r
8567                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8568 \r
8569                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8570             }\r
8571             return TRUE;\r
8572         case IDC_NFG_Random:\r
8573           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8574             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8575             return TRUE;\r
8576         }\r
8577 \r
8578         break;\r
8579     }\r
8580 \r
8581     return FALSE;\r
8582 }\r
8583 \r
8584 int NewGameFRC()\r
8585 {\r
8586     int result;\r
8587     int index = appData.defaultFrcPosition;\r
8588     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8589 \r
8590     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8591 \r
8592     if( result == 0 ) {\r
8593         appData.defaultFrcPosition = index;\r
8594     }\r
8595 \r
8596     return result;\r
8597 }\r
8598 \r
8599 /* [AS] Game list options. Refactored by HGM */\r
8600 \r
8601 HWND gameListOptionsDialog;\r
8602 \r
8603 // low-level front-end: clear text edit / list widget\r
8604 void\r
8605 \r
8606 GLT_ClearList()\r
8607 {\r
8608     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8609 }\r
8610 \r
8611 // low-level front-end: clear text edit / list widget\r
8612 void\r
8613 GLT_DeSelectList()\r
8614 {\r
8615     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8616 }\r
8617 \r
8618 // low-level front-end: append line to text edit / list widget\r
8619 void\r
8620 GLT_AddToList( char *name )\r
8621 {\r
8622     if( name != 0 ) {\r
8623             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8624     }\r
8625 }\r
8626 \r
8627 // low-level front-end: get line from text edit / list widget\r
8628 Boolean\r
8629 GLT_GetFromList( int index, char *name )\r
8630 {\r
8631     if( name != 0 ) {\r
8632             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8633                 return TRUE;\r
8634     }\r
8635     return FALSE;\r
8636 }\r
8637 \r
8638 void GLT_MoveSelection( HWND hDlg, int delta )\r
8639 {\r
8640     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8641     int idx2 = idx1 + delta;\r
8642     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8643 \r
8644     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8645         char buf[128];\r
8646 \r
8647         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8648         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8649         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8650         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8651     }\r
8652 }\r
8653 \r
8654 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8655 {\r
8656     switch( message )\r
8657     {\r
8658     case WM_INITDIALOG:\r
8659         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8660         \r
8661         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8662         Translate(hDlg, DLG_GameListOptions);\r
8663 \r
8664         /* Initialize list */\r
8665         GLT_TagsToList( lpUserGLT );\r
8666 \r
8667         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8668 \r
8669         break;\r
8670 \r
8671     case WM_COMMAND:\r
8672         switch( LOWORD(wParam) ) {\r
8673         case IDOK:\r
8674             GLT_ParseList();\r
8675             EndDialog( hDlg, 0 );\r
8676             return TRUE;\r
8677         case IDCANCEL:\r
8678             EndDialog( hDlg, 1 );\r
8679             return TRUE;\r
8680 \r
8681         case IDC_GLT_Default:\r
8682             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8683             return TRUE;\r
8684 \r
8685         case IDC_GLT_Restore:\r
8686             GLT_TagsToList( appData.gameListTags );\r
8687             return TRUE;\r
8688 \r
8689         case IDC_GLT_Up:\r
8690             GLT_MoveSelection( hDlg, -1 );\r
8691             return TRUE;\r
8692 \r
8693         case IDC_GLT_Down:\r
8694             GLT_MoveSelection( hDlg, +1 );\r
8695             return TRUE;\r
8696         }\r
8697 \r
8698         break;\r
8699     }\r
8700 \r
8701     return FALSE;\r
8702 }\r
8703 \r
8704 int GameListOptions()\r
8705 {\r
8706     int result;\r
8707     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8708 \r
8709       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8710 \r
8711     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8712 \r
8713     if( result == 0 ) {\r
8714         /* [AS] Memory leak here! */\r
8715         appData.gameListTags = strdup( lpUserGLT ); \r
8716     }\r
8717 \r
8718     return result;\r
8719 }\r
8720 \r
8721 VOID\r
8722 DisplayIcsInteractionTitle(char *str)\r
8723 {\r
8724   char consoleTitle[MSG_SIZ];\r
8725 \r
8726     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8727     SetWindowText(hwndConsole, consoleTitle);\r
8728 \r
8729     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8730       char buf[MSG_SIZ], *p = buf, *q;\r
8731         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8732       do {\r
8733         q = strchr(p, ';');\r
8734         if(q) *q++ = 0;\r
8735         if(*p) ChatPopUp(p);\r
8736       } while(p=q);\r
8737     }\r
8738 \r
8739     SetActiveWindow(hwndMain);\r
8740 }\r
8741 \r
8742 void\r
8743 DrawPosition(int fullRedraw, Board board)\r
8744 {\r
8745   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8746 }\r
8747 \r
8748 void NotifyFrontendLogin()\r
8749 {\r
8750         if (hwndConsole)\r
8751                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8752 }\r
8753 \r
8754 VOID\r
8755 ResetFrontEnd()\r
8756 {\r
8757   fromX = fromY = -1;\r
8758   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8759     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8760     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8761     dragInfo.lastpos = dragInfo.pos;\r
8762     dragInfo.start.x = dragInfo.start.y = -1;\r
8763     dragInfo.from = dragInfo.start;\r
8764     ReleaseCapture();\r
8765     DrawPosition(TRUE, NULL);\r
8766   }\r
8767   TagsPopDown();\r
8768 }\r
8769 \r
8770 \r
8771 VOID\r
8772 CommentPopUp(char *title, char *str)\r
8773 {\r
8774   HWND hwnd = GetActiveWindow();\r
8775   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8776   SAY(str);\r
8777   SetActiveWindow(hwnd);\r
8778 }\r
8779 \r
8780 VOID\r
8781 CommentPopDown(void)\r
8782 {\r
8783   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8784   if (commentDialog) {\r
8785     ShowWindow(commentDialog, SW_HIDE);\r
8786   }\r
8787   commentUp = FALSE;\r
8788 }\r
8789 \r
8790 VOID\r
8791 EditCommentPopUp(int index, char *title, char *str)\r
8792 {\r
8793   EitherCommentPopUp(index, title, str, TRUE);\r
8794 }\r
8795 \r
8796 \r
8797 int\r
8798 Roar()\r
8799 {\r
8800   MyPlaySound(&sounds[(int)SoundRoar]);\r
8801   return 1;\r
8802 }\r
8803 \r
8804 VOID\r
8805 RingBell()\r
8806 {\r
8807   MyPlaySound(&sounds[(int)SoundMove]);\r
8808 }\r
8809 \r
8810 VOID PlayIcsWinSound()\r
8811 {\r
8812   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8813 }\r
8814 \r
8815 VOID PlayIcsLossSound()\r
8816 {\r
8817   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8818 }\r
8819 \r
8820 VOID PlayIcsDrawSound()\r
8821 {\r
8822   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8823 }\r
8824 \r
8825 VOID PlayIcsUnfinishedSound()\r
8826 {\r
8827   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8828 }\r
8829 \r
8830 VOID\r
8831 PlayAlarmSound()\r
8832 {\r
8833   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8834 }\r
8835 \r
8836 VOID\r
8837 PlayTellSound()\r
8838 {\r
8839   MyPlaySound(&textAttribs[ColorTell].sound);\r
8840 }\r
8841 \r
8842 \r
8843 VOID\r
8844 EchoOn()\r
8845 {\r
8846   HWND hInput;\r
8847   consoleEcho = TRUE;\r
8848   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8849   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8850   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8851 }\r
8852 \r
8853 \r
8854 VOID\r
8855 EchoOff()\r
8856 {\r
8857   CHARFORMAT cf;\r
8858   HWND hInput;\r
8859   consoleEcho = FALSE;\r
8860   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8861   /* This works OK: set text and background both to the same color */\r
8862   cf = consoleCF;\r
8863   cf.crTextColor = COLOR_ECHOOFF;\r
8864   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8865   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8866 }\r
8867 \r
8868 /* No Raw()...? */\r
8869 \r
8870 void Colorize(ColorClass cc, int continuation)\r
8871 {\r
8872   currentColorClass = cc;\r
8873   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8874   consoleCF.crTextColor = textAttribs[cc].color;\r
8875   consoleCF.dwEffects = textAttribs[cc].effects;\r
8876   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8877 }\r
8878 \r
8879 char *\r
8880 UserName()\r
8881 {\r
8882   static char buf[MSG_SIZ];\r
8883   DWORD bufsiz = MSG_SIZ;\r
8884 \r
8885   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8886         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8887   }\r
8888   if (!GetUserName(buf, &bufsiz)) {\r
8889     /*DisplayError("Error getting user name", GetLastError());*/\r
8890     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8891   }\r
8892   return buf;\r
8893 }\r
8894 \r
8895 char *\r
8896 HostName()\r
8897 {\r
8898   static char buf[MSG_SIZ];\r
8899   DWORD bufsiz = MSG_SIZ;\r
8900 \r
8901   if (!GetComputerName(buf, &bufsiz)) {\r
8902     /*DisplayError("Error getting host name", GetLastError());*/\r
8903     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8904   }\r
8905   return buf;\r
8906 }\r
8907 \r
8908 \r
8909 int\r
8910 ClockTimerRunning()\r
8911 {\r
8912   return clockTimerEvent != 0;\r
8913 }\r
8914 \r
8915 int\r
8916 StopClockTimer()\r
8917 {\r
8918   if (clockTimerEvent == 0) return FALSE;\r
8919   KillTimer(hwndMain, clockTimerEvent);\r
8920   clockTimerEvent = 0;\r
8921   return TRUE;\r
8922 }\r
8923 \r
8924 void\r
8925 StartClockTimer(long millisec)\r
8926 {\r
8927   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8928                              (UINT) millisec, NULL);\r
8929 }\r
8930 \r
8931 void\r
8932 DisplayWhiteClock(long timeRemaining, int highlight)\r
8933 {\r
8934   HDC hdc;\r
8935   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8936 \r
8937   if(appData.noGUI) return;\r
8938   hdc = GetDC(hwndMain);\r
8939   if (!IsIconic(hwndMain)) {\r
8940     DisplayAClock(hdc, timeRemaining, highlight, \r
8941                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8942   }\r
8943   if (highlight && iconCurrent == iconBlack) {\r
8944     iconCurrent = iconWhite;\r
8945     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8946     if (IsIconic(hwndMain)) {\r
8947       DrawIcon(hdc, 2, 2, iconCurrent);\r
8948     }\r
8949   }\r
8950   (void) ReleaseDC(hwndMain, hdc);\r
8951   if (hwndConsole)\r
8952     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8953 }\r
8954 \r
8955 void\r
8956 DisplayBlackClock(long timeRemaining, int highlight)\r
8957 {\r
8958   HDC hdc;\r
8959   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8960 \r
8961 \r
8962   if(appData.noGUI) return;\r
8963   hdc = GetDC(hwndMain);\r
8964   if (!IsIconic(hwndMain)) {\r
8965     DisplayAClock(hdc, timeRemaining, highlight, \r
8966                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8967   }\r
8968   if (highlight && iconCurrent == iconWhite) {\r
8969     iconCurrent = iconBlack;\r
8970     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8971     if (IsIconic(hwndMain)) {\r
8972       DrawIcon(hdc, 2, 2, iconCurrent);\r
8973     }\r
8974   }\r
8975   (void) ReleaseDC(hwndMain, hdc);\r
8976   if (hwndConsole)\r
8977     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8978 }\r
8979 \r
8980 \r
8981 int\r
8982 LoadGameTimerRunning()\r
8983 {\r
8984   return loadGameTimerEvent != 0;\r
8985 }\r
8986 \r
8987 int\r
8988 StopLoadGameTimer()\r
8989 {\r
8990   if (loadGameTimerEvent == 0) return FALSE;\r
8991   KillTimer(hwndMain, loadGameTimerEvent);\r
8992   loadGameTimerEvent = 0;\r
8993   return TRUE;\r
8994 }\r
8995 \r
8996 void\r
8997 StartLoadGameTimer(long millisec)\r
8998 {\r
8999   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9000                                 (UINT) millisec, NULL);\r
9001 }\r
9002 \r
9003 void\r
9004 AutoSaveGame()\r
9005 {\r
9006   char *defName;\r
9007   FILE *f;\r
9008   char fileTitle[MSG_SIZ];\r
9009 \r
9010   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9011   f = OpenFileDialog(hwndMain, "a", defName,\r
9012                      appData.oldSaveStyle ? "gam" : "pgn",\r
9013                      GAME_FILT, \r
9014                      _("Save Game to File"), NULL, fileTitle, NULL);\r
9015   if (f != NULL) {\r
9016     SaveGame(f, 0, "");\r
9017     fclose(f);\r
9018   }\r
9019 }\r
9020 \r
9021 \r
9022 void\r
9023 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9024 {\r
9025   if (delayedTimerEvent != 0) {\r
9026     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9027       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9028     }\r
9029     KillTimer(hwndMain, delayedTimerEvent);\r
9030     delayedTimerEvent = 0;\r
9031     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9032     delayedTimerCallback();\r
9033   }\r
9034   delayedTimerCallback = cb;\r
9035   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9036                                 (UINT) millisec, NULL);\r
9037 }\r
9038 \r
9039 DelayedEventCallback\r
9040 GetDelayedEvent()\r
9041 {\r
9042   if (delayedTimerEvent) {\r
9043     return delayedTimerCallback;\r
9044   } else {\r
9045     return NULL;\r
9046   }\r
9047 }\r
9048 \r
9049 void\r
9050 CancelDelayedEvent()\r
9051 {\r
9052   if (delayedTimerEvent) {\r
9053     KillTimer(hwndMain, delayedTimerEvent);\r
9054     delayedTimerEvent = 0;\r
9055   }\r
9056 }\r
9057 \r
9058 DWORD GetWin32Priority(int nice)\r
9059 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9060 /*\r
9061 REALTIME_PRIORITY_CLASS     0x00000100\r
9062 HIGH_PRIORITY_CLASS         0x00000080\r
9063 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9064 NORMAL_PRIORITY_CLASS       0x00000020\r
9065 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9066 IDLE_PRIORITY_CLASS         0x00000040\r
9067 */\r
9068         if (nice < -15) return 0x00000080;\r
9069         if (nice < 0)   return 0x00008000;\r
9070         if (nice == 0)  return 0x00000020;\r
9071         if (nice < 15)  return 0x00004000;\r
9072         return 0x00000040;\r
9073 }\r
9074 \r
9075 void RunCommand(char *cmdLine)\r
9076 {\r
9077   /* Now create the child process. */\r
9078   STARTUPINFO siStartInfo;\r
9079   PROCESS_INFORMATION piProcInfo;\r
9080 \r
9081   siStartInfo.cb = sizeof(STARTUPINFO);\r
9082   siStartInfo.lpReserved = NULL;\r
9083   siStartInfo.lpDesktop = NULL;\r
9084   siStartInfo.lpTitle = NULL;\r
9085   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9086   siStartInfo.cbReserved2 = 0;\r
9087   siStartInfo.lpReserved2 = NULL;\r
9088   siStartInfo.hStdInput = NULL;\r
9089   siStartInfo.hStdOutput = NULL;\r
9090   siStartInfo.hStdError = NULL;\r
9091 \r
9092   CreateProcess(NULL,\r
9093                 cmdLine,           /* command line */\r
9094                 NULL,      /* process security attributes */\r
9095                 NULL,      /* primary thread security attrs */\r
9096                 TRUE,      /* handles are inherited */\r
9097                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9098                 NULL,      /* use parent's environment */\r
9099                 NULL,\r
9100                 &siStartInfo, /* STARTUPINFO pointer */\r
9101                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9102 \r
9103   CloseHandle(piProcInfo.hThread);\r
9104 }\r
9105 \r
9106 /* Start a child process running the given program.\r
9107    The process's standard output can be read from "from", and its\r
9108    standard input can be written to "to".\r
9109    Exit with fatal error if anything goes wrong.\r
9110    Returns an opaque pointer that can be used to destroy the process\r
9111    later.\r
9112 */\r
9113 int\r
9114 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9115 {\r
9116 #define BUFSIZE 4096\r
9117 \r
9118   HANDLE hChildStdinRd, hChildStdinWr,\r
9119     hChildStdoutRd, hChildStdoutWr;\r
9120   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9121   SECURITY_ATTRIBUTES saAttr;\r
9122   BOOL fSuccess;\r
9123   PROCESS_INFORMATION piProcInfo;\r
9124   STARTUPINFO siStartInfo;\r
9125   ChildProc *cp;\r
9126   char buf[MSG_SIZ];\r
9127   DWORD err;\r
9128 \r
9129   if (appData.debugMode) {\r
9130     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9131   }\r
9132 \r
9133   *pr = NoProc;\r
9134 \r
9135   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9136   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9137   saAttr.bInheritHandle = TRUE;\r
9138   saAttr.lpSecurityDescriptor = NULL;\r
9139 \r
9140   /*\r
9141    * The steps for redirecting child's STDOUT:\r
9142    *     1. Create anonymous pipe to be STDOUT for child.\r
9143    *     2. Create a noninheritable duplicate of read handle,\r
9144    *         and close the inheritable read handle.\r
9145    */\r
9146 \r
9147   /* Create a pipe for the child's STDOUT. */\r
9148   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9149     return GetLastError();\r
9150   }\r
9151 \r
9152   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9153   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9154                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9155                              FALSE,     /* not inherited */\r
9156                              DUPLICATE_SAME_ACCESS);\r
9157   if (! fSuccess) {\r
9158     return GetLastError();\r
9159   }\r
9160   CloseHandle(hChildStdoutRd);\r
9161 \r
9162   /*\r
9163    * The steps for redirecting child's STDIN:\r
9164    *     1. Create anonymous pipe to be STDIN for child.\r
9165    *     2. Create a noninheritable duplicate of write handle,\r
9166    *         and close the inheritable write handle.\r
9167    */\r
9168 \r
9169   /* Create a pipe for the child's STDIN. */\r
9170   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9171     return GetLastError();\r
9172   }\r
9173 \r
9174   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9175   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9176                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9177                              FALSE,     /* not inherited */\r
9178                              DUPLICATE_SAME_ACCESS);\r
9179   if (! fSuccess) {\r
9180     return GetLastError();\r
9181   }\r
9182   CloseHandle(hChildStdinWr);\r
9183 \r
9184   /* Arrange to (1) look in dir for the child .exe file, and\r
9185    * (2) have dir be the child's working directory.  Interpret\r
9186    * dir relative to the directory WinBoard loaded from. */\r
9187   GetCurrentDirectory(MSG_SIZ, buf);\r
9188   SetCurrentDirectory(installDir);\r
9189   SetCurrentDirectory(dir);\r
9190 \r
9191   /* Now create the child process. */\r
9192 \r
9193   siStartInfo.cb = sizeof(STARTUPINFO);\r
9194   siStartInfo.lpReserved = NULL;\r
9195   siStartInfo.lpDesktop = NULL;\r
9196   siStartInfo.lpTitle = NULL;\r
9197   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9198   siStartInfo.cbReserved2 = 0;\r
9199   siStartInfo.lpReserved2 = NULL;\r
9200   siStartInfo.hStdInput = hChildStdinRd;\r
9201   siStartInfo.hStdOutput = hChildStdoutWr;\r
9202   siStartInfo.hStdError = hChildStdoutWr;\r
9203 \r
9204   fSuccess = CreateProcess(NULL,\r
9205                            cmdLine,        /* command line */\r
9206                            NULL,           /* process security attributes */\r
9207                            NULL,           /* primary thread security attrs */\r
9208                            TRUE,           /* handles are inherited */\r
9209                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9210                            NULL,           /* use parent's environment */\r
9211                            NULL,\r
9212                            &siStartInfo, /* STARTUPINFO pointer */\r
9213                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9214 \r
9215   err = GetLastError();\r
9216   SetCurrentDirectory(buf); /* return to prev directory */\r
9217   if (! fSuccess) {\r
9218     return err;\r
9219   }\r
9220 \r
9221   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9222     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9223     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9224   }\r
9225 \r
9226   /* Close the handles we don't need in the parent */\r
9227   CloseHandle(piProcInfo.hThread);\r
9228   CloseHandle(hChildStdinRd);\r
9229   CloseHandle(hChildStdoutWr);\r
9230 \r
9231   /* Prepare return value */\r
9232   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9233   cp->kind = CPReal;\r
9234   cp->hProcess = piProcInfo.hProcess;\r
9235   cp->pid = piProcInfo.dwProcessId;\r
9236   cp->hFrom = hChildStdoutRdDup;\r
9237   cp->hTo = hChildStdinWrDup;\r
9238 \r
9239   *pr = (void *) cp;\r
9240 \r
9241   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9242      2000 where engines sometimes don't see the initial command(s)\r
9243      from WinBoard and hang.  I don't understand how that can happen,\r
9244      but the Sleep is harmless, so I've put it in.  Others have also\r
9245      reported what may be the same problem, so hopefully this will fix\r
9246      it for them too.  */\r
9247   Sleep(500);\r
9248 \r
9249   return NO_ERROR;\r
9250 }\r
9251 \r
9252 \r
9253 void\r
9254 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9255 {\r
9256   ChildProc *cp; int result;\r
9257 \r
9258   cp = (ChildProc *) pr;\r
9259   if (cp == NULL) return;\r
9260 \r
9261   switch (cp->kind) {\r
9262   case CPReal:\r
9263     /* TerminateProcess is considered harmful, so... */\r
9264     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9265     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9266     /* The following doesn't work because the chess program\r
9267        doesn't "have the same console" as WinBoard.  Maybe\r
9268        we could arrange for this even though neither WinBoard\r
9269        nor the chess program uses a console for stdio? */\r
9270     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9271 \r
9272     /* [AS] Special termination modes for misbehaving programs... */\r
9273     if( signal & 8 ) { \r
9274         result = TerminateProcess( cp->hProcess, 0 );\r
9275 \r
9276         if ( appData.debugMode) {\r
9277             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9278         }\r
9279     }\r
9280     else if( signal & 4 ) {\r
9281         DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most\r
9282 \r
9283         if( dw != WAIT_OBJECT_0 ) {\r
9284             result = TerminateProcess( cp->hProcess, 0 );\r
9285 \r
9286             if ( appData.debugMode) {\r
9287                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9288             }\r
9289 \r
9290         }\r
9291     }\r
9292 \r
9293     CloseHandle(cp->hProcess);\r
9294     break;\r
9295 \r
9296   case CPComm:\r
9297     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9298     break;\r
9299 \r
9300   case CPSock:\r
9301     closesocket(cp->sock);\r
9302     WSACleanup();\r
9303     break;\r
9304 \r
9305   case CPRcmd:\r
9306     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9307     closesocket(cp->sock);\r
9308     closesocket(cp->sock2);\r
9309     WSACleanup();\r
9310     break;\r
9311   }\r
9312   free(cp);\r
9313 }\r
9314 \r
9315 void\r
9316 InterruptChildProcess(ProcRef pr)\r
9317 {\r
9318   ChildProc *cp;\r
9319 \r
9320   cp = (ChildProc *) pr;\r
9321   if (cp == NULL) return;\r
9322   switch (cp->kind) {\r
9323   case CPReal:\r
9324     /* The following doesn't work because the chess program\r
9325        doesn't "have the same console" as WinBoard.  Maybe\r
9326        we could arrange for this even though neither WinBoard\r
9327        nor the chess program uses a console for stdio */\r
9328     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9329     break;\r
9330 \r
9331   case CPComm:\r
9332   case CPSock:\r
9333     /* Can't interrupt */\r
9334     break;\r
9335 \r
9336   case CPRcmd:\r
9337     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9338     break;\r
9339   }\r
9340 }\r
9341 \r
9342 \r
9343 int\r
9344 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9345 {\r
9346   char cmdLine[MSG_SIZ];\r
9347 \r
9348   if (port[0] == NULLCHAR) {\r
9349     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9350   } else {\r
9351     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9352   }\r
9353   return StartChildProcess(cmdLine, "", pr);\r
9354 }\r
9355 \r
9356 \r
9357 /* Code to open TCP sockets */\r
9358 \r
9359 int\r
9360 OpenTCP(char *host, char *port, ProcRef *pr)\r
9361 {\r
9362   ChildProc *cp;\r
9363   int err;\r
9364   SOCKET s;\r
9365 \r
9366   struct sockaddr_in sa, mysa;\r
9367   struct hostent FAR *hp;\r
9368   unsigned short uport;\r
9369   WORD wVersionRequested;\r
9370   WSADATA wsaData;\r
9371 \r
9372   /* Initialize socket DLL */\r
9373   wVersionRequested = MAKEWORD(1, 1);\r
9374   err = WSAStartup(wVersionRequested, &wsaData);\r
9375   if (err != 0) return err;\r
9376 \r
9377   /* Make socket */\r
9378   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9379     err = WSAGetLastError();\r
9380     WSACleanup();\r
9381     return err;\r
9382   }\r
9383 \r
9384   /* Bind local address using (mostly) don't-care values.\r
9385    */\r
9386   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9387   mysa.sin_family = AF_INET;\r
9388   mysa.sin_addr.s_addr = INADDR_ANY;\r
9389   uport = (unsigned short) 0;\r
9390   mysa.sin_port = htons(uport);\r
9391   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9392       == SOCKET_ERROR) {\r
9393     err = WSAGetLastError();\r
9394     WSACleanup();\r
9395     return err;\r
9396   }\r
9397 \r
9398   /* Resolve remote host name */\r
9399   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9400   if (!(hp = gethostbyname(host))) {\r
9401     unsigned int b0, b1, b2, b3;\r
9402 \r
9403     err = WSAGetLastError();\r
9404 \r
9405     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9406       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9407       hp->h_addrtype = AF_INET;\r
9408       hp->h_length = 4;\r
9409       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9410       hp->h_addr_list[0] = (char *) malloc(4);\r
9411       hp->h_addr_list[0][0] = (char) b0;\r
9412       hp->h_addr_list[0][1] = (char) b1;\r
9413       hp->h_addr_list[0][2] = (char) b2;\r
9414       hp->h_addr_list[0][3] = (char) b3;\r
9415     } else {\r
9416       WSACleanup();\r
9417       return err;\r
9418     }\r
9419   }\r
9420   sa.sin_family = hp->h_addrtype;\r
9421   uport = (unsigned short) atoi(port);\r
9422   sa.sin_port = htons(uport);\r
9423   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9424 \r
9425   /* Make connection */\r
9426   if (connect(s, (struct sockaddr *) &sa,\r
9427               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9428     err = WSAGetLastError();\r
9429     WSACleanup();\r
9430     return err;\r
9431   }\r
9432 \r
9433   /* Prepare return value */\r
9434   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9435   cp->kind = CPSock;\r
9436   cp->sock = s;\r
9437   *pr = (ProcRef *) cp;\r
9438 \r
9439   return NO_ERROR;\r
9440 }\r
9441 \r
9442 int\r
9443 OpenCommPort(char *name, ProcRef *pr)\r
9444 {\r
9445   HANDLE h;\r
9446   COMMTIMEOUTS ct;\r
9447   ChildProc *cp;\r
9448   char fullname[MSG_SIZ];\r
9449 \r
9450   if (*name != '\\')\r
9451     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9452   else\r
9453     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9454 \r
9455   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9456                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9457   if (h == (HANDLE) -1) {\r
9458     return GetLastError();\r
9459   }\r
9460   hCommPort = h;\r
9461 \r
9462   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9463 \r
9464   /* Accumulate characters until a 100ms pause, then parse */\r
9465   ct.ReadIntervalTimeout = 100;\r
9466   ct.ReadTotalTimeoutMultiplier = 0;\r
9467   ct.ReadTotalTimeoutConstant = 0;\r
9468   ct.WriteTotalTimeoutMultiplier = 0;\r
9469   ct.WriteTotalTimeoutConstant = 0;\r
9470   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9471 \r
9472   /* Prepare return value */\r
9473   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9474   cp->kind = CPComm;\r
9475   cp->hFrom = h;\r
9476   cp->hTo = h;\r
9477   *pr = (ProcRef *) cp;\r
9478 \r
9479   return NO_ERROR;\r
9480 }\r
9481 \r
9482 int\r
9483 OpenLoopback(ProcRef *pr)\r
9484 {\r
9485   DisplayFatalError(_("Not implemented"), 0, 1);\r
9486   return NO_ERROR;\r
9487 }\r
9488 \r
9489 \r
9490 int\r
9491 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9492 {\r
9493   ChildProc *cp;\r
9494   int err;\r
9495   SOCKET s, s2, s3;\r
9496   struct sockaddr_in sa, mysa;\r
9497   struct hostent FAR *hp;\r
9498   unsigned short uport;\r
9499   WORD wVersionRequested;\r
9500   WSADATA wsaData;\r
9501   int fromPort;\r
9502   char stderrPortStr[MSG_SIZ];\r
9503 \r
9504   /* Initialize socket DLL */\r
9505   wVersionRequested = MAKEWORD(1, 1);\r
9506   err = WSAStartup(wVersionRequested, &wsaData);\r
9507   if (err != 0) return err;\r
9508 \r
9509   /* Resolve remote host name */\r
9510   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9511   if (!(hp = gethostbyname(host))) {\r
9512     unsigned int b0, b1, b2, b3;\r
9513 \r
9514     err = WSAGetLastError();\r
9515 \r
9516     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9517       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9518       hp->h_addrtype = AF_INET;\r
9519       hp->h_length = 4;\r
9520       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9521       hp->h_addr_list[0] = (char *) malloc(4);\r
9522       hp->h_addr_list[0][0] = (char) b0;\r
9523       hp->h_addr_list[0][1] = (char) b1;\r
9524       hp->h_addr_list[0][2] = (char) b2;\r
9525       hp->h_addr_list[0][3] = (char) b3;\r
9526     } else {\r
9527       WSACleanup();\r
9528       return err;\r
9529     }\r
9530   }\r
9531   sa.sin_family = hp->h_addrtype;\r
9532   uport = (unsigned short) 514;\r
9533   sa.sin_port = htons(uport);\r
9534   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9535 \r
9536   /* Bind local socket to unused "privileged" port address\r
9537    */\r
9538   s = INVALID_SOCKET;\r
9539   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9540   mysa.sin_family = AF_INET;\r
9541   mysa.sin_addr.s_addr = INADDR_ANY;\r
9542   for (fromPort = 1023;; fromPort--) {\r
9543     if (fromPort < 0) {\r
9544       WSACleanup();\r
9545       return WSAEADDRINUSE;\r
9546     }\r
9547     if (s == INVALID_SOCKET) {\r
9548       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9549         err = WSAGetLastError();\r
9550         WSACleanup();\r
9551         return err;\r
9552       }\r
9553     }\r
9554     uport = (unsigned short) fromPort;\r
9555     mysa.sin_port = htons(uport);\r
9556     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9557         == SOCKET_ERROR) {\r
9558       err = WSAGetLastError();\r
9559       if (err == WSAEADDRINUSE) continue;\r
9560       WSACleanup();\r
9561       return err;\r
9562     }\r
9563     if (connect(s, (struct sockaddr *) &sa,\r
9564       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9565       err = WSAGetLastError();\r
9566       if (err == WSAEADDRINUSE) {\r
9567         closesocket(s);\r
9568         s = -1;\r
9569         continue;\r
9570       }\r
9571       WSACleanup();\r
9572       return err;\r
9573     }\r
9574     break;\r
9575   }\r
9576 \r
9577   /* Bind stderr local socket to unused "privileged" port address\r
9578    */\r
9579   s2 = INVALID_SOCKET;\r
9580   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9581   mysa.sin_family = AF_INET;\r
9582   mysa.sin_addr.s_addr = INADDR_ANY;\r
9583   for (fromPort = 1023;; fromPort--) {\r
9584     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9585     if (fromPort < 0) {\r
9586       (void) closesocket(s);\r
9587       WSACleanup();\r
9588       return WSAEADDRINUSE;\r
9589     }\r
9590     if (s2 == INVALID_SOCKET) {\r
9591       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9592         err = WSAGetLastError();\r
9593         closesocket(s);\r
9594         WSACleanup();\r
9595         return err;\r
9596       }\r
9597     }\r
9598     uport = (unsigned short) fromPort;\r
9599     mysa.sin_port = htons(uport);\r
9600     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9601         == SOCKET_ERROR) {\r
9602       err = WSAGetLastError();\r
9603       if (err == WSAEADDRINUSE) continue;\r
9604       (void) closesocket(s);\r
9605       WSACleanup();\r
9606       return err;\r
9607     }\r
9608     if (listen(s2, 1) == SOCKET_ERROR) {\r
9609       err = WSAGetLastError();\r
9610       if (err == WSAEADDRINUSE) {\r
9611         closesocket(s2);\r
9612         s2 = INVALID_SOCKET;\r
9613         continue;\r
9614       }\r
9615       (void) closesocket(s);\r
9616       (void) closesocket(s2);\r
9617       WSACleanup();\r
9618       return err;\r
9619     }\r
9620     break;\r
9621   }\r
9622   prevStderrPort = fromPort; // remember port used\r
9623   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9624 \r
9625   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9626     err = WSAGetLastError();\r
9627     (void) closesocket(s);\r
9628     (void) closesocket(s2);\r
9629     WSACleanup();\r
9630     return err;\r
9631   }\r
9632 \r
9633   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9634     err = WSAGetLastError();\r
9635     (void) closesocket(s);\r
9636     (void) closesocket(s2);\r
9637     WSACleanup();\r
9638     return err;\r
9639   }\r
9640   if (*user == NULLCHAR) user = UserName();\r
9641   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9642     err = WSAGetLastError();\r
9643     (void) closesocket(s);\r
9644     (void) closesocket(s2);\r
9645     WSACleanup();\r
9646     return err;\r
9647   }\r
9648   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9649     err = WSAGetLastError();\r
9650     (void) closesocket(s);\r
9651     (void) closesocket(s2);\r
9652     WSACleanup();\r
9653     return err;\r
9654   }\r
9655 \r
9656   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9657     err = WSAGetLastError();\r
9658     (void) closesocket(s);\r
9659     (void) closesocket(s2);\r
9660     WSACleanup();\r
9661     return err;\r
9662   }\r
9663   (void) closesocket(s2);  /* Stop listening */\r
9664 \r
9665   /* Prepare return value */\r
9666   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9667   cp->kind = CPRcmd;\r
9668   cp->sock = s;\r
9669   cp->sock2 = s3;\r
9670   *pr = (ProcRef *) cp;\r
9671 \r
9672   return NO_ERROR;\r
9673 }\r
9674 \r
9675 \r
9676 InputSourceRef\r
9677 AddInputSource(ProcRef pr, int lineByLine,\r
9678                InputCallback func, VOIDSTAR closure)\r
9679 {\r
9680   InputSource *is, *is2 = NULL;\r
9681   ChildProc *cp = (ChildProc *) pr;\r
9682 \r
9683   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9684   is->lineByLine = lineByLine;\r
9685   is->func = func;\r
9686   is->closure = closure;\r
9687   is->second = NULL;\r
9688   is->next = is->buf;\r
9689   if (pr == NoProc) {\r
9690     is->kind = CPReal;\r
9691     consoleInputSource = is;\r
9692   } else {\r
9693     is->kind = cp->kind;\r
9694     /* \r
9695         [AS] Try to avoid a race condition if the thread is given control too early:\r
9696         we create all threads suspended so that the is->hThread variable can be\r
9697         safely assigned, then let the threads start with ResumeThread.\r
9698     */\r
9699     switch (cp->kind) {\r
9700     case CPReal:\r
9701       is->hFile = cp->hFrom;\r
9702       cp->hFrom = NULL; /* now owned by InputThread */\r
9703       is->hThread =\r
9704         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9705                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9706       break;\r
9707 \r
9708     case CPComm:\r
9709       is->hFile = cp->hFrom;\r
9710       cp->hFrom = NULL; /* now owned by InputThread */\r
9711       is->hThread =\r
9712         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9713                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9714       break;\r
9715 \r
9716     case CPSock:\r
9717       is->sock = cp->sock;\r
9718       is->hThread =\r
9719         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9720                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9721       break;\r
9722 \r
9723     case CPRcmd:\r
9724       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9725       *is2 = *is;\r
9726       is->sock = cp->sock;\r
9727       is->second = is2;\r
9728       is2->sock = cp->sock2;\r
9729       is2->second = is2;\r
9730       is->hThread =\r
9731         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9732                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9733       is2->hThread =\r
9734         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9735                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9736       break;\r
9737     }\r
9738 \r
9739     if( is->hThread != NULL ) {\r
9740         ResumeThread( is->hThread );\r
9741     }\r
9742 \r
9743     if( is2 != NULL && is2->hThread != NULL ) {\r
9744         ResumeThread( is2->hThread );\r
9745     }\r
9746   }\r
9747 \r
9748   return (InputSourceRef) is;\r
9749 }\r
9750 \r
9751 void\r
9752 RemoveInputSource(InputSourceRef isr)\r
9753 {\r
9754   InputSource *is;\r
9755 \r
9756   is = (InputSource *) isr;\r
9757   is->hThread = NULL;  /* tell thread to stop */\r
9758   CloseHandle(is->hThread);\r
9759   if (is->second != NULL) {\r
9760     is->second->hThread = NULL;\r
9761     CloseHandle(is->second->hThread);\r
9762   }\r
9763 }\r
9764 \r
9765 int no_wrap(char *message, int count)\r
9766 {\r
9767     ConsoleOutput(message, count, FALSE);\r
9768     return count;\r
9769 }\r
9770 \r
9771 int\r
9772 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9773 {\r
9774   DWORD dOutCount;\r
9775   int outCount = SOCKET_ERROR;\r
9776   ChildProc *cp = (ChildProc *) pr;\r
9777   static OVERLAPPED ovl;\r
9778   static int line = 0;\r
9779 \r
9780   if (pr == NoProc)\r
9781   {\r
9782     if (appData.noJoin || !appData.useInternalWrap)\r
9783       return no_wrap(message, count);\r
9784     else\r
9785     {\r
9786       int width = get_term_width();\r
9787       int len = wrap(NULL, message, count, width, &line);\r
9788       char *msg = malloc(len);\r
9789       int dbgchk;\r
9790 \r
9791       if (!msg)\r
9792         return no_wrap(message, count);\r
9793       else\r
9794       {\r
9795         dbgchk = wrap(msg, message, count, width, &line);\r
9796         if (dbgchk != len && appData.debugMode)\r
9797             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9798         ConsoleOutput(msg, len, FALSE);\r
9799         free(msg);\r
9800         return len;\r
9801       }\r
9802     }\r
9803   }\r
9804 \r
9805   if (ovl.hEvent == NULL) {\r
9806     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9807   }\r
9808   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9809 \r
9810   switch (cp->kind) {\r
9811   case CPSock:\r
9812   case CPRcmd:\r
9813     outCount = send(cp->sock, message, count, 0);\r
9814     if (outCount == SOCKET_ERROR) {\r
9815       *outError = WSAGetLastError();\r
9816     } else {\r
9817       *outError = NO_ERROR;\r
9818     }\r
9819     break;\r
9820 \r
9821   case CPReal:\r
9822     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9823                   &dOutCount, NULL)) {\r
9824       *outError = NO_ERROR;\r
9825       outCount = (int) dOutCount;\r
9826     } else {\r
9827       *outError = GetLastError();\r
9828     }\r
9829     break;\r
9830 \r
9831   case CPComm:\r
9832     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9833                             &dOutCount, &ovl);\r
9834     if (*outError == NO_ERROR) {\r
9835       outCount = (int) dOutCount;\r
9836     }\r
9837     break;\r
9838   }\r
9839   return outCount;\r
9840 }\r
9841 \r
9842 void\r
9843 DoSleep(int n)\r
9844 {\r
9845     if(n != 0) Sleep(n);\r
9846 }\r
9847 \r
9848 int\r
9849 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9850                        long msdelay)\r
9851 {\r
9852   /* Ignore delay, not implemented for WinBoard */\r
9853   return OutputToProcess(pr, message, count, outError);\r
9854 }\r
9855 \r
9856 \r
9857 void\r
9858 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9859                         char *buf, int count, int error)\r
9860 {\r
9861   DisplayFatalError(_("Not implemented"), 0, 1);\r
9862 }\r
9863 \r
9864 /* see wgamelist.c for Game List functions */\r
9865 /* see wedittags.c for Edit Tags functions */\r
9866 \r
9867 \r
9868 int\r
9869 ICSInitScript()\r
9870 {\r
9871   FILE *f;\r
9872   char buf[MSG_SIZ];\r
9873   char *dummy;\r
9874 \r
9875   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9876     f = fopen(buf, "r");\r
9877     if (f != NULL) {\r
9878       ProcessICSInitScript(f);\r
9879       fclose(f);\r
9880       return TRUE;\r
9881     }\r
9882   }\r
9883   return FALSE;\r
9884 }\r
9885 \r
9886 \r
9887 VOID\r
9888 StartAnalysisClock()\r
9889 {\r
9890   if (analysisTimerEvent) return;\r
9891   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9892                                         (UINT) 2000, NULL);\r
9893 }\r
9894 \r
9895 VOID\r
9896 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9897 {\r
9898   highlightInfo.sq[0].x = fromX;\r
9899   highlightInfo.sq[0].y = fromY;\r
9900   highlightInfo.sq[1].x = toX;\r
9901   highlightInfo.sq[1].y = toY;\r
9902 }\r
9903 \r
9904 VOID\r
9905 ClearHighlights()\r
9906 {\r
9907   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9908     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9909 }\r
9910 \r
9911 VOID\r
9912 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9913 {\r
9914   premoveHighlightInfo.sq[0].x = fromX;\r
9915   premoveHighlightInfo.sq[0].y = fromY;\r
9916   premoveHighlightInfo.sq[1].x = toX;\r
9917   premoveHighlightInfo.sq[1].y = toY;\r
9918 }\r
9919 \r
9920 VOID\r
9921 ClearPremoveHighlights()\r
9922 {\r
9923   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9924     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9925 }\r
9926 \r
9927 VOID\r
9928 ShutDownFrontEnd()\r
9929 {\r
9930   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9931   DeleteClipboardTempFiles();\r
9932 }\r
9933 \r
9934 void\r
9935 BoardToTop()\r
9936 {\r
9937     if (IsIconic(hwndMain))\r
9938       ShowWindow(hwndMain, SW_RESTORE);\r
9939 \r
9940     SetActiveWindow(hwndMain);\r
9941 }\r
9942 \r
9943 /*\r
9944  * Prototypes for animation support routines\r
9945  */\r
9946 static void ScreenSquare(int column, int row, POINT * pt);\r
9947 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9948      POINT frames[], int * nFrames);\r
9949 \r
9950 \r
9951 #define kFactor 4\r
9952 \r
9953 void\r
9954 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9955 {       // [HGM] atomic: animate blast wave\r
9956         int i;\r
9957 \r
9958         explodeInfo.fromX = fromX;\r
9959         explodeInfo.fromY = fromY;\r
9960         explodeInfo.toX = toX;\r
9961         explodeInfo.toY = toY;\r
9962         for(i=1; i<4*kFactor; i++) {\r
9963             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9964             DrawPosition(FALSE, board);\r
9965             Sleep(appData.animSpeed);\r
9966         }\r
9967         explodeInfo.radius = 0;\r
9968         DrawPosition(TRUE, board);\r
9969 }\r
9970 \r
9971 void\r
9972 AnimateMove(board, fromX, fromY, toX, toY)\r
9973      Board board;\r
9974      int fromX;\r
9975      int fromY;\r
9976      int toX;\r
9977      int toY;\r
9978 {\r
9979   ChessSquare piece;\r
9980   int x = toX, y = toY;\r
9981   POINT start, finish, mid;\r
9982   POINT frames[kFactor * 2 + 1];\r
9983   int nFrames, n;\r
9984 \r
9985   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9986 \r
9987   if (!appData.animate) return;\r
9988   if (doingSizing) return;\r
9989   if (fromY < 0 || fromX < 0) return;\r
9990   piece = board[fromY][fromX];\r
9991   if (piece >= EmptySquare) return;\r
9992 \r
9993   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
9994 \r
9995 again:\r
9996 \r
9997   ScreenSquare(fromX, fromY, &start);\r
9998   ScreenSquare(toX, toY, &finish);\r
9999 \r
10000   /* All moves except knight jumps move in straight line */\r
10001   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
10002     mid.x = start.x + (finish.x - start.x) / 2;\r
10003     mid.y = start.y + (finish.y - start.y) / 2;\r
10004   } else {\r
10005     /* Knight: make straight movement then diagonal */\r
10006     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10007        mid.x = start.x + (finish.x - start.x) / 2;\r
10008        mid.y = start.y;\r
10009      } else {\r
10010        mid.x = start.x;\r
10011        mid.y = start.y + (finish.y - start.y) / 2;\r
10012      }\r
10013   }\r
10014   \r
10015   /* Don't use as many frames for very short moves */\r
10016   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10017     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10018   else\r
10019     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10020 \r
10021   animInfo.from.x = fromX;\r
10022   animInfo.from.y = fromY;\r
10023   animInfo.to.x = toX;\r
10024   animInfo.to.y = toY;\r
10025   animInfo.lastpos = start;\r
10026   animInfo.piece = piece;\r
10027   for (n = 0; n < nFrames; n++) {\r
10028     animInfo.pos = frames[n];\r
10029     DrawPosition(FALSE, NULL);\r
10030     animInfo.lastpos = animInfo.pos;\r
10031     Sleep(appData.animSpeed);\r
10032   }\r
10033   animInfo.pos = finish;\r
10034   DrawPosition(FALSE, NULL);\r
10035 \r
10036   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10037 \r
10038   animInfo.piece = EmptySquare;\r
10039   Explode(board, fromX, fromY, toX, toY);\r
10040 }\r
10041 \r
10042 /*      Convert board position to corner of screen rect and color       */\r
10043 \r
10044 static void\r
10045 ScreenSquare(column, row, pt)\r
10046      int column; int row; POINT * pt;\r
10047 {\r
10048   if (flipView) {\r
10049     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10050     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10051   } else {\r
10052     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10053     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10054   }\r
10055 }\r
10056 \r
10057 /*      Generate a series of frame coords from start->mid->finish.\r
10058         The movement rate doubles until the half way point is\r
10059         reached, then halves back down to the final destination,\r
10060         which gives a nice slow in/out effect. The algorithmn\r
10061         may seem to generate too many intermediates for short\r
10062         moves, but remember that the purpose is to attract the\r
10063         viewers attention to the piece about to be moved and\r
10064         then to where it ends up. Too few frames would be less\r
10065         noticeable.                                             */\r
10066 \r
10067 static void\r
10068 Tween(start, mid, finish, factor, frames, nFrames)\r
10069      POINT * start; POINT * mid;\r
10070      POINT * finish; int factor;\r
10071      POINT frames[]; int * nFrames;\r
10072 {\r
10073   int n, fraction = 1, count = 0;\r
10074 \r
10075   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10076   for (n = 0; n < factor; n++)\r
10077     fraction *= 2;\r
10078   for (n = 0; n < factor; n++) {\r
10079     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10080     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10081     count ++;\r
10082     fraction = fraction / 2;\r
10083   }\r
10084   \r
10085   /* Midpoint */\r
10086   frames[count] = *mid;\r
10087   count ++;\r
10088   \r
10089   /* Slow out, stepping 1/2, then 1/4, ... */\r
10090   fraction = 2;\r
10091   for (n = 0; n < factor; n++) {\r
10092     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10093     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10094     count ++;\r
10095     fraction = fraction * 2;\r
10096   }\r
10097   *nFrames = count;\r
10098 }\r
10099 \r
10100 void\r
10101 SettingsPopUp(ChessProgramState *cps)\r
10102 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10103       EngineOptionsPopup(savedHwnd, cps);\r
10104 }\r
10105 \r
10106 int flock(int fid, int code)\r
10107 {\r
10108     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10109     OVERLAPPED ov;\r
10110     ov.hEvent = NULL;\r
10111     ov.Offset = 0;\r
10112     ov.OffsetHigh = 0;\r
10113     switch(code) {\r
10114       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10115 \r
10116       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10117       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10118       default: return -1;\r
10119     }\r
10120     return 0;\r
10121 }\r
10122 \r
10123 char *\r
10124 Col2Text (int n)\r
10125 {\r
10126     static int i=0;\r
10127     static char col[8][20];\r
10128     COLORREF color = *(COLORREF *) colorVariable[n];\r
10129     i = i+1 & 7;\r
10130     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10131     return col[i];\r
10132 }\r
10133 \r
10134 void\r
10135 ActivateTheme (int new)\r
10136 {   // Redo initialization of features depending on options that can occur in themes\r
10137    InitTextures();\r
10138    if(new) InitDrawingColors();\r
10139    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10140    InitDrawingSizes(boardSize, 0);\r
10141    InvalidateRect(hwndMain, NULL, TRUE);\r
10142 }\r