Merge branch 'v4.7.x' into master
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts.\r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 #include <io.h>\r
77 \r
78 #if __GNUC__\r
79 #include <errno.h>\r
80 #include <string.h>\r
81 #endif\r
82 \r
83 #include "common.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "winboard.h"\r
87 #include "moves.h"\r
88 #include "wclipbrd.h"\r
89 #include "woptions.h"\r
90 #include "wsockerr.h"\r
91 #include "defaults.h"\r
92 #include "help.h"\r
93 #include "wsnap.h"\r
94 \r
95 #define SLASH '/'\r
96 #define DATADIR "~~"\r
97 \r
98 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
99 \r
100   int myrandom(void);\r
101   void mysrandom(unsigned int seed);\r
102 \r
103 extern int whiteFlag, blackFlag;\r
104 Boolean flipClock = FALSE;\r
105 extern HANDLE chatHandle[];\r
106 extern enum ICS_TYPE ics_type;\r
107 \r
108 int  MySearchPath P((char *installDir, char *name, char *fullname));\r
109 int  MyGetFullPathName P((char *name, char *fullname));\r
110 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
111 VOID NewVariantPopup(HWND hwnd);\r
112 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
113                    /*char*/int promoChar));\r
114 void DisplayMove P((int moveNumber));\r
115 void ChatPopUp P((char *s));\r
116 typedef struct {\r
117   ChessSquare piece;  \r
118   POINT pos;      /* window coordinates of current pos */\r
119   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
120   POINT from;     /* board coordinates of the piece's orig pos */\r
121   POINT to;       /* board coordinates of the piece's new pos */\r
122 } AnimInfo;\r
123 \r
124 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
125 \r
126 typedef struct {\r
127   POINT start;    /* window coordinates of start pos */\r
128   POINT pos;      /* window coordinates of current pos */\r
129   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
130   POINT from;     /* board coordinates of the piece's orig pos */\r
131   ChessSquare piece;\r
132 } DragInfo;\r
133 \r
134 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };\r
135 \r
136 typedef struct {\r
137   POINT sq[2];    /* board coordinates of from, to squares */\r
138 } HighlightInfo;\r
139 \r
140 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
141 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
142 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
143 static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
144 \r
145 typedef struct { // [HGM] atomic\r
146   int fromX, fromY, toX, toY, radius;\r
147 } ExplodeInfo;\r
148 \r
149 static ExplodeInfo explodeInfo;\r
150 \r
151 /* Window class names */\r
152 char szAppName[] = "WinBoard";\r
153 char szConsoleName[] = "WBConsole";\r
154 \r
155 /* Title bar text */\r
156 char szTitle[] = "WinBoard";\r
157 char szConsoleTitle[] = "I C S Interaction";\r
158 \r
159 char *programName;\r
160 char *settingsFileName;\r
161 Boolean saveSettingsOnExit;\r
162 char installDir[MSG_SIZ];\r
163 int errorExitStatus;\r
164 \r
165 BoardSize boardSize;\r
166 Boolean chessProgram;\r
167 //static int boardX, boardY;\r
168 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
169 int squareSize, lineGap, minorSize, border;\r
170 static int winW, winH;\r
171 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
172 static int logoHeight = 0;\r
173 static char messageText[MESSAGE_TEXT_MAX];\r
174 static int clockTimerEvent = 0;\r
175 static int loadGameTimerEvent = 0;\r
176 static int analysisTimerEvent = 0;\r
177 static DelayedEventCallback delayedTimerCallback;\r
178 static int delayedTimerEvent = 0;\r
179 static int buttonCount = 2;\r
180 char *icsTextMenuString;\r
181 char *icsNames;\r
182 char *firstChessProgramNames;\r
183 char *secondChessProgramNames;\r
184 \r
185 #define PALETTESIZE 256\r
186 \r
187 HINSTANCE hInst;          /* current instance */\r
188 Boolean alwaysOnTop = FALSE;\r
189 RECT boardRect;\r
190 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
191   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
192 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };\r
193 HPALETTE hPal;\r
194 ColorClass currentColorClass;\r
195 \r
196 static HWND savedHwnd;\r
197 HWND hCommPort = NULL;    /* currently open comm port */\r
198 static HWND hwndPause;    /* pause button */\r
199 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
200 static HBRUSH lightSquareBrush, darkSquareBrush,\r
201   blackSquareBrush, /* [HGM] for band between board and holdings */\r
202   explodeBrush,     /* [HGM] atomic */\r
203   markerBrush[8],   /* [HGM] markers */\r
204   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
205 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
206 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
207 static HPEN gridPen = NULL;\r
208 static HPEN highlightPen = NULL;\r
209 static HPEN premovePen = NULL;\r
210 static NPLOGPALETTE pLogPal;\r
211 static BOOL paletteChanged = FALSE;\r
212 static HICON iconWhite, iconBlack, iconCurrent;\r
213 static int doingSizing = FALSE;\r
214 static int lastSizing = 0;\r
215 static int prevStderrPort;\r
216 static HBITMAP userLogo;\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 \r
229 #if defined(_winmajor)\r
230 #define oldDialog (_winmajor < 4)\r
231 #else\r
232 #define oldDialog 0\r
233 #endif\r
234 #endif\r
235 \r
236 #define INTERNATIONAL\r
237 \r
238 #ifdef INTERNATIONAL\r
239 #  define _(s) T_(s)\r
240 #  define N_(s) s\r
241 #else\r
242 #  define _(s) s\r
243 #  define N_(s) s\r
244 #  define T_(s) s\r
245 #  define Translate(x, y)\r
246 #  define LoadLanguageFile(s)\r
247 #endif\r
248 \r
249 #ifdef INTERNATIONAL\r
250 \r
251 Boolean barbaric; // flag indicating if translation is needed\r
252 \r
253 // list of item numbers used in each dialog (used to alter language at run time)\r
254 \r
255 #define ABOUTBOX -1  /* not sure why these are needed */\r
256 #define ABOUTBOX2 -1\r
257 \r
258 int dialogItems[][42] = {\r
259 { ABOUTBOX, IDOK, OPT_MESS, 400 }, \r
260 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
261   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
262 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,\r
263   OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, \r
264 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
265   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
266 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
267 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
268   IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
269 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
270 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
271   PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
272 { ABOUTBOX2, IDC_ChessBoard }, \r
273 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
274   OPT_GameListClose, IDC_GameListDoFilter }, \r
275 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
276 { DLG_Error, IDOK }, \r
277 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
278   OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
279 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
280 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
281   IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
282   IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
283 { DLG_IndexNumber, IDC_Index }, \r
284 { DLG_TypeInMove, IDOK, IDCANCEL }, \r
285 { DLG_TypeInName, IDOK, IDCANCEL }, \r
286 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
287   OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
288 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
289   OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
290   OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
291   OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
292   OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
293   OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
294   OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove }, \r
295 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
296   OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
297   OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
298   OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
299   OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
300   OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
301   OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
302   OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
303   GPB_General, GPB_Alarm, OPT_AutoCreate }, \r
304 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
305   OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
306   OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
307   OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
308   OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
309   OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
310   OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
311   IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid }, \r
312 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
313   OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
314   OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
315   OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
316   OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
317   OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
318   OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,\r
319   OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
320   IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
321 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
322   OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
323   OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,\r
324   OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7, \r
325   OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
326 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
327 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
328   IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
329 { DLG_MoveHistory }, \r
330 { DLG_EvalGraph }, \r
331 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
332 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
333 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
334   IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
335   IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
336   GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
337 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
338   IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
339   IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
340 { 0 }\r
341 };\r
342 \r
343 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
344 static int lastChecked;\r
345 static char oldLanguage[MSG_SIZ], *menuText[10][30];\r
346 extern int tinyLayout;\r
347 extern char * menuBarText[][10];\r
348 \r
349 void\r
350 LoadLanguageFile(char *name)\r
351 {   //load the file with translations, and make a list of the strings to be translated, and their translations\r
352     FILE *f;\r
353     int i=0, j=0, n=0, k;\r
354     char buf[MSG_SIZ];\r
355 \r
356     if(!name || name[0] == NULLCHAR) return;\r
357       snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
358     appData.language = oldLanguage;\r
359     if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
360     if((f = fopen(buf, "r")) == NULL) return;\r
361     while((k = fgetc(f)) != EOF) {\r
362         if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
363         languageBuf[i] = k;\r
364         if(k == '\n') {\r
365             if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
366                 char *p;\r
367                 if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
368                     if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
369                         if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
370                         english[j] = languageBuf + n + 1; *p = 0;\r
371                         foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
372 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
373                     }\r
374                 }\r
375             }\r
376             n = i + 1;\r
377         } else if(i > 0 && languageBuf[i-1] == '\\') {\r
378             switch(k) {\r
379               case 'n': k = '\n'; break;\r
380               case 'r': k = '\r'; break;\r
381               case 't': k = '\t'; break;\r
382             }\r
383             languageBuf[--i] = k;\r
384         }\r
385         i++;\r
386     }\r
387     fclose(f);\r
388     barbaric = (j != 0);\r
389     safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
390 }\r
391 \r
392 char *\r
393 T_(char *s)\r
394 {   // return the translation of the given string\r
395     // efficiency can be improved a lot...\r
396     int i=0;\r
397     static char buf[MSG_SIZ];\r
398 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
399     if(!barbaric) return s;\r
400     if(!s) return ""; // sanity\r
401     while(english[i]) {\r
402         if(!strcmp(s, english[i])) return foreign[i];\r
403         if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending\r
404             snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion\r
405             return buf;\r
406         }\r
407         i++;\r
408     }\r
409     return s;\r
410 }\r
411 \r
412 void\r
413 Translate(HWND hDlg, int dialogID)\r
414 {   // translate all text items in the given dialog\r
415     int i=0, j, k;\r
416     char buf[MSG_SIZ], *s;\r
417     if(!barbaric) return;\r
418     while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
419     if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
420     GetWindowText( hDlg, buf, MSG_SIZ );\r
421     s = T_(buf);\r
422     if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
423     for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
424         GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
425         if(strlen(buf) == 0) continue;\r
426         s = T_(buf);\r
427         if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
428     }\r
429 }\r
430 \r
431 HMENU\r
432 TranslateOneMenu(int i, HMENU subMenu)\r
433 {\r
434     int j;\r
435     static MENUITEMINFO info;\r
436 \r
437     info.cbSize = sizeof(MENUITEMINFO);\r
438     info.fMask = MIIM_STATE | MIIM_TYPE;\r
439           for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
440             char buf[MSG_SIZ];\r
441             info.dwTypeData = buf;\r
442             info.cch = sizeof(buf);\r
443             GetMenuItemInfo(subMenu, j, TRUE, &info);\r
444             if(i < 10) {\r
445                 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );\r
446                 else menuText[i][j] = strdup(buf); // remember original on first change\r
447             }\r
448             if(buf[0] == NULLCHAR) continue;\r
449             info.dwTypeData = T_(buf);\r
450             info.cch = strlen(buf)+1;\r
451             SetMenuItemInfo(subMenu, j, TRUE, &info);\r
452           }\r
453     return subMenu;\r
454 }\r
455 \r
456 void\r
457 TranslateMenus(int addLanguage)\r
458 {\r
459     int i;\r
460     WIN32_FIND_DATA fileData;\r
461     HANDLE hFind;\r
462 #define IDM_English 1970\r
463     if(1) {\r
464         HMENU mainMenu = GetMenu(hwndMain);\r
465         for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
466           HMENU subMenu = GetSubMenu(mainMenu, i);\r
467           ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
468                                                                   (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
469           TranslateOneMenu(i, subMenu);\r
470         }\r
471         DrawMenuBar(hwndMain);\r
472     }\r
473 \r
474     if(!addLanguage) return;\r
475     if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
476         HMENU mainMenu = GetMenu(hwndMain);\r
477         HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
478         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
479         AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
480         i = 0; lastChecked = IDM_English;\r
481         do {\r
482             char *p, *q = fileData.cFileName;\r
483             int checkFlag = MF_UNCHECKED;\r
484             languageFile[i] = strdup(q);\r
485             if(barbaric && !strcmp(oldLanguage, q)) {\r
486                 checkFlag = MF_CHECKED;\r
487                 lastChecked = IDM_English + i + 1;\r
488                 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
489             }\r
490             *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
491             p = strstr(fileData.cFileName, ".lng");\r
492             if(p) *p = 0;\r
493             AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
494         } while(FindNextFile(hFind, &fileData));\r
495         FindClose(hFind);\r
496     }\r
497 }\r
498 \r
499 #endif\r
500 \r
501 #define IDM_RecentEngines 3000\r
502 \r
503 void\r
504 RecentEngineMenu (char *s)\r
505 {\r
506     if(appData.icsActive) return;\r
507     if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty\r
508         HMENU mainMenu = GetMenu(hwndMain);\r
509         HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu\r
510         int i=IDM_RecentEngines;\r
511         recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu\r
512         AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
513         while(*s) {\r
514           char *p = strchr(s, '\n');\r
515           if(p == NULL) return; // malformed!\r
516           *p = NULLCHAR;\r
517           AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);\r
518           *p = '\n';\r
519           s = p+1;\r
520         }\r
521     }\r
522 }\r
523 \r
524 \r
525 typedef struct {\r
526   char *name;\r
527   int squareSize;\r
528   int lineGap;\r
529   int smallLayout;\r
530   int tinyLayout;\r
531   int cliWidth, cliHeight;\r
532 } SizeInfo;\r
533 \r
534 SizeInfo sizeInfo[] = \r
535 {\r
536   { "tiny",     21, 0, 1, 1, 0, 0 },\r
537   { "teeny",    25, 1, 1, 1, 0, 0 },\r
538   { "dinky",    29, 1, 1, 1, 0, 0 },\r
539   { "petite",   33, 1, 1, 1, 0, 0 },\r
540   { "slim",     37, 2, 1, 0, 0, 0 },\r
541   { "small",    40, 2, 1, 0, 0, 0 },\r
542   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
543   { "middling", 49, 2, 0, 0, 0, 0 },\r
544   { "average",  54, 2, 0, 0, 0, 0 },\r
545   { "moderate", 58, 3, 0, 0, 0, 0 },\r
546   { "medium",   64, 3, 0, 0, 0, 0 },\r
547   { "bulky",    72, 3, 0, 0, 0, 0 },\r
548   { "large",    80, 3, 0, 0, 0, 0 },\r
549   { "big",      87, 3, 0, 0, 0, 0 },\r
550   { "huge",     95, 3, 0, 0, 0, 0 },\r
551   { "giant",    108, 3, 0, 0, 0, 0 },\r
552   { "colossal", 116, 4, 0, 0, 0, 0 },\r
553   { "titanic",  129, 4, 0, 0, 0, 0 },\r
554   { NULL, 0, 0, 0, 0, 0, 0 }\r
555 };\r
556 \r
557 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
558 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
559 {\r
560   { 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
561   { 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
562   { 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
563   { 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
564   { 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
565   { 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
566   { 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
567   { 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
568   { 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
569   { 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
570   { 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
571   { 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
572   { 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
573   { 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
574   { 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
575   { 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
576   { 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
577   { 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
578 };\r
579 \r
580 MyFont *font[NUM_SIZES][NUM_FONTS];\r
581 \r
582 typedef struct {\r
583   char *label;\r
584   int id;\r
585   HWND hwnd;\r
586   WNDPROC wndproc;\r
587 } MyButtonDesc;\r
588 \r
589 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
590 #define N_BUTTONS 5\r
591 \r
592 MyButtonDesc buttonDesc[N_BUTTONS] =\r
593 {\r
594   {"<<", IDM_ToStart, NULL, NULL},\r
595   {"<", IDM_Backward, NULL, NULL},\r
596   {"P", IDM_Pause, NULL, NULL},\r
597   {">", IDM_Forward, NULL, NULL},\r
598   {">>", IDM_ToEnd, NULL, NULL},\r
599 };\r
600 \r
601 int tinyLayout = 0, smallLayout = 0;\r
602 #define MENU_BAR_ITEMS 9\r
603 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
604   { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
605   { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
606 };\r
607 \r
608 \r
609 MySound sounds[(int)NSoundClasses];\r
610 MyTextAttribs textAttribs[(int)NColorClasses];\r
611 \r
612 MyColorizeAttribs colorizeAttribs[] = {\r
613   { (COLORREF)0, 0, N_("Shout Text") },\r
614   { (COLORREF)0, 0, N_("SShout/CShout") },\r
615   { (COLORREF)0, 0, N_("Channel 1 Text") },\r
616   { (COLORREF)0, 0, N_("Channel Text") },\r
617   { (COLORREF)0, 0, N_("Kibitz Text") },\r
618   { (COLORREF)0, 0, N_("Tell Text") },\r
619   { (COLORREF)0, 0, N_("Challenge Text") },\r
620   { (COLORREF)0, 0, N_("Request Text") },\r
621   { (COLORREF)0, 0, N_("Seek Text") },\r
622   { (COLORREF)0, 0, N_("Normal Text") },\r
623   { (COLORREF)0, 0, N_("None") }\r
624 };\r
625 \r
626 \r
627 \r
628 static char *commentTitle;\r
629 static char *commentText;\r
630 static int commentIndex;\r
631 static Boolean editComment = FALSE;\r
632 \r
633 \r
634 char errorTitle[MSG_SIZ];\r
635 char errorMessage[2*MSG_SIZ];\r
636 HWND errorDialog = NULL;\r
637 BOOLEAN moveErrorMessageUp = FALSE;\r
638 BOOLEAN consoleEcho = TRUE;\r
639 CHARFORMAT consoleCF;\r
640 COLORREF consoleBackgroundColor;\r
641 \r
642 char *programVersion;\r
643 \r
644 #define CPReal 1\r
645 #define CPComm 2\r
646 #define CPSock 3\r
647 #define CPRcmd 4\r
648 typedef int CPKind;\r
649 \r
650 typedef struct {\r
651   CPKind kind;\r
652   HANDLE hProcess;\r
653   DWORD pid;\r
654   HANDLE hTo;\r
655   HANDLE hFrom;\r
656   SOCKET sock;\r
657   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
658 } ChildProc;\r
659 \r
660 #define INPUT_SOURCE_BUF_SIZE 4096\r
661 \r
662 typedef struct _InputSource {\r
663   CPKind kind;\r
664   HANDLE hFile;\r
665   SOCKET sock;\r
666   int lineByLine;\r
667   HANDLE hThread;\r
668   DWORD id;\r
669   char buf[INPUT_SOURCE_BUF_SIZE];\r
670   char *next;\r
671   DWORD count;\r
672   int error;\r
673   InputCallback func;\r
674   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
675   VOIDSTAR closure;\r
676 } InputSource;\r
677 \r
678 InputSource *consoleInputSource;\r
679 \r
680 DCB dcb;\r
681 \r
682 /* forward */\r
683 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
684 VOID ConsoleCreate();\r
685 LRESULT CALLBACK\r
686   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
687 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
688 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
689 VOID ParseCommSettings(char *arg, DCB *dcb);\r
690 LRESULT CALLBACK\r
691   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
692 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
693 void ParseIcsTextMenu(char *icsTextMenuString);\r
694 VOID PopUpNameDialog(char firstchar);\r
695 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
696 \r
697 /* [AS] */\r
698 int NewGameFRC();\r
699 int GameListOptions();\r
700 \r
701 int dummy; // [HGM] for obsolete args\r
702 \r
703 HWND hwndMain = NULL;        /* root window*/\r
704 HWND hwndConsole = NULL;\r
705 HWND commentDialog = NULL;\r
706 HWND moveHistoryDialog = NULL;\r
707 HWND evalGraphDialog = NULL;\r
708 HWND engineOutputDialog = NULL;\r
709 HWND gameListDialog = NULL;\r
710 HWND editTagsDialog = NULL;\r
711 \r
712 int commentUp = FALSE;\r
713 \r
714 WindowPlacement wpMain;\r
715 WindowPlacement wpConsole;\r
716 WindowPlacement wpComment;\r
717 WindowPlacement wpMoveHistory;\r
718 WindowPlacement wpEvalGraph;\r
719 WindowPlacement wpEngineOutput;\r
720 WindowPlacement wpGameList;\r
721 WindowPlacement wpTags;\r
722 \r
723 VOID EngineOptionsPopup(); // [HGM] settings\r
724 \r
725 VOID GothicPopUp(char *title, VariantClass variant);\r
726 /*\r
727  * Setting "frozen" should disable all user input other than deleting\r
728  * the window.  We do this while engines are initializing themselves.\r
729  */\r
730 static int frozen = 0;\r
731 static int oldMenuItemState[MENU_BAR_ITEMS];\r
732 void FreezeUI()\r
733 {\r
734   HMENU hmenu;\r
735   int i;\r
736 \r
737   if (frozen) return;\r
738   frozen = 1;\r
739   hmenu = GetMenu(hwndMain);\r
740   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
741     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
742   }\r
743   DrawMenuBar(hwndMain);\r
744 }\r
745 \r
746 /* Undo a FreezeUI */\r
747 void ThawUI()\r
748 {\r
749   HMENU hmenu;\r
750   int i;\r
751 \r
752   if (!frozen) return;\r
753   frozen = 0;\r
754   hmenu = GetMenu(hwndMain);\r
755   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
756     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
757   }\r
758   DrawMenuBar(hwndMain);\r
759 }\r
760 \r
761 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
762 \r
763 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
764 #ifdef JAWS\r
765 #include "jaws.c"\r
766 #else\r
767 #define JAWS_INIT\r
768 #define JAWS_ARGS\r
769 #define JAWS_ALT_INTERCEPT\r
770 #define JAWS_KBUP_NAVIGATION\r
771 #define JAWS_KBDOWN_NAVIGATION\r
772 #define JAWS_MENU_ITEMS\r
773 #define JAWS_SILENCE\r
774 #define JAWS_REPLAY\r
775 #define JAWS_ACCEL\r
776 #define JAWS_COPYRIGHT\r
777 #define JAWS_DELETE(X) X\r
778 #define SAYMACHINEMOVE()\r
779 #define SAY(X)\r
780 #endif\r
781 \r
782 /*---------------------------------------------------------------------------*\\r
783  *\r
784  * WinMain\r
785  *\r
786 \*---------------------------------------------------------------------------*/\r
787 \r
788 int APIENTRY\r
789 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
790         LPSTR lpCmdLine, int nCmdShow)\r
791 {\r
792   MSG msg;\r
793   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
794 //  INITCOMMONCONTROLSEX ex;\r
795 \r
796   debugFP = stderr;\r
797 \r
798   LoadLibrary("RICHED32.DLL");\r
799   consoleCF.cbSize = sizeof(CHARFORMAT);\r
800 \r
801   if (!InitApplication(hInstance)) {\r
802     return (FALSE);\r
803   }\r
804   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
805     return (FALSE);\r
806   }\r
807 \r
808   JAWS_INIT\r
809   TranslateMenus(1);\r
810 \r
811 //  InitCommonControlsEx(&ex);\r
812   InitCommonControls();\r
813 \r
814   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
815   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
816   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
817 \r
818   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
819 \r
820   while (GetMessage(&msg, /* message structure */\r
821                     NULL, /* handle of window receiving the message */\r
822                     0,    /* lowest message to examine */\r
823                     0))   /* highest message to examine */\r
824     {\r
825 \r
826       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
827         // [HGM] navigate: switch between all windows with tab\r
828         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
829         int i, currentElement = 0;\r
830 \r
831         // first determine what element of the chain we come from (if any)\r
832         if(appData.icsActive) {\r
833             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
834             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
835         }\r
836         if(engineOutputDialog && EngineOutputIsUp()) {\r
837             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
838             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
839         }\r
840         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
841             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
842         }\r
843         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
844         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
845         if(msg.hwnd == e1)                 currentElement = 2; else\r
846         if(msg.hwnd == e2)                 currentElement = 3; else\r
847         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
848         if(msg.hwnd == mh)                currentElement = 4; else\r
849         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
850         if(msg.hwnd == hText)  currentElement = 5; else\r
851         if(msg.hwnd == hInput) currentElement = 6; else\r
852         for (i = 0; i < N_BUTTONS; i++) {\r
853             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
854         }\r
855 \r
856         // determine where to go to\r
857         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
858           do {\r
859             currentElement = (currentElement + direction) % 7;\r
860             switch(currentElement) {\r
861                 case 0:\r
862                   h = hwndMain; break; // passing this case always makes the loop exit\r
863                 case 1:\r
864                   h = buttonDesc[0].hwnd; break; // could be NULL\r
865                 case 2:\r
866                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
867                   h = e1; break;\r
868                 case 3:\r
869                   if(!EngineOutputIsUp()) continue;\r
870                   h = e2; break;\r
871                 case 4:\r
872                   if(!MoveHistoryIsUp()) continue;\r
873                   h = mh; break;\r
874 //              case 6: // input to eval graph does not seem to get here!\r
875 //                if(!EvalGraphIsUp()) continue;\r
876 //                h = evalGraphDialog; break;\r
877                 case 5:\r
878                   if(!appData.icsActive) continue;\r
879                   SAY("display");\r
880                   h = hText; break;\r
881                 case 6:\r
882                   if(!appData.icsActive) continue;\r
883                   SAY("input");\r
884                   h = hInput; break;\r
885             }\r
886           } while(h == 0);\r
887 \r
888           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
889           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
890           SetFocus(h);\r
891 \r
892           continue; // this message now has been processed\r
893         }\r
894       }\r
895 \r
896       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
897           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
898           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
899           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
900           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
901           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
902           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
903           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
904           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
905           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
906         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
907         for(i=0; i<MAX_CHAT; i++) \r
908             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
909                 done = 1; break;\r
910         }\r
911         if(done) continue; // [HGM] chat: end patch\r
912         TranslateMessage(&msg); /* Translates virtual key codes */\r
913         DispatchMessage(&msg);  /* Dispatches message to window */\r
914       }\r
915     }\r
916 \r
917 \r
918   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
919 }\r
920 \r
921 /*---------------------------------------------------------------------------*\\r
922  *\r
923  * Initialization functions\r
924  *\r
925 \*---------------------------------------------------------------------------*/\r
926 \r
927 void\r
928 SetUserLogo()\r
929 {   // update user logo if necessary\r
930     static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;\r
931 \r
932     if(appData.autoLogo) {\r
933           curName = UserName();\r
934           if(strcmp(curName, oldUserName)) {\r
935                 GetCurrentDirectory(MSG_SIZ, dir);\r
936                 SetCurrentDirectory(installDir);\r
937                 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
938                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
939                 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
940                 if(userLogo == NULL)\r
941                     userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
942                 SetCurrentDirectory(dir); /* return to prev directory */\r
943           }\r
944     }\r
945 }\r
946 \r
947 BOOL\r
948 InitApplication(HINSTANCE hInstance)\r
949 {\r
950   WNDCLASS wc;\r
951 \r
952   /* Fill in window class structure with parameters that describe the */\r
953   /* main window. */\r
954 \r
955   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
956   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
957   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
958   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
959   wc.hInstance     = hInstance;         /* Owner of this class */\r
960   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
961   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
962   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
963   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
964   wc.lpszClassName = szAppName;                 /* Name to register as */\r
965 \r
966   /* Register the window class and return success/failure code. */\r
967   if (!RegisterClass(&wc)) return FALSE;\r
968 \r
969   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
970   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
971   wc.cbClsExtra    = 0;\r
972   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
973   wc.hInstance     = hInstance;\r
974   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
975   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
976   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
977   wc.lpszMenuName  = NULL;\r
978   wc.lpszClassName = szConsoleName;\r
979 \r
980   if (!RegisterClass(&wc)) return FALSE;\r
981   return TRUE;\r
982 }\r
983 \r
984 \r
985 /* Set by InitInstance, used by EnsureOnScreen */\r
986 int screenHeight, screenWidth;\r
987 RECT screenGeometry;\r
988 \r
989 void\r
990 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
991 {\r
992 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
993   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
994   if (*x > screenGeometry.right - 32) *x = screenGeometry.left;\r
995   if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;\r
996   if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;\r
997   if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;\r
998 }\r
999 \r
1000 VOID\r
1001 LoadLogo(ChessProgramState *cps, int n, Boolean ics)\r
1002 {\r
1003   char buf[MSG_SIZ], dir[MSG_SIZ];\r
1004   GetCurrentDirectory(MSG_SIZ, dir);\r
1005   SetCurrentDirectory(installDir);\r
1006   if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {\r
1007       cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1008 \r
1009       if (cps->programLogo == NULL && appData.debugMode) {\r
1010           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );\r
1011       }\r
1012   } else if(appData.autoLogo) {\r
1013       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS\r
1014         char *opponent = "";\r
1015         if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;\r
1016         if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;\r
1017         sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);\r
1018         if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {\r
1019             sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
1020             cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1021         }\r
1022       } else\r
1023       if(appData.directory[n] && appData.directory[n][0]) {\r
1024         SetCurrentDirectory(appData.directory[n]);\r
1025         cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );     \r
1026       }\r
1027   }\r
1028   SetCurrentDirectory(dir); /* return to prev directory */\r
1029 }\r
1030 \r
1031 VOID\r
1032 InitTextures()\r
1033 {\r
1034   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
1035   backTextureSquareSize = 0; // kludge to force recalculation of texturemode\r
1036   \r
1037   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
1038       if(liteBackTexture) DeleteObject(liteBackTexture);\r
1039       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1040       liteBackTextureMode = appData.liteBackTextureMode;\r
1041 \r
1042       if (liteBackTexture == NULL && appData.debugMode) {\r
1043           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
1044       }\r
1045   }\r
1046   \r
1047   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
1048       if(darkBackTexture) DeleteObject(darkBackTexture);\r
1049       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
1050       darkBackTextureMode = appData.darkBackTextureMode;\r
1051 \r
1052       if (darkBackTexture == NULL && appData.debugMode) {\r
1053           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
1054       }\r
1055   }\r
1056 }\r
1057 \r
1058 #ifndef SM_CXVIRTUALSCREEN\r
1059 #define SM_CXVIRTUALSCREEN 78\r
1060 #endif\r
1061 #ifndef SM_CYVIRTUALSCREEN\r
1062 #define SM_CYVIRTUALSCREEN 79\r
1063 #endif\r
1064 #ifndef SM_XVIRTUALSCREEN \r
1065 #define SM_XVIRTUALSCREEN 76\r
1066 #endif\r
1067 #ifndef SM_YVIRTUALSCREEN \r
1068 #define SM_YVIRTUALSCREEN 77\r
1069 #endif\r
1070 \r
1071 VOID\r
1072 InitGeometry()\r
1073 {\r
1074   screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);\r
1075   if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
1076   screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);\r
1077   if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
1078   screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);\r
1079   screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);\r
1080   screenGeometry.right = screenGeometry.left + screenWidth;\r
1081   screenGeometry.bottom = screenGeometry.top + screenHeight;\r
1082 }\r
1083 \r
1084 BOOL\r
1085 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
1086 {\r
1087   HWND hwnd; /* Main window handle. */\r
1088   int ibs;\r
1089   WINDOWPLACEMENT wp;\r
1090   char *filepart;\r
1091 \r
1092   hInst = hInstance;    /* Store instance handle in our global variable */\r
1093   programName = szAppName;\r
1094 \r
1095   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
1096     *filepart = NULLCHAR;\r
1097     SetCurrentDirectory(installDir);\r
1098   } else {\r
1099     GetCurrentDirectory(MSG_SIZ, installDir);\r
1100   }\r
1101   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
1102   InitGeometry();\r
1103   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
1104   /* xboard, and older WinBoards, controlled the move sound with the\r
1105      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1106      always turn the option on (so that the backend will call us),\r
1107      then let the user turn the sound off by setting it to silence if\r
1108      desired.  To accommodate old winboard.ini files saved by old\r
1109      versions of WinBoard, we also turn off the sound if the option\r
1110      was initially set to false. [HGM] taken out of InitAppData */\r
1111   if (!appData.ringBellAfterMoves) {\r
1112     sounds[(int)SoundMove].name = strdup("");\r
1113     appData.ringBellAfterMoves = TRUE;\r
1114   }\r
1115   if (appData.debugMode) {\r
1116     debugFP = fopen(appData.nameOfDebugFile, "w");\r
1117     setbuf(debugFP, NULL);\r
1118   }\r
1119 \r
1120   LoadLanguageFile(appData.language);\r
1121 \r
1122   InitBackEnd1();\r
1123 \r
1124 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
1125 //  InitEngineUCI( installDir, &second );\r
1126 \r
1127   /* Create a main window for this application instance. */\r
1128   hwnd = CreateWindow(szAppName, szTitle,\r
1129                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
1130                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
1131                       NULL, NULL, hInstance, NULL);\r
1132   hwndMain = hwnd;\r
1133 \r
1134   /* If window could not be created, return "failure" */\r
1135   if (!hwnd) {\r
1136     return (FALSE);\r
1137   }\r
1138 \r
1139   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
1140   LoadLogo(&first, 0, FALSE);\r
1141   LoadLogo(&second, 1, appData.icsActive);\r
1142 \r
1143   SetUserLogo();\r
1144 \r
1145   iconWhite = LoadIcon(hInstance, "icon_white");\r
1146   iconBlack = LoadIcon(hInstance, "icon_black");\r
1147   iconCurrent = iconWhite;\r
1148   InitDrawingColors();\r
1149 \r
1150   InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args\r
1151   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
1152     /* Compute window size for each board size, and use the largest\r
1153        size that fits on this screen as the default. */\r
1154     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
1155     if (boardSize == (BoardSize)-1 &&\r
1156         winH <= screenHeight\r
1157            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
1158         && winW <= screenWidth) {\r
1159       boardSize = (BoardSize)ibs;\r
1160     }\r
1161   }\r
1162 \r
1163   InitDrawingSizes(boardSize, 0);\r
1164   RecentEngineMenu(appData.recentEngineList);\r
1165   InitMenuChecks();\r
1166   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
1167 \r
1168   /* [AS] Load textures if specified */\r
1169   InitTextures();\r
1170 \r
1171   mysrandom( (unsigned) time(NULL) );\r
1172 \r
1173   /* [AS] Restore layout */\r
1174   if( wpMoveHistory.visible ) {\r
1175       MoveHistoryPopUp();\r
1176   }\r
1177 \r
1178   if( wpEvalGraph.visible ) {\r
1179       EvalGraphPopUp();\r
1180   }\r
1181 \r
1182   if( wpEngineOutput.visible ) {\r
1183       EngineOutputPopUp();\r
1184   }\r
1185 \r
1186   /* Make the window visible; update its client area; and return "success" */\r
1187   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
1188   wp.length = sizeof(WINDOWPLACEMENT);\r
1189   wp.flags = 0;\r
1190   wp.showCmd = nCmdShow;\r
1191   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
1192   wp.rcNormalPosition.left = wpMain.x;\r
1193   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
1194   wp.rcNormalPosition.top = wpMain.y;\r
1195   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
1196   SetWindowPlacement(hwndMain, &wp);\r
1197 \r
1198   InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
1199 \r
1200   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1201                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1202 \r
1203   if (hwndConsole) {\r
1204 #if AOT_CONSOLE\r
1205     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
1206                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
1207 #endif\r
1208     ShowWindow(hwndConsole, nCmdShow);\r
1209     SetActiveWindow(hwndConsole);\r
1210   }\r
1211   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
1212   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
1213 \r
1214   return TRUE;\r
1215 \r
1216 }\r
1217 \r
1218 VOID\r
1219 InitMenuChecks()\r
1220 {\r
1221   HMENU hmenu = GetMenu(hwndMain);\r
1222 \r
1223   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1224                         MF_BYCOMMAND|((appData.icsActive &&\r
1225                                        *appData.icsCommPort != NULLCHAR) ?\r
1226                                       MF_ENABLED : MF_GRAYED));\r
1227   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1228                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1229                                      MF_CHECKED : MF_UNCHECKED));\r
1230 }\r
1231 \r
1232 //---------------------------------------------------------------------------------------------------------\r
1233 \r
1234 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
1235 #define XBOARD FALSE\r
1236 \r
1237 #define OPTCHAR "/"\r
1238 #define SEPCHAR "="\r
1239 #define TOPLEVEL 0\r
1240 \r
1241 #include "args.h"\r
1242 \r
1243 // front-end part of option handling\r
1244 \r
1245 VOID\r
1246 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1247 {\r
1248   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1249   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1250   DeleteDC(hdc);\r
1251   lf->lfWidth = 0;\r
1252   lf->lfEscapement = 0;\r
1253   lf->lfOrientation = 0;\r
1254   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1255   lf->lfItalic = mfp->italic;\r
1256   lf->lfUnderline = mfp->underline;\r
1257   lf->lfStrikeOut = mfp->strikeout;\r
1258   lf->lfCharSet = mfp->charset;\r
1259   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1260   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1261   lf->lfQuality = DEFAULT_QUALITY;\r
1262   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1263     safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
1264 }\r
1265 \r
1266 void\r
1267 CreateFontInMF(MyFont *mf)\r
1268\r
1269   LFfromMFP(&mf->lf, &mf->mfp);\r
1270   if (mf->hf) DeleteObject(mf->hf);\r
1271   mf->hf = CreateFontIndirect(&mf->lf);\r
1272 }\r
1273 \r
1274 // [HGM] This platform-dependent table provides the location for storing the color info\r
1275 void *\r
1276 colorVariable[] = {\r
1277   &whitePieceColor, \r
1278   &blackPieceColor, \r
1279   &lightSquareColor,\r
1280   &darkSquareColor, \r
1281   &highlightSquareColor,\r
1282   &premoveHighlightColor,\r
1283   NULL,\r
1284   &consoleBackgroundColor,\r
1285   &appData.fontForeColorWhite,\r
1286   &appData.fontBackColorWhite,\r
1287   &appData.fontForeColorBlack,\r
1288   &appData.fontBackColorBlack,\r
1289   &appData.evalHistColorWhite,\r
1290   &appData.evalHistColorBlack,\r
1291   &appData.highlightArrowColor,\r
1292 };\r
1293 \r
1294 /* Command line font name parser.  NULL name means do nothing.\r
1295    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1296    For backward compatibility, syntax without the colon is also\r
1297    accepted, but font names with digits in them won't work in that case.\r
1298 */\r
1299 VOID\r
1300 ParseFontName(char *name, MyFontParams *mfp)\r
1301 {\r
1302   char *p, *q;\r
1303   if (name == NULL) return;\r
1304   p = name;\r
1305   q = strchr(p, ':');\r
1306   if (q) {\r
1307     if (q - p >= sizeof(mfp->faceName))\r
1308       ExitArgError(_("Font name too long:"), name, TRUE);\r
1309     memcpy(mfp->faceName, p, q - p);\r
1310     mfp->faceName[q - p] = NULLCHAR;\r
1311     p = q + 1;\r
1312   } else {\r
1313     q = mfp->faceName;\r
1314 \r
1315     while (*p && !isdigit(*p)) {\r
1316       *q++ = *p++;\r
1317       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1318         ExitArgError(_("Font name too long:"), name, TRUE);\r
1319     }\r
1320     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1321     *q = NULLCHAR;\r
1322   }\r
1323   if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);\r
1324   mfp->pointSize = (float) atof(p);\r
1325   mfp->bold = (strchr(p, 'b') != NULL);\r
1326   mfp->italic = (strchr(p, 'i') != NULL);\r
1327   mfp->underline = (strchr(p, 'u') != NULL);\r
1328   mfp->strikeout = (strchr(p, 's') != NULL);\r
1329   mfp->charset = DEFAULT_CHARSET;\r
1330   q = strchr(p, 'c');\r
1331   if (q)\r
1332     mfp->charset = (BYTE) atoi(q+1);\r
1333 }\r
1334 \r
1335 void\r
1336 ParseFont(char *name, int number)\r
1337 { // wrapper to shield back-end from 'font'\r
1338   ParseFontName(name, &font[boardSize][number]->mfp);\r
1339 }\r
1340 \r
1341 void\r
1342 SetFontDefaults()\r
1343 { // in WB  we have a 2D array of fonts; this initializes their description\r
1344   int i, j;\r
1345   /* Point font array elements to structures and\r
1346      parse default font names */\r
1347   for (i=0; i<NUM_FONTS; i++) {\r
1348     for (j=0; j<NUM_SIZES; j++) {\r
1349       font[j][i] = &fontRec[j][i];\r
1350       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1351     }\r
1352   }\r
1353 }\r
1354 \r
1355 void\r
1356 CreateFonts()\r
1357 { // here we create the actual fonts from the selected descriptions\r
1358   int i, j;\r
1359   for (i=0; i<NUM_FONTS; i++) {\r
1360     for (j=0; j<NUM_SIZES; j++) {\r
1361       CreateFontInMF(font[j][i]);\r
1362     }\r
1363   }\r
1364 }\r
1365 /* Color name parser.\r
1366    X version accepts X color names, but this one\r
1367    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1368 COLORREF\r
1369 ParseColorName(char *name)\r
1370 {\r
1371   int red, green, blue, count;\r
1372   char buf[MSG_SIZ];\r
1373 \r
1374   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1375   if (count != 3) {\r
1376     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1377       &red, &green, &blue);\r
1378   }\r
1379   if (count != 3) {\r
1380     snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
1381     DisplayError(buf, 0);\r
1382     return RGB(0, 0, 0);\r
1383   }\r
1384   return PALETTERGB(red, green, blue);\r
1385 }\r
1386 \r
1387 void\r
1388 ParseColor(int n, char *name)\r
1389 { // for WinBoard the color is an int, which needs to be derived from the string\r
1390   if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);\r
1391 }\r
1392 \r
1393 void\r
1394 ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1395 {\r
1396   char *e = argValue;\r
1397   int eff = 0;\r
1398 \r
1399   while (*e) {\r
1400     if (*e == 'b')      eff |= CFE_BOLD;\r
1401     else if (*e == 'i') eff |= CFE_ITALIC;\r
1402     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1403     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1404     else if (*e == '#' || isdigit(*e)) break;\r
1405     e++;\r
1406   }\r
1407   *effects = eff;\r
1408   *color   = ParseColorName(e);\r
1409 }\r
1410 \r
1411 void\r
1412 ParseTextAttribs(ColorClass cc, char *s)\r
1413 {   // [HGM] front-end wrapper that does the platform-dependent call\r
1414     // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);\r
1415     ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);\r
1416 }\r
1417 \r
1418 void\r
1419 ParseBoardSize(void *addr, char *name)\r
1420 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize\r
1421   BoardSize bs = SizeTiny;\r
1422   while (sizeInfo[bs].name != NULL) {\r
1423     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {\r
1424         *(BoardSize *)addr = bs;\r
1425         return;\r
1426     }\r
1427     bs++;\r
1428   }\r
1429   ExitArgError(_("Unrecognized board size value"), name, TRUE);\r
1430 }\r
1431 \r
1432 void\r
1433 LoadAllSounds()\r
1434 { // [HGM] import name from appData first\r
1435   ColorClass cc;\r
1436   SoundClass sc;\r
1437   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1438     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1439     textAttribs[cc].sound.data = NULL;\r
1440     MyLoadSound(&textAttribs[cc].sound);\r
1441   }\r
1442   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1443     textAttribs[cc].sound.name = strdup("");\r
1444     textAttribs[cc].sound.data = NULL;\r
1445   }\r
1446   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1447     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1448     sounds[sc].data = NULL;\r
1449     MyLoadSound(&sounds[sc]);\r
1450   }\r
1451 }\r
1452 \r
1453 void\r
1454 SetCommPortDefaults()\r
1455 {\r
1456    memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1457   dcb.DCBlength = sizeof(DCB);\r
1458   dcb.BaudRate = 9600;\r
1459   dcb.fBinary = TRUE;\r
1460   dcb.fParity = FALSE;\r
1461   dcb.fOutxCtsFlow = FALSE;\r
1462   dcb.fOutxDsrFlow = FALSE;\r
1463   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1464   dcb.fDsrSensitivity = FALSE;\r
1465   dcb.fTXContinueOnXoff = TRUE;\r
1466   dcb.fOutX = FALSE;\r
1467   dcb.fInX = FALSE;\r
1468   dcb.fNull = FALSE;\r
1469   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1470   dcb.fAbortOnError = FALSE;\r
1471   dcb.ByteSize = 7;\r
1472   dcb.Parity = SPACEPARITY;\r
1473   dcb.StopBits = ONESTOPBIT;\r
1474 }\r
1475 \r
1476 // [HGM] args: these three cases taken out to stay in front-end\r
1477 void\r
1478 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
1479 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
1480         // while the curent board size determines the element. This system should be ported to XBoard.\r
1481         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
1482         int bs;\r
1483         for (bs=0; bs<NUM_SIZES; bs++) {\r
1484           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1485           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1486           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
1487             ad->argName, mfp->faceName, mfp->pointSize,\r
1488             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1489             mfp->bold ? "b" : "",\r
1490             mfp->italic ? "i" : "",\r
1491             mfp->underline ? "u" : "",\r
1492             mfp->strikeout ? "s" : "",\r
1493             (int)mfp->charset);\r
1494         }\r
1495       }\r
1496 \r
1497 void\r
1498 ExportSounds()\r
1499 { // [HGM] copy the names from the internal WB variables to appData\r
1500   ColorClass cc;\r
1501   SoundClass sc;\r
1502   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
1503     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
1504   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
1505     (&appData.soundMove)[sc] = sounds[sc].name;\r
1506 }\r
1507 \r
1508 void\r
1509 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
1510 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
1511         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1512         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
1513           (ta->effects & CFE_BOLD) ? "b" : "",\r
1514           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1515           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1516           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1517           (ta->effects) ? " " : "",\r
1518           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1519       }\r
1520 \r
1521 void\r
1522 SaveColor(FILE *f, ArgDescriptor *ad)\r
1523 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
1524         COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];\r
1525         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
1526           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1527 }\r
1528 \r
1529 void\r
1530 SaveBoardSize(FILE *f, char *name, void *addr)\r
1531 { // wrapper to shield back-end from BoardSize & sizeInfo\r
1532   fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);\r
1533 }\r
1534 \r
1535 void\r
1536 ParseCommPortSettings(char *s)\r
1537 { // wrapper to keep dcb from back-end\r
1538   ParseCommSettings(s, &dcb);\r
1539 }\r
1540 \r
1541 void\r
1542 GetWindowCoords()\r
1543 { // wrapper to shield use of window handles from back-end (make addressible by number?)\r
1544   GetActualPlacement(hwndMain, &wpMain);\r
1545   GetActualPlacement(hwndConsole, &wpConsole);\r
1546   GetActualPlacement(commentDialog, &wpComment);\r
1547   GetActualPlacement(editTagsDialog, &wpTags);\r
1548   GetActualPlacement(gameListDialog, &wpGameList);\r
1549   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
1550   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
1551   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
1552 }\r
1553 \r
1554 void\r
1555 PrintCommPortSettings(FILE *f, char *name)\r
1556 { // wrapper to shield back-end from DCB\r
1557       PrintCommSettings(f, name, &dcb);\r
1558 }\r
1559 \r
1560 int\r
1561 MySearchPath(char *installDir, char *name, char *fullname)\r
1562 {\r
1563   char *dummy, buf[MSG_SIZ], *p = name, *q;\r
1564   if(name[0]== '%') {\r
1565     fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
1566     while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
1567       safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
1568       *strchr(buf, '%') = 0;\r
1569       strcat(fullname, getenv(buf));\r
1570       p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
1571     }\r
1572     strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
1573     if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
1574     return (int) strlen(fullname);\r
1575   }\r
1576   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1577 }\r
1578 \r
1579 int\r
1580 MyGetFullPathName(char *name, char *fullname)\r
1581 {\r
1582   char *dummy;\r
1583   return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);\r
1584 }\r
1585 \r
1586 int\r
1587 MainWindowUp()\r
1588 { // [HGM] args: allows testing if main window is realized from back-end\r
1589   return hwndMain != NULL;\r
1590 }\r
1591 \r
1592 void\r
1593 PopUpStartupDialog()\r
1594 {\r
1595     FARPROC lpProc;\r
1596     \r
1597     LoadLanguageFile(appData.language);\r
1598     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1599     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1600     FreeProcInstance(lpProc);\r
1601 }\r
1602 \r
1603 /*---------------------------------------------------------------------------*\\r
1604  *\r
1605  * GDI board drawing routines\r
1606  *\r
1607 \*---------------------------------------------------------------------------*/\r
1608 \r
1609 /* [AS] Draw square using background texture */\r
1610 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
1611 {\r
1612     XFORM   x;\r
1613 \r
1614     if( mode == 0 ) {\r
1615         return; /* Should never happen! */\r
1616     }\r
1617 \r
1618     SetGraphicsMode( dst, GM_ADVANCED );\r
1619 \r
1620     switch( mode ) {\r
1621     case 1:\r
1622         /* Identity */\r
1623         break;\r
1624     case 2:\r
1625         /* X reflection */\r
1626         x.eM11 = -1.0;\r
1627         x.eM12 = 0;\r
1628         x.eM21 = 0;\r
1629         x.eM22 = 1.0;\r
1630         x.eDx = (FLOAT) dw + dx - 1;\r
1631         x.eDy = 0;\r
1632         dx = 0;\r
1633         SetWorldTransform( dst, &x );\r
1634         break;\r
1635     case 3:\r
1636         /* Y reflection */\r
1637         x.eM11 = 1.0;\r
1638         x.eM12 = 0;\r
1639         x.eM21 = 0;\r
1640         x.eM22 = -1.0;\r
1641         x.eDx = 0;\r
1642         x.eDy = (FLOAT) dh + dy - 1;\r
1643         dy = 0;\r
1644         SetWorldTransform( dst, &x );\r
1645         break;\r
1646     case 4:\r
1647         /* X/Y flip */\r
1648         x.eM11 = 0;\r
1649         x.eM12 = 1.0;\r
1650         x.eM21 = 1.0;\r
1651         x.eM22 = 0;\r
1652         x.eDx = (FLOAT) dx;\r
1653         x.eDy = (FLOAT) dy;\r
1654         dx = 0;\r
1655         dy = 0;\r
1656         SetWorldTransform( dst, &x );\r
1657         break;\r
1658     }\r
1659 \r
1660     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
1661 \r
1662     x.eM11 = 1.0;\r
1663     x.eM12 = 0;\r
1664     x.eM21 = 0;\r
1665     x.eM22 = 1.0;\r
1666     x.eDx = 0;\r
1667     x.eDy = 0;\r
1668     SetWorldTransform( dst, &x );\r
1669 \r
1670     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
1671 }\r
1672 \r
1673 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
1674 enum {\r
1675     PM_WP = (int) WhitePawn, \r
1676     PM_WN = (int) WhiteKnight, \r
1677     PM_WB = (int) WhiteBishop, \r
1678     PM_WR = (int) WhiteRook, \r
1679     PM_WQ = (int) WhiteQueen, \r
1680     PM_WF = (int) WhiteFerz, \r
1681     PM_WW = (int) WhiteWazir, \r
1682     PM_WE = (int) WhiteAlfil, \r
1683     PM_WM = (int) WhiteMan, \r
1684     PM_WO = (int) WhiteCannon, \r
1685     PM_WU = (int) WhiteUnicorn, \r
1686     PM_WH = (int) WhiteNightrider, \r
1687     PM_WA = (int) WhiteAngel, \r
1688     PM_WC = (int) WhiteMarshall, \r
1689     PM_WAB = (int) WhiteCardinal, \r
1690     PM_WD = (int) WhiteDragon, \r
1691     PM_WL = (int) WhiteLance, \r
1692     PM_WS = (int) WhiteCobra, \r
1693     PM_WV = (int) WhiteFalcon, \r
1694     PM_WSG = (int) WhiteSilver, \r
1695     PM_WG = (int) WhiteGrasshopper, \r
1696     PM_WK = (int) WhiteKing,\r
1697     PM_BP = (int) BlackPawn, \r
1698     PM_BN = (int) BlackKnight, \r
1699     PM_BB = (int) BlackBishop, \r
1700     PM_BR = (int) BlackRook, \r
1701     PM_BQ = (int) BlackQueen, \r
1702     PM_BF = (int) BlackFerz, \r
1703     PM_BW = (int) BlackWazir, \r
1704     PM_BE = (int) BlackAlfil, \r
1705     PM_BM = (int) BlackMan,\r
1706     PM_BO = (int) BlackCannon, \r
1707     PM_BU = (int) BlackUnicorn, \r
1708     PM_BH = (int) BlackNightrider, \r
1709     PM_BA = (int) BlackAngel, \r
1710     PM_BC = (int) BlackMarshall, \r
1711     PM_BG = (int) BlackGrasshopper, \r
1712     PM_BAB = (int) BlackCardinal,\r
1713     PM_BD = (int) BlackDragon,\r
1714     PM_BL = (int) BlackLance,\r
1715     PM_BS = (int) BlackCobra,\r
1716     PM_BV = (int) BlackFalcon,\r
1717     PM_BSG = (int) BlackSilver,\r
1718     PM_BK = (int) BlackKing\r
1719 };\r
1720 \r
1721 static HFONT hPieceFont = NULL;\r
1722 static HBITMAP hPieceMask[(int) EmptySquare];\r
1723 static HBITMAP hPieceFace[(int) EmptySquare];\r
1724 static int fontBitmapSquareSize = 0;\r
1725 static char pieceToFontChar[(int) EmptySquare] =\r
1726                               { 'p', 'n', 'b', 'r', 'q', \r
1727                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
1728                       'k', 'o', 'm', 'v', 't', 'w', \r
1729                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
1730                                                               'l' };\r
1731 \r
1732 extern BOOL SetCharTable( char *table, const char * map );\r
1733 /* [HGM] moved to backend.c */\r
1734 \r
1735 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
1736 {\r
1737     HBRUSH hbrush;\r
1738     BYTE r1 = GetRValue( color );\r
1739     BYTE g1 = GetGValue( color );\r
1740     BYTE b1 = GetBValue( color );\r
1741     BYTE r2 = r1 / 2;\r
1742     BYTE g2 = g1 / 2;\r
1743     BYTE b2 = b1 / 2;\r
1744     RECT rc;\r
1745 \r
1746     /* Create a uniform background first */\r
1747     hbrush = CreateSolidBrush( color );\r
1748     SetRect( &rc, 0, 0, squareSize, squareSize );\r
1749     FillRect( hdc, &rc, hbrush );\r
1750     DeleteObject( hbrush );\r
1751     \r
1752     if( mode == 1 ) {\r
1753         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
1754         int steps = squareSize / 2;\r
1755         int i;\r
1756 \r
1757         for( i=0; i<steps; i++ ) {\r
1758             BYTE r = r1 - (r1-r2) * i / steps;\r
1759             BYTE g = g1 - (g1-g2) * i / steps;\r
1760             BYTE b = b1 - (b1-b2) * i / steps;\r
1761 \r
1762             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
1763             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
1764             FillRect( hdc, &rc, hbrush );\r
1765             DeleteObject(hbrush);\r
1766         }\r
1767     }\r
1768     else if( mode == 2 ) {\r
1769         /* Diagonal gradient, good more or less for every piece */\r
1770         POINT triangle[3];\r
1771         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
1772         HBRUSH hbrush_old;\r
1773         int steps = squareSize;\r
1774         int i;\r
1775 \r
1776         triangle[0].x = squareSize - steps;\r
1777         triangle[0].y = squareSize;\r
1778         triangle[1].x = squareSize;\r
1779         triangle[1].y = squareSize;\r
1780         triangle[2].x = squareSize;\r
1781         triangle[2].y = squareSize - steps;\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             hbrush_old = SelectObject( hdc, hbrush );\r
1790             Polygon( hdc, triangle, 3 );\r
1791             SelectObject( hdc, hbrush_old );\r
1792             DeleteObject(hbrush);\r
1793             triangle[0].x++;\r
1794             triangle[2].y++;\r
1795         }\r
1796 \r
1797         SelectObject( hdc, hpen );\r
1798     }\r
1799 }\r
1800 \r
1801 /*\r
1802     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
1803     seems to work ok. The main problem here is to find the "inside" of a chess\r
1804     piece: follow the steps as explained below.\r
1805 */\r
1806 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
1807 {\r
1808     HBITMAP hbm;\r
1809     HBITMAP hbm_old;\r
1810     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
1811     RECT rc;\r
1812     SIZE sz;\r
1813     POINT pt;\r
1814     int backColor = whitePieceColor; \r
1815     int foreColor = blackPieceColor;\r
1816     \r
1817     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
1818         backColor = appData.fontBackColorWhite;\r
1819         foreColor = appData.fontForeColorWhite;\r
1820     }\r
1821     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
1822         backColor = appData.fontBackColorBlack;\r
1823         foreColor = appData.fontForeColorBlack;\r
1824     }\r
1825 \r
1826     /* Mask */\r
1827     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1828 \r
1829     hbm_old = SelectObject( hdc, hbm );\r
1830 \r
1831     rc.left = 0;\r
1832     rc.top = 0;\r
1833     rc.right = squareSize;\r
1834     rc.bottom = squareSize;\r
1835 \r
1836     /* Step 1: background is now black */\r
1837     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
1838 \r
1839     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
1840 \r
1841     pt.x = (squareSize - sz.cx) / 2;\r
1842     pt.y = (squareSize - sz.cy) / 2;\r
1843 \r
1844     SetBkMode( hdc, TRANSPARENT );\r
1845     SetTextColor( hdc, chroma );\r
1846     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
1847     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1848 \r
1849     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
1850     /* Step 3: the area outside the piece is filled with white */\r
1851 //    FloodFill( hdc, 0, 0, chroma );\r
1852     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
1853     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
1854     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
1855     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
1856     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
1857     /* \r
1858         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
1859         but if the start point is not inside the piece we're lost!\r
1860         There should be a better way to do this... if we could create a region or path\r
1861         from the fill operation we would be fine for example.\r
1862     */\r
1863 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
1864     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
1865 \r
1866     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
1867         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1868         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1869 \r
1870         SelectObject( dc2, bm2 );\r
1871         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
1872         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1873         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1874         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1875         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
1876 \r
1877         DeleteDC( dc2 );\r
1878         DeleteObject( bm2 );\r
1879     }\r
1880 \r
1881     SetTextColor( hdc, 0 );\r
1882     /* \r
1883         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
1884         draw the piece again in black for safety.\r
1885     */\r
1886     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1887 \r
1888     SelectObject( hdc, hbm_old );\r
1889 \r
1890     if( hPieceMask[index] != NULL ) {\r
1891         DeleteObject( hPieceMask[index] );\r
1892     }\r
1893 \r
1894     hPieceMask[index] = hbm;\r
1895 \r
1896     /* Face */\r
1897     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1898 \r
1899     SelectObject( hdc, hbm );\r
1900 \r
1901     {\r
1902         HDC dc1 = CreateCompatibleDC( hdc_window );\r
1903         HDC dc2 = CreateCompatibleDC( hdc_window );\r
1904         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
1905 \r
1906         SelectObject( dc1, hPieceMask[index] );\r
1907         SelectObject( dc2, bm2 );\r
1908         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
1909         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
1910         \r
1911         /* \r
1912             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
1913             the piece background and deletes (makes transparent) the rest.\r
1914             Thanks to that mask, we are free to paint the background with the greates\r
1915             freedom, as we'll be able to mask off the unwanted parts when finished.\r
1916             We use this, to make gradients and give the pieces a "roundish" look.\r
1917         */\r
1918         SetPieceBackground( hdc, backColor, 2 );\r
1919         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
1920 \r
1921         DeleteDC( dc2 );\r
1922         DeleteDC( dc1 );\r
1923         DeleteObject( bm2 );\r
1924     }\r
1925 \r
1926     SetTextColor( hdc, foreColor );\r
1927     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
1928 \r
1929     SelectObject( hdc, hbm_old );\r
1930 \r
1931     if( hPieceFace[index] != NULL ) {\r
1932         DeleteObject( hPieceFace[index] );\r
1933     }\r
1934 \r
1935     hPieceFace[index] = hbm;\r
1936 }\r
1937 \r
1938 static int TranslatePieceToFontPiece( int piece )\r
1939 {\r
1940     switch( piece ) {\r
1941     case BlackPawn:\r
1942         return PM_BP;\r
1943     case BlackKnight:\r
1944         return PM_BN;\r
1945     case BlackBishop:\r
1946         return PM_BB;\r
1947     case BlackRook:\r
1948         return PM_BR;\r
1949     case BlackQueen:\r
1950         return PM_BQ;\r
1951     case BlackKing:\r
1952         return PM_BK;\r
1953     case WhitePawn:\r
1954         return PM_WP;\r
1955     case WhiteKnight:\r
1956         return PM_WN;\r
1957     case WhiteBishop:\r
1958         return PM_WB;\r
1959     case WhiteRook:\r
1960         return PM_WR;\r
1961     case WhiteQueen:\r
1962         return PM_WQ;\r
1963     case WhiteKing:\r
1964         return PM_WK;\r
1965 \r
1966     case BlackAngel:\r
1967         return PM_BA;\r
1968     case BlackMarshall:\r
1969         return PM_BC;\r
1970     case BlackFerz:\r
1971         return PM_BF;\r
1972     case BlackNightrider:\r
1973         return PM_BH;\r
1974     case BlackAlfil:\r
1975         return PM_BE;\r
1976     case BlackWazir:\r
1977         return PM_BW;\r
1978     case BlackUnicorn:\r
1979         return PM_BU;\r
1980     case BlackCannon:\r
1981         return PM_BO;\r
1982     case BlackGrasshopper:\r
1983         return PM_BG;\r
1984     case BlackMan:\r
1985         return PM_BM;\r
1986     case BlackSilver:\r
1987         return PM_BSG;\r
1988     case BlackLance:\r
1989         return PM_BL;\r
1990     case BlackFalcon:\r
1991         return PM_BV;\r
1992     case BlackCobra:\r
1993         return PM_BS;\r
1994     case BlackCardinal:\r
1995         return PM_BAB;\r
1996     case BlackDragon:\r
1997         return PM_BD;\r
1998 \r
1999     case WhiteAngel:\r
2000         return PM_WA;\r
2001     case WhiteMarshall:\r
2002         return PM_WC;\r
2003     case WhiteFerz:\r
2004         return PM_WF;\r
2005     case WhiteNightrider:\r
2006         return PM_WH;\r
2007     case WhiteAlfil:\r
2008         return PM_WE;\r
2009     case WhiteWazir:\r
2010         return PM_WW;\r
2011     case WhiteUnicorn:\r
2012         return PM_WU;\r
2013     case WhiteCannon:\r
2014         return PM_WO;\r
2015     case WhiteGrasshopper:\r
2016         return PM_WG;\r
2017     case WhiteMan:\r
2018         return PM_WM;\r
2019     case WhiteSilver:\r
2020         return PM_WSG;\r
2021     case WhiteLance:\r
2022         return PM_WL;\r
2023     case WhiteFalcon:\r
2024         return PM_WV;\r
2025     case WhiteCobra:\r
2026         return PM_WS;\r
2027     case WhiteCardinal:\r
2028         return PM_WAB;\r
2029     case WhiteDragon:\r
2030         return PM_WD;\r
2031     }\r
2032 \r
2033     return 0;\r
2034 }\r
2035 \r
2036 void CreatePiecesFromFont()\r
2037 {\r
2038     LOGFONT lf;\r
2039     HDC hdc_window = NULL;\r
2040     HDC hdc = NULL;\r
2041     HFONT hfont_old;\r
2042     int fontHeight;\r
2043     int i;\r
2044 \r
2045     if( fontBitmapSquareSize < 0 ) {\r
2046         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2047         return;\r
2048     }\r
2049 \r
2050     if( !appData.useFont || appData.renderPiecesWithFont == NULL ||\r
2051             appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2052         fontBitmapSquareSize = -1;\r
2053         return;\r
2054     }\r
2055 \r
2056     if( fontBitmapSquareSize != squareSize ) {\r
2057         hdc_window = GetDC( hwndMain );\r
2058         hdc = CreateCompatibleDC( hdc_window );\r
2059 \r
2060         if( hPieceFont != NULL ) {\r
2061             DeleteObject( hPieceFont );\r
2062         }\r
2063         else {\r
2064             for( i=0; i<=(int)BlackKing; i++ ) {\r
2065                 hPieceMask[i] = NULL;\r
2066                 hPieceFace[i] = NULL;\r
2067             }\r
2068         }\r
2069 \r
2070         fontHeight = 75;\r
2071 \r
2072         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2073             fontHeight = appData.fontPieceSize;\r
2074         }\r
2075 \r
2076         fontHeight = (fontHeight * squareSize) / 100;\r
2077 \r
2078         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2079         lf.lfWidth = 0;\r
2080         lf.lfEscapement = 0;\r
2081         lf.lfOrientation = 0;\r
2082         lf.lfWeight = FW_NORMAL;\r
2083         lf.lfItalic = 0;\r
2084         lf.lfUnderline = 0;\r
2085         lf.lfStrikeOut = 0;\r
2086         lf.lfCharSet = DEFAULT_CHARSET;\r
2087         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2088         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2089         lf.lfQuality = PROOF_QUALITY;\r
2090         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2091         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2092         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2093 \r
2094         hPieceFont = CreateFontIndirect( &lf );\r
2095 \r
2096         if( hPieceFont == NULL ) {\r
2097             fontBitmapSquareSize = -2;\r
2098         }\r
2099         else {\r
2100             /* Setup font-to-piece character table */\r
2101             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2102                 /* No (or wrong) global settings, try to detect the font */\r
2103                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2104                     /* Alpha */\r
2105                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2106                 }\r
2107                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2108                     /* DiagramTT* family */\r
2109                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2110                 }\r
2111                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2112                     /* Fairy symbols */\r
2113                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2114                 }\r
2115                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2116                     /* Good Companion (Some characters get warped as literal :-( */\r
2117                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2118                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2119                     SetCharTable(pieceToFontChar, s);\r
2120                 }\r
2121                 else {\r
2122                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2123                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2124                 }\r
2125             }\r
2126 \r
2127             /* Create bitmaps */\r
2128             hfont_old = SelectObject( hdc, hPieceFont );\r
2129             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2130                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2131                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2132 \r
2133             SelectObject( hdc, hfont_old );\r
2134 \r
2135             fontBitmapSquareSize = squareSize;\r
2136         }\r
2137     }\r
2138 \r
2139     if( hdc != NULL ) {\r
2140         DeleteDC( hdc );\r
2141     }\r
2142 \r
2143     if( hdc_window != NULL ) {\r
2144         ReleaseDC( hwndMain, hdc_window );\r
2145     }\r
2146 }\r
2147 \r
2148 HBITMAP\r
2149 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2150 {\r
2151   char name[128], buf[MSG_SIZ];\r
2152 \r
2153     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
2154   if(appData.pieceDirectory[0]) {\r
2155     HBITMAP res;\r
2156     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
2157     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
2158     if(res) return res;\r
2159   }\r
2160   if (gameInfo.event &&\r
2161       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2162       strcmp(name, "k80s") == 0) {\r
2163     safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
2164   }\r
2165   return LoadBitmap(hinst, name);\r
2166 }\r
2167 \r
2168 \r
2169 /* Insert a color into the program's logical palette\r
2170    structure.  This code assumes the given color is\r
2171    the result of the RGB or PALETTERGB macro, and it\r
2172    knows how those macros work (which is documented).\r
2173 */\r
2174 VOID\r
2175 InsertInPalette(COLORREF color)\r
2176 {\r
2177   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2178 \r
2179   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2180     DisplayFatalError(_("Too many colors"), 0, 1);\r
2181     pLogPal->palNumEntries--;\r
2182     return;\r
2183   }\r
2184 \r
2185   pe->peFlags = (char) 0;\r
2186   pe->peRed = (char) (0xFF & color);\r
2187   pe->peGreen = (char) (0xFF & (color >> 8));\r
2188   pe->peBlue = (char) (0xFF & (color >> 16));\r
2189   return;\r
2190 }\r
2191 \r
2192 \r
2193 VOID\r
2194 InitDrawingColors()\r
2195 {\r
2196   int i;\r
2197   if (pLogPal == NULL) {\r
2198     /* Allocate enough memory for a logical palette with\r
2199      * PALETTESIZE entries and set the size and version fields\r
2200      * of the logical palette structure.\r
2201      */\r
2202     pLogPal = (NPLOGPALETTE)\r
2203       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2204                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2205     pLogPal->palVersion    = 0x300;\r
2206   }\r
2207   pLogPal->palNumEntries = 0;\r
2208 \r
2209   InsertInPalette(lightSquareColor);\r
2210   InsertInPalette(darkSquareColor);\r
2211   InsertInPalette(whitePieceColor);\r
2212   InsertInPalette(blackPieceColor);\r
2213   InsertInPalette(highlightSquareColor);\r
2214   InsertInPalette(premoveHighlightColor);\r
2215 \r
2216   /*  create a logical color palette according the information\r
2217    *  in the LOGPALETTE structure.\r
2218    */\r
2219   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2220 \r
2221   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2222   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2223   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2224   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2225   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2226   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2227   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2228     for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers\r
2229 \r
2230    /* [AS] Force rendering of the font-based pieces */\r
2231   if( fontBitmapSquareSize > 0 ) {\r
2232     fontBitmapSquareSize = 0;\r
2233   }\r
2234 }\r
2235 \r
2236 \r
2237 int\r
2238 BoardWidth(int boardSize, int n)\r
2239 { /* [HGM] argument n added to allow different width and height */\r
2240   int lineGap = sizeInfo[boardSize].lineGap;\r
2241 \r
2242   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2243       lineGap = appData.overrideLineGap;\r
2244   }\r
2245 \r
2246   return (n + 1) * lineGap +\r
2247           n * sizeInfo[boardSize].squareSize;\r
2248 }\r
2249 \r
2250 /* Respond to board resize by dragging edge */\r
2251 VOID\r
2252 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2253 {\r
2254   BoardSize newSize = NUM_SIZES - 1;\r
2255   static int recurse = 0;\r
2256   if (IsIconic(hwndMain)) return;\r
2257   if (recurse > 0) return;\r
2258   recurse++;\r
2259   while (newSize > 0) {\r
2260         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2261         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2262            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2263     newSize--;\r
2264   } \r
2265   boardSize = newSize;\r
2266   InitDrawingSizes(boardSize, flags);\r
2267   recurse--;\r
2268 }\r
2269 \r
2270 \r
2271 extern Boolean twoBoards, partnerUp; // [HGM] dual\r
2272 \r
2273 VOID\r
2274 InitDrawingSizes(BoardSize boardSize, int flags)\r
2275 {\r
2276   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2277   ChessSquare piece;\r
2278   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2279   HDC hdc;\r
2280   SIZE clockSize, messageSize;\r
2281   HFONT oldFont;\r
2282   char buf[MSG_SIZ];\r
2283   char *str;\r
2284   HMENU hmenu = GetMenu(hwndMain);\r
2285   RECT crect, wrect, oldRect;\r
2286   int offby;\r
2287   LOGBRUSH logbrush;\r
2288   VariantClass v = gameInfo.variant;\r
2289 \r
2290   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2291   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2292 \r
2293   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2294   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2295   if(boardSize == -1) return;     // no size defined yet; abort (to allow early call of InitPosition)\r
2296   oldBoardSize = boardSize;\r
2297 \r
2298   if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)\r
2299   { // correct board size to one where built-in pieces exist\r
2300     if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)\r
2301        && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range\r
2302       || (v == VariantShogi && boardSize != SizeModerate)   // Japanese-style Shogi\r
2303       ||  v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan\r
2304       ||  v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {\r
2305       if(boardSize < SizeMediocre) boardSize = SizePetite; else\r
2306       if(boardSize > SizeModerate) boardSize = SizeBulky;  else\r
2307                                    boardSize = SizeMiddling;\r
2308     }\r
2309   }\r
2310   if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite\r
2311 \r
2312   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2313   oldRect.top = wpMain.y;\r
2314   oldRect.right = wpMain.x + wpMain.width;\r
2315   oldRect.bottom = wpMain.y + wpMain.height;\r
2316 \r
2317   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2318   smallLayout = sizeInfo[boardSize].smallLayout;\r
2319   squareSize = sizeInfo[boardSize].squareSize;\r
2320   lineGap = sizeInfo[boardSize].lineGap;\r
2321   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2322   border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
2323 \r
2324   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2325       lineGap = appData.overrideLineGap;\r
2326   }\r
2327 \r
2328   if (tinyLayout != oldTinyLayout) {\r
2329     long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
2330     if (tinyLayout) {\r
2331       style &= ~WS_SYSMENU;\r
2332       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2333                  "&Minimize\tCtrl+F4");\r
2334     } else {\r
2335       style |= WS_SYSMENU;\r
2336       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2337     }\r
2338     SetWindowLongPtr(hwndMain, GWL_STYLE, style);\r
2339 \r
2340     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2341       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2342         (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
2343     }\r
2344     DrawMenuBar(hwndMain);\r
2345   }\r
2346 \r
2347   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;\r
2348   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;\r
2349 \r
2350   /* Get text area sizes */\r
2351   hdc = GetDC(hwndMain);\r
2352   if (appData.clockMode) {\r
2353     snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
2354   } else {\r
2355     snprintf(buf, MSG_SIZ, _("White"));\r
2356   }\r
2357   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2358   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2359   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2360   str = _("We only care about the height here");\r
2361   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2362   SelectObject(hdc, oldFont);\r
2363   ReleaseDC(hwndMain, hdc);\r
2364 \r
2365   /* Compute where everything goes */\r
2366   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
2367         /* [HGM] logo: if either logo is on, reserve space for it */\r
2368         logoHeight =  2*clockSize.cy;\r
2369         leftLogoRect.left   = OUTER_MARGIN;\r
2370         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
2371         leftLogoRect.top    = OUTER_MARGIN;\r
2372         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2373 \r
2374         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
2375         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
2376         rightLogoRect.top    = OUTER_MARGIN;\r
2377         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
2378 \r
2379 \r
2380     whiteRect.left = leftLogoRect.right;\r
2381     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
2382     whiteRect.top = OUTER_MARGIN;\r
2383     whiteRect.bottom = whiteRect.top + logoHeight;\r
2384 \r
2385     blackRect.right = rightLogoRect.left;\r
2386     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2387     blackRect.top = whiteRect.top;\r
2388     blackRect.bottom = whiteRect.bottom;\r
2389   } else {\r
2390     whiteRect.left = OUTER_MARGIN;\r
2391     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2392     whiteRect.top = OUTER_MARGIN;\r
2393     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2394 \r
2395     blackRect.left = whiteRect.right + INNER_MARGIN;\r
2396     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2397     blackRect.top = whiteRect.top;\r
2398     blackRect.bottom = whiteRect.bottom;\r
2399 \r
2400     logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
2401   }\r
2402 \r
2403   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
2404   if (appData.showButtonBar) {\r
2405     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
2406       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2407   } else {\r
2408     messageRect.right = OUTER_MARGIN + boardWidth;\r
2409   }\r
2410   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2411   messageRect.bottom = messageRect.top + messageSize.cy;\r
2412 \r
2413   boardRect.left = OUTER_MARGIN;\r
2414   boardRect.right = boardRect.left + boardWidth;\r
2415   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2416   boardRect.bottom = boardRect.top + boardHeight;\r
2417 \r
2418   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2419   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2420   oldTinyLayout = tinyLayout;\r
2421   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2422   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2423     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2424   winW *= 1 + twoBoards;\r
2425   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
2426   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
2427   wpMain.height = winH; //       without disturbing window attachments\r
2428   GetWindowRect(hwndMain, &wrect);\r
2429   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2430                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2431 \r
2432   // [HGM] placement: let attached windows follow size change.\r
2433   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
2434   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
2435   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
2436   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
2437   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
2438 \r
2439   /* compensate if menu bar wrapped */\r
2440   GetClientRect(hwndMain, &crect);\r
2441   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2442   wpMain.height += offby;\r
2443   switch (flags) {\r
2444   case WMSZ_TOPLEFT:\r
2445     SetWindowPos(hwndMain, NULL, \r
2446                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
2447                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2448     break;\r
2449 \r
2450   case WMSZ_TOPRIGHT:\r
2451   case WMSZ_TOP:\r
2452     SetWindowPos(hwndMain, NULL, \r
2453                  wrect.left, wrect.bottom - wpMain.height, \r
2454                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2455     break;\r
2456 \r
2457   case WMSZ_BOTTOMLEFT:\r
2458   case WMSZ_LEFT:\r
2459     SetWindowPos(hwndMain, NULL, \r
2460                  wrect.right - wpMain.width, wrect.top, \r
2461                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2462     break;\r
2463 \r
2464   case WMSZ_BOTTOMRIGHT:\r
2465   case WMSZ_BOTTOM:\r
2466   case WMSZ_RIGHT:\r
2467   default:\r
2468     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
2469                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2470     break;\r
2471   }\r
2472 \r
2473   hwndPause = NULL;\r
2474   for (i = 0; i < N_BUTTONS; i++) {\r
2475     if (buttonDesc[i].hwnd != NULL) {\r
2476       DestroyWindow(buttonDesc[i].hwnd);\r
2477       buttonDesc[i].hwnd = NULL;\r
2478     }\r
2479     if (appData.showButtonBar) {\r
2480       buttonDesc[i].hwnd =\r
2481         CreateWindow("BUTTON", buttonDesc[i].label,\r
2482                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2483                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2484                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2485                      (HMENU) buttonDesc[i].id,\r
2486                      (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
2487       if (tinyLayout) {\r
2488         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2489                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2490                     MAKELPARAM(FALSE, 0));\r
2491       }\r
2492       if (buttonDesc[i].id == IDM_Pause)\r
2493         hwndPause = buttonDesc[i].hwnd;\r
2494       buttonDesc[i].wndproc = (WNDPROC)\r
2495         SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);\r
2496     }\r
2497   }\r
2498   if (gridPen != NULL) DeleteObject(gridPen);\r
2499   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2500   if (premovePen != NULL) DeleteObject(premovePen);\r
2501   if (lineGap != 0) {\r
2502     logbrush.lbStyle = BS_SOLID;\r
2503     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2504     gridPen =\r
2505       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2506                    lineGap, &logbrush, 0, NULL);\r
2507     logbrush.lbColor = highlightSquareColor;\r
2508     highlightPen =\r
2509       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2510                    lineGap, &logbrush, 0, NULL);\r
2511 \r
2512     logbrush.lbColor = premoveHighlightColor; \r
2513     premovePen =\r
2514       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2515                    lineGap, &logbrush, 0, NULL);\r
2516 \r
2517     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
2518     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
2519       gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;\r
2520       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2521         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2522       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2523         BOARD_WIDTH * (squareSize + lineGap) + border;\r
2524       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2525     }\r
2526     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
2527       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;\r
2528       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
2529         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
2530         lineGap / 2 + (i * (squareSize + lineGap)) + border;\r
2531       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
2532         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;\r
2533       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2534     }\r
2535   }\r
2536 \r
2537   /* [HGM] Licensing requirement */\r
2538 #ifdef GOTHIC\r
2539   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
2540 #endif\r
2541 #ifdef FALCON\r
2542   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
2543 #endif\r
2544   GothicPopUp( "", VariantNormal);\r
2545 \r
2546 \r
2547 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
2548 \r
2549   /* Load piece bitmaps for this board size */\r
2550   for (i=0; i<=2; i++) {\r
2551     for (piece = WhitePawn;\r
2552          (int) piece < (int) BlackPawn;\r
2553          piece = (ChessSquare) ((int) piece + 1)) {\r
2554       if (pieceBitmap[i][piece] != NULL)\r
2555         DeleteObject(pieceBitmap[i][piece]);\r
2556     }\r
2557   }\r
2558 \r
2559   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
2560   // Orthodox Chess pieces\r
2561   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2562   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2563   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2564   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2565   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2566   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2567   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2568   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2569   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2570   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2571   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2572   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2573   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2574   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2575   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2576   if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {\r
2577     // in Shogi, Hijack the unused Queen for Lance\r
2578     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2579     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2580     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2581   } else {\r
2582     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2583     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2584     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2585   }\r
2586 \r
2587   if(squareSize <= 72 && squareSize >= 33) { \r
2588     /* A & C are available in most sizes now */\r
2589     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
2590       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2591       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2592       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2593       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2594       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2595       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2596       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2597       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2598       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2599       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2600       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2601       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2602     } else { // Smirf-like\r
2603       if(gameInfo.variant == VariantSChess) {\r
2604         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2605         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2606         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2607       } else {\r
2608         pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
2609         pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
2610         pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
2611       }\r
2612     }\r
2613     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
2614       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2615       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2616       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2617     } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {\r
2618       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2619       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2620       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2621     } else { // WinBoard standard\r
2622       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
2623       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
2624       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
2625     }\r
2626   }\r
2627 \r
2628 \r
2629   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
2630     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
2631     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
2632     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
2633     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
2634     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
2635     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2636     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
2637     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
2638     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
2639     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
2640     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
2641     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
2642     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
2643     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
2644     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
2645     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
2646     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
2647     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
2648     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
2649     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
2650     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
2651     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
2652     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
2653     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
2654     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
2655     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
2656     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
2657     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
2658     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
2659     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
2660     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
2661     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
2662     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
2663 \r
2664     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
2665       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
2666       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
2667       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2668       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
2669       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
2670       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2671       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
2672       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
2673       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2674       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
2675       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
2676       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
2677     } else {\r
2678       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
2679       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
2680       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
2681       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
2682       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
2683       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
2684       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
2685       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
2686       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
2687       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
2688       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
2689       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
2690     }\r
2691 \r
2692   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
2693     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
2694     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
2695     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
2696     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
2697     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
2698     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
2699     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
2700     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
2701     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
2702     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
2703     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
2704     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
2705     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
2706     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
2707   }\r
2708 \r
2709 \r
2710   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
2711   /* special Shogi support in this size */\r
2712   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
2713       for (piece = WhitePawn;\r
2714            (int) piece < (int) BlackPawn;\r
2715            piece = (ChessSquare) ((int) piece + 1)) {\r
2716         if (pieceBitmap[i][piece] != NULL)\r
2717           DeleteObject(pieceBitmap[i][piece]);\r
2718       }\r
2719     }\r
2720   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2721   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2722   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2723   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2724   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2725   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2726   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2727   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2728   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2729   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2730   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2731   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2732   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2733   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2734   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
2735   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
2736   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
2737   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
2738   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
2739   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
2740   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
2741   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
2742   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
2743   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
2744   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
2745   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
2746   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
2747   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
2748   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2749   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2750   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2751   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2752   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2753   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
2754   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2755   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2756   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
2757   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
2758   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2759   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
2760   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
2761   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
2762   minorSize = 0;\r
2763   }\r
2764 }\r
2765 \r
2766 HBITMAP\r
2767 PieceBitmap(ChessSquare p, int kind)\r
2768 {\r
2769   if ((int) p >= (int) BlackPawn)\r
2770     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2771 \r
2772   return pieceBitmap[kind][(int) p];\r
2773 }\r
2774 \r
2775 /***************************************************************/\r
2776 \r
2777 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2778 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2779 /*\r
2780 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2781 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2782 */\r
2783 \r
2784 VOID\r
2785 SquareToPos(int row, int column, int * x, int * y)\r
2786 {\r
2787   if (flipView) {\r
2788     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
2789     *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;\r
2790   } else {\r
2791     *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;\r
2792     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
2793   }\r
2794 }\r
2795 \r
2796 VOID\r
2797 DrawCoordsOnDC(HDC hdc)\r
2798 {\r
2799   static char files[] = "0123456789012345678901221098765432109876543210";\r
2800   static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";\r
2801   char str[2] = { NULLCHAR, NULLCHAR };\r
2802   int oldMode, oldAlign, x, y, start, i;\r
2803   HFONT oldFont;\r
2804   HBRUSH oldBrush;\r
2805 \r
2806   if (!appData.showCoords)\r
2807     return;\r
2808 \r
2809   start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;\r
2810 \r
2811   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2812   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2813   oldAlign = GetTextAlign(hdc);\r
2814   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2815 \r
2816   y = boardRect.top + lineGap;\r
2817   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
2818 \r
2819   if(border) {\r
2820     SetTextAlign(hdc, TA_RIGHT|TA_TOP);\r
2821     x += border - lineGap - 4; y += squareSize - 6;\r
2822   } else\r
2823   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2824   for (i = 0; i < BOARD_HEIGHT; i++) {\r
2825     str[0] = files[start + i];\r
2826     ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);\r
2827     y += squareSize + lineGap;\r
2828   }\r
2829 \r
2830   start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;\r
2831 \r
2832   if(border) {\r
2833     SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2834     x += -border + 4; y += border - squareSize + 6;\r
2835   } else\r
2836   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2837   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
2838     str[0] = ranks[start + i];\r
2839     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2840     x += squareSize + lineGap;\r
2841   }    \r
2842 \r
2843   SelectObject(hdc, oldBrush);\r
2844   SetBkMode(hdc, oldMode);\r
2845   SetTextAlign(hdc, oldAlign);\r
2846   SelectObject(hdc, oldFont);\r
2847 }\r
2848 \r
2849 VOID\r
2850 DrawGridOnDC(HDC hdc)\r
2851 {\r
2852   HPEN oldPen;\r
2853  \r
2854   if (lineGap != 0) {\r
2855     oldPen = SelectObject(hdc, gridPen);\r
2856     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
2857     SelectObject(hdc, oldPen);\r
2858   }\r
2859 }\r
2860 \r
2861 #define HIGHLIGHT_PEN 0\r
2862 #define PREMOVE_PEN   1\r
2863 \r
2864 VOID\r
2865 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2866 {\r
2867   int x1, y1;\r
2868   HPEN oldPen, hPen;\r
2869   if (lineGap == 0) return;\r
2870   if (flipView) {\r
2871     x1 = boardRect.left +\r
2872       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;\r
2873     y1 = boardRect.top +\r
2874       lineGap/2 + y * (squareSize + lineGap) + border;\r
2875   } else {\r
2876     x1 = boardRect.left +\r
2877       lineGap/2 + x * (squareSize + lineGap) + border;\r
2878     y1 = boardRect.top +\r
2879       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;\r
2880   }\r
2881   hPen = pen ? premovePen : highlightPen;\r
2882   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2883   MoveToEx(hdc, x1, y1, NULL);\r
2884   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2885   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2886   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2887   LineTo(hdc, x1, y1);\r
2888   SelectObject(hdc, oldPen);\r
2889 }\r
2890 \r
2891 VOID\r
2892 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
2893 {\r
2894   int i;\r
2895   for (i=0; i<2; i++) {\r
2896     if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
2897       DrawHighlightOnDC(hdc, TRUE,\r
2898                         h->sq[i].x, h->sq[i].y,\r
2899                         pen);\r
2900   }\r
2901 }\r
2902 \r
2903 /* Note: sqcolor is used only in monoMode */\r
2904 /* Note that this code is largely duplicated in woptions.c,\r
2905    function DrawSampleSquare, so that needs to be updated too */\r
2906 VOID\r
2907 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2908 {\r
2909   HBITMAP oldBitmap;\r
2910   HBRUSH oldBrush;\r
2911   int tmpSize;\r
2912 \r
2913   if (appData.blindfold) return;\r
2914 \r
2915   /* [AS] Use font-based pieces if needed */\r
2916   if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
2917     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
2918     CreatePiecesFromFont();\r
2919 \r
2920     if( fontBitmapSquareSize == squareSize ) {\r
2921         int index = TranslatePieceToFontPiece(piece);\r
2922 \r
2923         SelectObject( tmphdc, hPieceMask[ index ] );\r
2924 \r
2925       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2926         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
2927       else\r
2928         BitBlt( hdc,\r
2929             x, y,\r
2930             squareSize, squareSize,\r
2931             tmphdc,\r
2932             0, 0,\r
2933             SRCAND );\r
2934 \r
2935         SelectObject( tmphdc, hPieceFace[ index ] );\r
2936 \r
2937       if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
2938         StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
2939       else\r
2940         BitBlt( hdc,\r
2941             x, y,\r
2942             squareSize, squareSize,\r
2943             tmphdc,\r
2944             0, 0,\r
2945             SRCPAINT );\r
2946 \r
2947         return;\r
2948     }\r
2949   }\r
2950 \r
2951   if (appData.monoMode) {\r
2952     SelectObject(tmphdc, PieceBitmap(piece, \r
2953       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2954     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2955            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2956   } else {\r
2957     HBRUSH xBrush = whitePieceBrush;\r
2958     tmpSize = squareSize;\r
2959     if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);\r
2960     if(minorSize &&\r
2961         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
2962          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
2963       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
2964       /* Bitmaps of smaller size are substituted, but we have to align them */\r
2965       x += (squareSize - minorSize)>>1;\r
2966       y += squareSize - minorSize - 2;\r
2967       tmpSize = minorSize;\r
2968     }\r
2969     if (color || appData.allWhite ) {\r
2970       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2971       if( color )\r
2972               oldBrush = SelectObject(hdc, xBrush);\r
2973       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
2974       if(appData.upsideDown && color==flipView)\r
2975         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2976       else\r
2977         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2978       /* Use black for outline of white pieces */\r
2979       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2980       if(appData.upsideDown && color==flipView)\r
2981         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2982       else\r
2983         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2984     } else if(appData.pieceDirectory[0]) {\r
2985       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2986       oldBrush = SelectObject(hdc, xBrush);\r
2987       if(appData.upsideDown && color==flipView)\r
2988         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
2989       else\r
2990         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
2991       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2992       if(appData.upsideDown && color==flipView)\r
2993         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
2994       else\r
2995         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
2996     } else {\r
2997       /* Use square color for details of black pieces */\r
2998       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2999       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3000       if(appData.upsideDown && !flipView)\r
3001         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3002       else\r
3003         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3004     }\r
3005     SelectObject(hdc, oldBrush);\r
3006     SelectObject(tmphdc, oldBitmap);\r
3007   }\r
3008 }\r
3009 \r
3010 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3011 int GetBackTextureMode( int algo )\r
3012 {\r
3013     int result = BACK_TEXTURE_MODE_DISABLED;\r
3014 \r
3015     switch( algo ) \r
3016     {\r
3017         case BACK_TEXTURE_MODE_PLAIN:\r
3018             result = 1; /* Always use identity map */\r
3019             break;\r
3020         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3021             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3022             break;\r
3023     }\r
3024 \r
3025     return result;\r
3026 }\r
3027 \r
3028 /* \r
3029     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3030     to handle redraws cleanly (as random numbers would always be different).\r
3031 */\r
3032 VOID RebuildTextureSquareInfo()\r
3033 {\r
3034     BITMAP bi;\r
3035     int lite_w = 0;\r
3036     int lite_h = 0;\r
3037     int dark_w = 0;\r
3038     int dark_h = 0;\r
3039     int row;\r
3040     int col;\r
3041 \r
3042     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3043 \r
3044     if( liteBackTexture != NULL ) {\r
3045         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3046             lite_w = bi.bmWidth;\r
3047             lite_h = bi.bmHeight;\r
3048         }\r
3049     }\r
3050 \r
3051     if( darkBackTexture != NULL ) {\r
3052         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3053             dark_w = bi.bmWidth;\r
3054             dark_h = bi.bmHeight;\r
3055         }\r
3056     }\r
3057 \r
3058     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3059         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3060             if( (col + row) & 1 ) {\r
3061                 /* Lite square */\r
3062                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3063                   if( lite_w >= squareSize*BOARD_WIDTH )\r
3064                     backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
3065                   else\r
3066                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3067                   if( lite_h >= squareSize*BOARD_HEIGHT )\r
3068                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
3069                   else\r
3070                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3071                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3072                 }\r
3073             }\r
3074             else {\r
3075                 /* Dark square */\r
3076                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3077                   if( dark_w >= squareSize*BOARD_WIDTH )\r
3078                     backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
3079                   else\r
3080                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3081                   if( dark_h >= squareSize*BOARD_HEIGHT )\r
3082                     backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
3083                   else\r
3084                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3085                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3086                 }\r
3087             }\r
3088         }\r
3089     }\r
3090 }\r
3091 \r
3092 /* [AS] Arrow highlighting support */\r
3093 \r
3094 static double A_WIDTH = 5; /* Width of arrow body */\r
3095 \r
3096 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3097 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3098 \r
3099 static double Sqr( double x )\r
3100 {\r
3101     return x*x;\r
3102 }\r
3103 \r
3104 static int Round( double x )\r
3105 {\r
3106     return (int) (x + 0.5);\r
3107 }\r
3108 \r
3109 /* Draw an arrow between two points using current settings */\r
3110 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3111 {\r
3112     POINT arrow[7];\r
3113     double dx, dy, j, k, x, y;\r
3114 \r
3115     if( d_x == s_x ) {\r
3116         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3117 \r
3118         arrow[0].x = s_x + A_WIDTH + 0.5;\r
3119         arrow[0].y = s_y;\r
3120 \r
3121         arrow[1].x = s_x + A_WIDTH + 0.5;\r
3122         arrow[1].y = d_y - h;\r
3123 \r
3124         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3125         arrow[2].y = d_y - h;\r
3126 \r
3127         arrow[3].x = d_x;\r
3128         arrow[3].y = d_y;\r
3129 \r
3130         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3131         arrow[5].y = d_y - h;\r
3132 \r
3133         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3134         arrow[4].y = d_y - h;\r
3135 \r
3136         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;\r
3137         arrow[6].y = s_y;\r
3138     }\r
3139     else if( d_y == s_y ) {\r
3140         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3141 \r
3142         arrow[0].x = s_x;\r
3143         arrow[0].y = s_y + A_WIDTH + 0.5;\r
3144 \r
3145         arrow[1].x = d_x - w;\r
3146         arrow[1].y = s_y + A_WIDTH + 0.5;\r
3147 \r
3148         arrow[2].x = d_x - w;\r
3149         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3150 \r
3151         arrow[3].x = d_x;\r
3152         arrow[3].y = d_y;\r
3153 \r
3154         arrow[5].x = d_x - w;\r
3155         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3156 \r
3157         arrow[4].x = d_x - w;\r
3158         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;\r
3159 \r
3160         arrow[6].x = s_x;\r
3161         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;\r
3162     }\r
3163     else {\r
3164         /* [AS] Needed a lot of paper for this! :-) */\r
3165         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3166         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3167   \r
3168         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3169 \r
3170         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3171 \r
3172         x = s_x;\r
3173         y = s_y;\r
3174 \r
3175         arrow[0].x = Round(x - j);\r
3176         arrow[0].y = Round(y + j*dx);\r
3177 \r
3178         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice\r
3179         arrow[1].y = Round(arrow[0].y - 2*j*dx);\r
3180 \r
3181         if( d_x > s_x ) {\r
3182             x = (double) d_x - k;\r
3183             y = (double) d_y - k*dy;\r
3184         }\r
3185         else {\r
3186             x = (double) d_x + k;\r
3187             y = (double) d_y + k*dy;\r
3188         }\r
3189 \r
3190         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends\r
3191 \r
3192         arrow[6].x = Round(x - j);\r
3193         arrow[6].y = Round(y + j*dx);\r
3194 \r
3195         arrow[2].x = Round(arrow[6].x + 2*j);\r
3196         arrow[2].y = Round(arrow[6].y - 2*j*dx);\r
3197 \r
3198         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));\r
3199         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);\r
3200 \r
3201         arrow[4].x = d_x;\r
3202         arrow[4].y = d_y;\r
3203 \r
3204         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));\r
3205         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);\r
3206     }\r
3207 \r
3208     Polygon( hdc, arrow, 7 );\r
3209 }\r
3210 \r
3211 /* [AS] Draw an arrow between two squares */\r
3212 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3213 {\r
3214     int s_x, s_y, d_x, d_y;\r
3215     HPEN hpen;\r
3216     HPEN holdpen;\r
3217     HBRUSH hbrush;\r
3218     HBRUSH holdbrush;\r
3219     LOGBRUSH stLB;\r
3220 \r
3221     if( s_col == d_col && s_row == d_row ) {\r
3222         return;\r
3223     }\r
3224 \r
3225     /* Get source and destination points */\r
3226     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3227     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3228 \r
3229     if( d_y > s_y ) {\r
3230         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!\r
3231     }\r
3232     else if( d_y < s_y ) {\r
3233         d_y += squareSize / 2 + squareSize / 4;\r
3234     }\r
3235     else {\r
3236         d_y += squareSize / 2;\r
3237     }\r
3238 \r
3239     if( d_x > s_x ) {\r
3240         d_x += squareSize / 2 - squareSize / 4;\r
3241     }\r
3242     else if( d_x < s_x ) {\r
3243         d_x += squareSize / 2 + squareSize / 4;\r
3244     }\r
3245     else {\r
3246         d_x += squareSize / 2;\r
3247     }\r
3248 \r
3249     s_x += squareSize / 2;\r
3250     s_y += squareSize / 2;\r
3251 \r
3252     /* Adjust width */\r
3253     A_WIDTH = squareSize / 14.; //[HGM] make float\r
3254 \r
3255     /* Draw */\r
3256     stLB.lbStyle = BS_SOLID;\r
3257     stLB.lbColor = appData.highlightArrowColor;\r
3258     stLB.lbHatch = 0;\r
3259 \r
3260     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3261     holdpen = SelectObject( hdc, hpen );\r
3262     hbrush = CreateBrushIndirect( &stLB );\r
3263     holdbrush = SelectObject( hdc, hbrush );\r
3264 \r
3265     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3266 \r
3267     SelectObject( hdc, holdpen );\r
3268     SelectObject( hdc, holdbrush );\r
3269     DeleteObject( hpen );\r
3270     DeleteObject( hbrush );\r
3271 }\r
3272 \r
3273 BOOL HasHighlightInfo()\r
3274 {\r
3275     BOOL result = FALSE;\r
3276 \r
3277     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3278         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3279     {\r
3280         result = TRUE;\r
3281     }\r
3282 \r
3283     return result;\r
3284 \r
3285 \r
3286 \r
3287 }\r
3288 \r
3289 BOOL IsDrawArrowEnabled()\r
3290 {\r
3291     BOOL result = FALSE;\r
3292 \r
3293     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3294         result = TRUE;\r
3295     }\r
3296 \r
3297     return result;\r
3298 }\r
3299 \r
3300 VOID DrawArrowHighlight( HDC hdc )\r
3301 {\r
3302     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3303         DrawArrowBetweenSquares( hdc,\r
3304             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3305             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3306     }\r
3307 }\r
3308 \r
3309 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3310 {\r
3311     HRGN result = NULL;\r
3312 \r
3313     if( HasHighlightInfo() ) {\r
3314         int x1, y1, x2, y2;\r
3315         int sx, sy, dx, dy;\r
3316 \r
3317         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3318         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3319 \r
3320         sx = MIN( x1, x2 );\r
3321         sy = MIN( y1, y2 );\r
3322         dx = MAX( x1, x2 ) + squareSize;\r
3323         dy = MAX( y1, y2 ) + squareSize;\r
3324 \r
3325         result = CreateRectRgn( sx, sy, dx, dy );\r
3326     }\r
3327 \r
3328     return result;\r
3329 }\r
3330 \r
3331 /*\r
3332     Warning: this function modifies the behavior of several other functions. \r
3333     \r
3334     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3335     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3336     repaint is scattered all over the place, which is not good for features such as\r
3337     "arrow highlighting" that require a full repaint of the board.\r
3338 \r
3339     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3340     user interaction, when speed is not so important) but especially to avoid errors\r
3341     in the displayed graphics.\r
3342 \r
3343     In such patched places, I always try refer to this function so there is a single\r
3344     place to maintain knowledge.\r
3345     \r
3346     To restore the original behavior, just return FALSE unconditionally.\r
3347 */\r
3348 BOOL IsFullRepaintPreferrable()\r
3349 {\r
3350     BOOL result = FALSE;\r
3351 \r
3352     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3353         /* Arrow may appear on the board */\r
3354         result = TRUE;\r
3355     }\r
3356 \r
3357     return result;\r
3358 }\r
3359 \r
3360 /* \r
3361     This function is called by DrawPosition to know whether a full repaint must\r
3362     be forced or not.\r
3363 \r
3364     Only DrawPosition may directly call this function, which makes use of \r
3365     some state information. Other function should call DrawPosition specifying \r
3366     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3367 */\r
3368 BOOL DrawPositionNeedsFullRepaint()\r
3369 {\r
3370     BOOL result = FALSE;\r
3371 \r
3372     /* \r
3373         Probably a slightly better policy would be to trigger a full repaint\r
3374         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3375         but animation is fast enough that it's difficult to notice.\r
3376     */\r
3377     if( animInfo.piece == EmptySquare ) {\r
3378         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
3379             result = TRUE;\r
3380         }\r
3381     }\r
3382 \r
3383     return result;\r
3384 }\r
3385 \r
3386 static HBITMAP borderBitmap;\r
3387 \r
3388 VOID\r
3389 DrawBackgroundOnDC(HDC hdc)\r
3390 {\r
3391   \r
3392   BITMAP bi;\r
3393   HDC tmphdc;\r
3394   HBITMAP hbm;\r
3395   static char oldBorder[MSG_SIZ];\r
3396   int w = 600, h = 600, mode;\r
3397 \r
3398   if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid\r
3399     strncpy(oldBorder, appData.border, MSG_SIZ-1);\r
3400     borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); \r
3401   }\r
3402   if(borderBitmap == NULL) { // loading failed, use white\r
3403     FillRect( hdc, &boardRect, whitePieceBrush );\r
3404     return;\r
3405   }\r
3406   tmphdc = CreateCompatibleDC(hdc);\r
3407   hbm = SelectObject(tmphdc, borderBitmap);\r
3408   if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {\r
3409             w = bi.bmWidth;\r
3410             h = bi.bmHeight;\r
3411   }\r
3412   mode = SetStretchBltMode(hdc, COLORONCOLOR);\r
3413   StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left, \r
3414                   boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3415   SetStretchBltMode(hdc, mode);\r
3416   SelectObject(tmphdc, hbm);\r
3417   DeleteDC(tmphdc);\r
3418 }\r
3419 \r
3420 VOID\r
3421 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3422 {\r
3423   int row, column, x, y, square_color, piece_color;\r
3424   ChessSquare piece;\r
3425   HBRUSH oldBrush;\r
3426   HDC texture_hdc = NULL;\r
3427 \r
3428   /* [AS] Initialize background textures if needed */\r
3429   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3430       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
3431       if( backTextureSquareSize != squareSize \r
3432        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
3433           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
3434           backTextureSquareSize = squareSize;\r
3435           RebuildTextureSquareInfo();\r
3436       }\r
3437 \r
3438       texture_hdc = CreateCompatibleDC( hdc );\r
3439   }\r
3440 \r
3441   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3442     for (column = 0; column < BOARD_WIDTH; column++) {\r
3443   \r
3444       SquareToPos(row, column, &x, &y);\r
3445 \r
3446       piece = board[row][column];\r
3447 \r
3448       square_color = ((column + row) % 2) == 1;\r
3449       if( gameInfo.variant == VariantXiangqi ) {\r
3450           square_color = !InPalace(row, column);\r
3451           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3452           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3453       }\r
3454       piece_color = (int) piece < (int) BlackPawn;\r
3455 \r
3456 \r
3457       /* [HGM] holdings file: light square or black */\r
3458       if(column == BOARD_LEFT-2) {\r
3459             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3460                 square_color = 1;\r
3461             else {\r
3462                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3463                 continue;\r
3464             }\r
3465       } else\r
3466       if(column == BOARD_RGHT + 1 ) {\r
3467             if( row < gameInfo.holdingsSize )\r
3468                 square_color = 1;\r
3469             else {\r
3470                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3471                 continue;\r
3472             }\r
3473       }\r
3474       if(column == BOARD_LEFT-1 ) /* left align */\r
3475             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
3476       else if( column == BOARD_RGHT) /* right align */\r
3477             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
3478       else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);\r
3479       else\r
3480       if (appData.monoMode) {\r
3481         if (piece == EmptySquare) {\r
3482           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3483                  square_color ? WHITENESS : BLACKNESS);\r
3484         } else {\r
3485           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3486         }\r
3487       } \r
3488       else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {\r
3489           /* [AS] Draw the square using a texture bitmap */\r
3490           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3491           int r = row, c = column; // [HGM] do not flip board in flipView\r
3492           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
3493 \r
3494           DrawTile( x, y, \r
3495               squareSize, squareSize, \r
3496               hdc, \r
3497               texture_hdc,\r
3498               backTextureSquareInfo[r][c].mode,\r
3499               backTextureSquareInfo[r][c].x,\r
3500               backTextureSquareInfo[r][c].y );\r
3501 \r
3502           SelectObject( texture_hdc, hbm );\r
3503 \r
3504           if (piece != EmptySquare) {\r
3505               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3506           }\r
3507       }\r
3508       else {\r
3509         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3510 \r
3511         oldBrush = SelectObject(hdc, brush );\r
3512         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3513         SelectObject(hdc, oldBrush);\r
3514         if (piece != EmptySquare)\r
3515           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3516       }\r
3517     }\r
3518   }\r
3519 \r
3520   if( texture_hdc != NULL ) {\r
3521     DeleteDC( texture_hdc );\r
3522   }\r
3523 }\r
3524 \r
3525 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
3526 void fputDW(FILE *f, int x)\r
3527 {\r
3528         fputc(x     & 255, f);\r
3529         fputc(x>>8  & 255, f);\r
3530         fputc(x>>16 & 255, f);\r
3531         fputc(x>>24 & 255, f);\r
3532 }\r
3533 \r
3534 #define MAX_CLIPS 200   /* more than enough */\r
3535 \r
3536 VOID\r
3537 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
3538 {\r
3539 //  HBITMAP bufferBitmap;\r
3540   BITMAP bi;\r
3541 //  RECT Rect;\r
3542   HDC tmphdc;\r
3543   HBITMAP hbm;\r
3544   int w = 100, h = 50;\r
3545 \r
3546   if(logo == NULL) {\r
3547     if(!logoHeight) return;\r
3548     FillRect( hdc, &logoRect, whitePieceBrush );\r
3549   }\r
3550 //  GetClientRect(hwndMain, &Rect);\r
3551 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3552 //                                      Rect.bottom-Rect.top+1);\r
3553   tmphdc = CreateCompatibleDC(hdc);\r
3554   hbm = SelectObject(tmphdc, logo);\r
3555   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
3556             w = bi.bmWidth;\r
3557             h = bi.bmHeight;\r
3558   }\r
3559   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
3560                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
3561   SelectObject(tmphdc, hbm);\r
3562   DeleteDC(tmphdc);\r
3563 }\r
3564 \r
3565 VOID\r
3566 DisplayLogos()\r
3567 {\r
3568   if(logoHeight) {\r
3569         HDC hdc = GetDC(hwndMain);\r
3570         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
3571         if(appData.autoLogo) {\r
3572           \r
3573           switch(gameMode) { // pick logos based on game mode\r
3574             case IcsObserving:\r
3575                 whiteLogo = second.programLogo; // ICS logo\r
3576                 blackLogo = second.programLogo;\r
3577             default:\r
3578                 break;\r
3579             case IcsPlayingWhite:\r
3580                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
3581                 blackLogo = second.programLogo; // ICS logo\r
3582                 break;\r
3583             case IcsPlayingBlack:\r
3584                 whiteLogo = second.programLogo; // ICS logo\r
3585                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
3586                 break;\r
3587             case TwoMachinesPlay:\r
3588                 if(first.twoMachinesColor[0] == 'b') {\r
3589                     whiteLogo = second.programLogo;\r
3590                     blackLogo = first.programLogo;\r
3591                 }\r
3592                 break;\r
3593             case MachinePlaysWhite:\r
3594                 blackLogo = userLogo;\r
3595                 break;\r
3596             case MachinePlaysBlack:\r
3597                 whiteLogo = userLogo;\r
3598                 blackLogo = first.programLogo;\r
3599           }\r
3600         }\r
3601         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
3602         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
3603         ReleaseDC(hwndMain, hdc);\r
3604   }\r
3605 }\r
3606 \r
3607 void\r
3608 UpdateLogos(int display)\r
3609 { // called after loading new engine(s), in tourney or from menu\r
3610   LoadLogo(&first, 0, FALSE);\r
3611   LoadLogo(&second, 1, appData.icsActive);\r
3612   InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos\r
3613   if(display) DisplayLogos();\r
3614 }\r
3615 \r
3616 static HDC hdcSeek;\r
3617 \r
3618 // [HGM] seekgraph\r
3619 void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
3620 {\r
3621     POINT stPt;\r
3622     HPEN hp = SelectObject( hdcSeek, gridPen );\r
3623     MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
3624     LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
3625     SelectObject( hdcSeek, hp );\r
3626 }\r
3627 \r
3628 // front-end wrapper for drawing functions to do rectangles\r
3629 void DrawSeekBackground( int left, int top, int right, int bottom )\r
3630 {\r
3631     HPEN hp;\r
3632     RECT rc;\r
3633 \r
3634     if (hdcSeek == NULL) {\r
3635     hdcSeek = GetDC(hwndMain);\r
3636       if (!appData.monoMode) {\r
3637         SelectPalette(hdcSeek, hPal, FALSE);\r
3638         RealizePalette(hdcSeek);\r
3639       }\r
3640     }\r
3641     hp = SelectObject( hdcSeek, gridPen );\r
3642     rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
3643     rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
3644     FillRect( hdcSeek, &rc, lightSquareBrush );\r
3645     SelectObject( hdcSeek, hp );\r
3646 }\r
3647 \r
3648 // front-end wrapper for putting text in graph\r
3649 void DrawSeekText(char *buf, int x, int y)\r
3650 {\r
3651         SIZE stSize;\r
3652         SetBkMode( hdcSeek, TRANSPARENT );\r
3653         GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
3654         TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
3655 }\r
3656 \r
3657 void DrawSeekDot(int x, int y, int color)\r
3658 {\r
3659         int square = color & 0x80;\r
3660         HBRUSH oldBrush = SelectObject(hdcSeek, \r
3661                         color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);\r
3662         color &= 0x7F;\r
3663         if(square)\r
3664             Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
3665                                boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
3666         else\r
3667             Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
3668                              boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
3669             SelectObject(hdcSeek, oldBrush);\r
3670 }\r
3671 \r
3672 void DrawSeekOpen()\r
3673 {\r
3674 }\r
3675 \r
3676 void DrawSeekClose()\r
3677 {\r
3678 }\r
3679 \r
3680 VOID\r
3681 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3682 {\r
3683   static Board lastReq[2], lastDrawn[2];\r
3684   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3685   static int lastDrawnFlipView = 0;\r
3686   static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
3687   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3688   HDC tmphdc;\r
3689   HDC hdcmem;\r
3690   HBITMAP bufferBitmap;\r
3691   HBITMAP oldBitmap;\r
3692   RECT Rect;\r
3693   HRGN clips[MAX_CLIPS];\r
3694   ChessSquare dragged_piece = EmptySquare;\r
3695   int nr = twoBoards*partnerUp;\r
3696 \r
3697   /* I'm undecided on this - this function figures out whether a full\r
3698    * repaint is necessary on its own, so there's no real reason to have the\r
3699    * caller tell it that.  I think this can safely be set to FALSE - but\r
3700    * if we trust the callers not to request full repaints unnessesarily, then\r
3701    * we could skip some clipping work.  In other words, only request a full\r
3702    * redraw when the majority of pieces have changed positions (ie. flip, \r
3703    * gamestart and similar)  --Hawk\r
3704    */\r
3705   Boolean fullrepaint = repaint;\r
3706 \r
3707   if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
3708 \r
3709   if( DrawPositionNeedsFullRepaint() ) {\r
3710       fullrepaint = TRUE;\r
3711   }\r
3712 \r
3713   if (board == NULL) {\r
3714     if (!lastReqValid[nr]) {\r
3715       return;\r
3716     }\r
3717     board = lastReq[nr];\r
3718   } else {\r
3719     CopyBoard(lastReq[nr], board);\r
3720     lastReqValid[nr] = 1;\r
3721   }\r
3722 \r
3723   if (doingSizing) {\r
3724     return;\r
3725   }\r
3726 \r
3727   if (IsIconic(hwndMain)) {\r
3728     return;\r
3729   }\r
3730 \r
3731   if (hdc == NULL) {\r
3732     hdc = GetDC(hwndMain);\r
3733     if (!appData.monoMode) {\r
3734       SelectPalette(hdc, hPal, FALSE);\r
3735       RealizePalette(hdc);\r
3736     }\r
3737     releaseDC = TRUE;\r
3738   } else {\r
3739     releaseDC = FALSE;\r
3740   }\r
3741 \r
3742   /* Create some work-DCs */\r
3743   hdcmem = CreateCompatibleDC(hdc);\r
3744   tmphdc = CreateCompatibleDC(hdc);\r
3745 \r
3746   /* If dragging is in progress, we temporarely remove the piece */\r
3747   /* [HGM] or temporarily decrease count if stacked              */\r
3748   /*       !! Moved to before board compare !!                   */\r
3749   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3750     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3751     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3752             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3753         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3754     } else \r
3755     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3756             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3757         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3758     } else \r
3759         board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;\r
3760   }\r
3761 \r
3762   /* Figure out which squares need updating by comparing the \r
3763    * newest board with the last drawn board and checking if\r
3764    * flipping has changed.\r
3765    */\r
3766   if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
3767     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3768       for (column = 0; column < BOARD_WIDTH; column++) {\r
3769         if (lastDrawn[nr][row][column] != board[row][column]) {\r
3770           SquareToPos(row, column, &x, &y);\r
3771           clips[num_clips++] =\r
3772             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3773         }\r
3774       }\r
3775     }\r
3776    if(nr == 0) { // [HGM] dual: no highlights on second board\r
3777     for (i=0; i<2; i++) {\r
3778       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3779           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3780         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3781             lastDrawnHighlight.sq[i].y >= 0) {\r
3782           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3783                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3784           clips[num_clips++] =\r
3785             CreateRectRgn(x - lineGap, y - lineGap, \r
3786                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3787         }\r
3788         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3789           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3790           clips[num_clips++] =\r
3791             CreateRectRgn(x - lineGap, y - lineGap, \r
3792                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3793         }\r
3794       }\r
3795     }\r
3796     for (i=0; i<2; i++) {\r
3797       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3798           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3799         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3800             lastDrawnPremove.sq[i].y >= 0) {\r
3801           SquareToPos(lastDrawnPremove.sq[i].y,\r
3802                       lastDrawnPremove.sq[i].x, &x, &y);\r
3803           clips[num_clips++] =\r
3804             CreateRectRgn(x - lineGap, y - lineGap, \r
3805                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3806         }\r
3807         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3808             premoveHighlightInfo.sq[i].y >= 0) {\r
3809           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3810                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3811           clips[num_clips++] =\r
3812             CreateRectRgn(x - lineGap, y - lineGap, \r
3813                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3814         }\r
3815       }\r
3816     }\r
3817    } else { // nr == 1\r
3818         partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
3819         partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
3820         partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
3821         partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
3822       for (i=0; i<2; i++) {\r
3823         if (partnerHighlightInfo.sq[i].x >= 0 &&\r
3824             partnerHighlightInfo.sq[i].y >= 0) {\r
3825           SquareToPos(partnerHighlightInfo.sq[i].y,\r
3826                       partnerHighlightInfo.sq[i].x, &x, &y);\r
3827           clips[num_clips++] =\r
3828             CreateRectRgn(x - lineGap, y - lineGap, \r
3829                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3830         }\r
3831         if (oldPartnerHighlight.sq[i].x >= 0 && \r
3832             oldPartnerHighlight.sq[i].y >= 0) {\r
3833           SquareToPos(oldPartnerHighlight.sq[i].y, \r
3834                       oldPartnerHighlight.sq[i].x, &x, &y);\r
3835           clips[num_clips++] =\r
3836             CreateRectRgn(x - lineGap, y - lineGap, \r
3837                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3838         }\r
3839       }\r
3840    }\r
3841   } else {\r
3842     fullrepaint = TRUE;\r
3843   }\r
3844 \r
3845   /* Create a buffer bitmap - this is the actual bitmap\r
3846    * being written to.  When all the work is done, we can\r
3847    * copy it to the real DC (the screen).  This avoids\r
3848    * the problems with flickering.\r
3849    */\r
3850   GetClientRect(hwndMain, &Rect);\r
3851   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3852                                         Rect.bottom-Rect.top+1);\r
3853   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3854   if (!appData.monoMode) {\r
3855     SelectPalette(hdcmem, hPal, FALSE);\r
3856   }\r
3857 \r
3858   /* Create clips for dragging */\r
3859   if (!fullrepaint) {\r
3860     if (dragInfo.from.x >= 0) {\r
3861       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3862       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3863     }\r
3864     if (dragInfo.start.x >= 0) {\r
3865       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3866       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3867     }\r
3868     if (dragInfo.pos.x >= 0) {\r
3869       x = dragInfo.pos.x - squareSize / 2;\r
3870       y = dragInfo.pos.y - squareSize / 2;\r
3871       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3872     }\r
3873     if (dragInfo.lastpos.x >= 0) {\r
3874       x = dragInfo.lastpos.x - squareSize / 2;\r
3875       y = dragInfo.lastpos.y - squareSize / 2;\r
3876       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3877     }\r
3878   }\r
3879 \r
3880   /* Are we animating a move?  \r
3881    * If so, \r
3882    *   - remove the piece from the board (temporarely)\r
3883    *   - calculate the clipping region\r
3884    */\r
3885   if (!fullrepaint) {\r
3886     if (animInfo.piece != EmptySquare) {\r
3887       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3888       x = boardRect.left + animInfo.lastpos.x;\r
3889       y = boardRect.top + animInfo.lastpos.y;\r
3890       x2 = boardRect.left + animInfo.pos.x;\r
3891       y2 = boardRect.top + animInfo.pos.y;\r
3892       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3893       /* Slight kludge.  The real problem is that after AnimateMove is\r
3894          done, the position on the screen does not match lastDrawn.\r
3895          This currently causes trouble only on e.p. captures in\r
3896          atomic, where the piece moves to an empty square and then\r
3897          explodes.  The old and new positions both had an empty square\r
3898          at the destination, but animation has drawn a piece there and\r
3899          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
3900       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3901     }\r
3902   }\r
3903 \r
3904   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3905   if (num_clips == 0)\r
3906     fullrepaint = TRUE;\r
3907 \r
3908   /* Set clipping on the memory DC */\r
3909   if (!fullrepaint) {\r
3910     SelectClipRgn(hdcmem, clips[0]);\r
3911     for (x = 1; x < num_clips; x++) {\r
3912       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3913         abort();  // this should never ever happen!\r
3914     }\r
3915   }\r
3916 \r
3917   /* Do all the drawing to the memory DC */\r
3918   if(explodeInfo.radius) { // [HGM] atomic\r
3919         HBRUSH oldBrush;\r
3920         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
3921         ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];\r
3922         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
3923         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
3924         x += squareSize/2;\r
3925         y += squareSize/2;\r
3926         if(!fullrepaint) {\r
3927           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
3928           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
3929         }\r
3930         DrawGridOnDC(hdcmem);\r
3931         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3932         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3933         DrawBoardOnDC(hdcmem, board, tmphdc);\r
3934         board[explodeInfo.fromY][explodeInfo.fromX] = piece;\r
3935         oldBrush = SelectObject(hdcmem, explodeBrush);\r
3936         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
3937         SelectObject(hdcmem, oldBrush);\r
3938   } else {\r
3939     if(border) DrawBackgroundOnDC(hdcmem);\r
3940     DrawGridOnDC(hdcmem);\r
3941     if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
3942         DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
3943         DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
3944     } else {\r
3945         DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
3946         oldPartnerHighlight = partnerHighlightInfo;\r
3947     }\r
3948     DrawBoardOnDC(hdcmem, board, tmphdc);\r
3949   }\r
3950   if(nr == 0) // [HGM] dual: markers only on left board\r
3951   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3952     for (column = 0; column < BOARD_WIDTH; column++) {\r
3953         if (marker[row][column]) { // marker changes only occur with full repaint!\r
3954             HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);\r
3955             SquareToPos(row, column, &x, &y);\r
3956             Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,\r
3957                           x + 3*squareSize/4, y + 3*squareSize/4);\r
3958             SelectObject(hdcmem, oldBrush);\r
3959         }\r
3960     }\r
3961   }\r
3962 \r
3963   if( appData.highlightMoveWithArrow ) {\r
3964     DrawArrowHighlight(hdcmem);\r
3965   }\r
3966 \r
3967   DrawCoordsOnDC(hdcmem);\r
3968 \r
3969   CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
3970                  /* to make sure lastDrawn contains what is actually drawn */\r
3971 \r
3972   /* Put the dragged piece back into place and draw it (out of place!) */\r
3973     if (dragged_piece != EmptySquare) {\r
3974     /* [HGM] or restack */\r
3975     if(dragInfo.from.x == BOARD_LEFT-2 )\r
3976                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
3977     else\r
3978     if(dragInfo.from.x == BOARD_RGHT+1 )\r
3979                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
3980     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3981     x = dragInfo.pos.x - squareSize / 2;\r
3982     y = dragInfo.pos.y - squareSize / 2;\r
3983     DrawPieceOnDC(hdcmem, dragInfo.piece,\r
3984                   ((int) dragInfo.piece < (int) BlackPawn), \r
3985                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3986   }   \r
3987   \r
3988   /* Put the animated piece back into place and draw it */\r
3989   if (animInfo.piece != EmptySquare) {\r
3990     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3991     x = boardRect.left + animInfo.pos.x;\r
3992     y = boardRect.top + animInfo.pos.y;\r
3993     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3994                   ((int) animInfo.piece < (int) BlackPawn),\r
3995                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3996   }\r
3997 \r
3998   /* Release the bufferBitmap by selecting in the old bitmap \r
3999    * and delete the memory DC\r
4000    */\r
4001   SelectObject(hdcmem, oldBitmap);\r
4002   DeleteDC(hdcmem);\r
4003 \r
4004   /* Set clipping on the target DC */\r
4005   if (!fullrepaint) {\r
4006     if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
4007         RECT rect;\r
4008         GetRgnBox(clips[x], &rect);\r
4009         DeleteObject(clips[x]);\r
4010         clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
4011                           rect.right + wpMain.width/2, rect.bottom);\r
4012     }\r
4013     SelectClipRgn(hdc, clips[0]);\r
4014     for (x = 1; x < num_clips; x++) {\r
4015       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4016         abort();   // this should never ever happen!\r
4017     } \r
4018   }\r
4019 \r
4020   /* Copy the new bitmap onto the screen in one go.\r
4021    * This way we avoid any flickering\r
4022    */\r
4023   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4024   BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
4025          boardRect.right - boardRect.left,\r
4026          boardRect.bottom - boardRect.top,\r
4027          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4028   if(saveDiagFlag) { \r
4029     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
4030     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4031 \r
4032     GetObject(bufferBitmap, sizeof(b), &b);\r
4033     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
4034         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4035         bih.biWidth = b.bmWidth;\r
4036         bih.biHeight = b.bmHeight;\r
4037         bih.biPlanes = 1;\r
4038         bih.biBitCount = b.bmBitsPixel;\r
4039         bih.biCompression = 0;\r
4040         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4041         bih.biXPelsPerMeter = 0;\r
4042         bih.biYPelsPerMeter = 0;\r
4043         bih.biClrUsed = 0;\r
4044         bih.biClrImportant = 0;\r
4045 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4046 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4047         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4048 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4049 \r
4050         wb = b.bmWidthBytes;\r
4051         // count colors\r
4052         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4053                 int k = ((int*) pData)[i];\r
4054                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4055                 if(j >= 16) break;\r
4056                 color[j] = k;\r
4057                 if(j >= nrColors) nrColors = j+1;\r
4058         }\r
4059         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4060                 INT p = 0;\r
4061                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4062                     for(w=0; w<(wb>>2); w+=2) {\r
4063                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4064                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4065                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4066                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4067                         pData[p++] = m | j<<4;\r
4068                     }\r
4069                     while(p&3) pData[p++] = 0;\r
4070                 }\r
4071                 fac = 3;\r
4072                 wb = ((wb+31)>>5)<<2;\r
4073         }\r
4074         // write BITMAPFILEHEADER\r
4075         fprintf(diagFile, "BM");\r
4076         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4077         fputDW(diagFile, 0);\r
4078         fputDW(diagFile, 0x36 + (fac?64:0));\r
4079         // write BITMAPINFOHEADER\r
4080         fputDW(diagFile, 40);\r
4081         fputDW(diagFile, b.bmWidth);\r
4082         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4083         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4084         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4085         fputDW(diagFile, 0);\r
4086         fputDW(diagFile, 0);\r
4087         fputDW(diagFile, 0);\r
4088         fputDW(diagFile, 0);\r
4089         fputDW(diagFile, 0);\r
4090         fputDW(diagFile, 0);\r
4091         // write color table\r
4092         if(fac)\r
4093         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4094         // write bitmap data\r
4095         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4096                 fputc(pData[i], diagFile);\r
4097         free(pData);\r
4098      }\r
4099   }\r
4100 \r
4101   SelectObject(tmphdc, oldBitmap);\r
4102 \r
4103   /* Massive cleanup */\r
4104   for (x = 0; x < num_clips; x++)\r
4105     DeleteObject(clips[x]);\r
4106 \r
4107   DeleteDC(tmphdc);\r
4108   DeleteObject(bufferBitmap);\r
4109 \r
4110   if (releaseDC) \r
4111     ReleaseDC(hwndMain, hdc);\r
4112   \r
4113   if (lastDrawnFlipView != flipView && nr == 0) {\r
4114     if (flipView)\r
4115       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4116     else\r
4117       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4118   }\r
4119 \r
4120 /*  CopyBoard(lastDrawn, board);*/\r
4121   lastDrawnHighlight = highlightInfo;\r
4122   lastDrawnPremove   = premoveHighlightInfo;\r
4123   lastDrawnFlipView = flipView;\r
4124   lastDrawnValid[nr] = 1;\r
4125 }\r
4126 \r
4127 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4128 int\r
4129 SaveDiagram(f)\r
4130      FILE *f;\r
4131 {\r
4132     saveDiagFlag = 1; diagFile = f;\r
4133     HDCDrawPosition(NULL, TRUE, NULL);\r
4134     saveDiagFlag = 0;\r
4135 \r
4136     fclose(f);\r
4137     return TRUE;\r
4138 }\r
4139 \r
4140 \r
4141 /*---------------------------------------------------------------------------*\\r
4142 | CLIENT PAINT PROCEDURE\r
4143 |   This is the main event-handler for the WM_PAINT message.\r
4144 |\r
4145 \*---------------------------------------------------------------------------*/\r
4146 VOID\r
4147 PaintProc(HWND hwnd)\r
4148 {\r
4149   HDC         hdc;\r
4150   PAINTSTRUCT ps;\r
4151   HFONT       oldFont;\r
4152 \r
4153   if((hdc = BeginPaint(hwnd, &ps))) {\r
4154     if (IsIconic(hwnd)) {\r
4155       DrawIcon(hdc, 2, 2, iconCurrent);\r
4156     } else {\r
4157       if (!appData.monoMode) {\r
4158         SelectPalette(hdc, hPal, FALSE);\r
4159         RealizePalette(hdc);\r
4160       }\r
4161       HDCDrawPosition(hdc, 1, NULL);\r
4162       if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
4163         flipView = !flipView; partnerUp = !partnerUp;\r
4164         HDCDrawPosition(hdc, 1, NULL);\r
4165         flipView = !flipView; partnerUp = !partnerUp;\r
4166       }\r
4167       oldFont =\r
4168         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4169       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4170                  ETO_CLIPPED|ETO_OPAQUE,\r
4171                  &messageRect, messageText, strlen(messageText), NULL);\r
4172       SelectObject(hdc, oldFont);\r
4173       DisplayBothClocks();\r
4174       DisplayLogos();\r
4175     }\r
4176     EndPaint(hwnd,&ps);\r
4177   }\r
4178 \r
4179   return;\r
4180 }\r
4181 \r
4182 \r
4183 /*\r
4184  * If the user selects on a border boundary, return -1; if off the board,\r
4185  *   return -2.  Otherwise map the event coordinate to the square.\r
4186  * The offset boardRect.left or boardRect.top must already have been\r
4187  *   subtracted from x.\r
4188  */\r
4189 int EventToSquare(x, limit)\r
4190      int x, limit;\r
4191 {\r
4192   if (x <= border)\r
4193     return -2;\r
4194   if (x < lineGap + border)\r
4195     return -1;\r
4196   x -= lineGap + border;\r
4197   if ((x % (squareSize + lineGap)) >= squareSize)\r
4198     return -1;\r
4199   x /= (squareSize + lineGap);\r
4200     if (x >= limit)\r
4201     return -2;\r
4202   return x;\r
4203 }\r
4204 \r
4205 typedef struct {\r
4206   char piece;\r
4207   int command;\r
4208   char* name;\r
4209 } DropEnable;\r
4210 \r
4211 DropEnable dropEnables[] = {\r
4212   { 'P', DP_Pawn, N_("Pawn") },\r
4213   { 'N', DP_Knight, N_("Knight") },\r
4214   { 'B', DP_Bishop, N_("Bishop") },\r
4215   { 'R', DP_Rook, N_("Rook") },\r
4216   { 'Q', DP_Queen, N_("Queen") },\r
4217 };\r
4218 \r
4219 VOID\r
4220 SetupDropMenu(HMENU hmenu)\r
4221 {\r
4222   int i, count, enable;\r
4223   char *p;\r
4224   extern char white_holding[], black_holding[];\r
4225   char item[MSG_SIZ];\r
4226 \r
4227   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4228     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4229                dropEnables[i].piece);\r
4230     count = 0;\r
4231     while (p && *p++ == dropEnables[i].piece) count++;\r
4232       snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
4233     enable = count > 0 || !appData.testLegality\r
4234       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4235                       && !appData.icsActive);\r
4236     ModifyMenu(hmenu, dropEnables[i].command,\r
4237                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4238                dropEnables[i].command, item);\r
4239   }\r
4240 }\r
4241 \r
4242 void DragPieceBegin(int x, int y, Boolean instantly)\r
4243 {\r
4244       dragInfo.lastpos.x = boardRect.left + x;\r
4245       dragInfo.lastpos.y = boardRect.top + y;\r
4246       if(instantly) dragInfo.pos = dragInfo.lastpos;\r
4247       dragInfo.from.x = fromX;\r
4248       dragInfo.from.y = fromY;\r
4249       dragInfo.piece = boards[currentMove][fromY][fromX];\r
4250       dragInfo.start = dragInfo.from;\r
4251       SetCapture(hwndMain);\r
4252 }\r
4253 \r
4254 void DragPieceEnd(int x, int y)\r
4255 {\r
4256     ReleaseCapture();\r
4257     dragInfo.start.x = dragInfo.start.y = -1;\r
4258     dragInfo.from = dragInfo.start;\r
4259     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4260 }\r
4261 \r
4262 void ChangeDragPiece(ChessSquare piece)\r
4263 {\r
4264     dragInfo.piece = piece;\r
4265 }\r
4266 \r
4267 /* Event handler for mouse messages */\r
4268 VOID\r
4269 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4270 {\r
4271   int x, y, menuNr;\r
4272   POINT pt;\r
4273   static int recursive = 0;\r
4274   HMENU hmenu;\r
4275   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4276 \r
4277   if (recursive) {\r
4278     if (message == WM_MBUTTONUP) {\r
4279       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4280          to the middle button: we simulate pressing the left button too!\r
4281          */\r
4282       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4283       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4284     }\r
4285     return;\r
4286   }\r
4287   recursive++;\r
4288   \r
4289   pt.x = LOWORD(lParam);\r
4290   pt.y = HIWORD(lParam);\r
4291   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4292   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4293   if (!flipView && y >= 0) {\r
4294     y = BOARD_HEIGHT - 1 - y;\r
4295   }\r
4296   if (flipView && x >= 0) {\r
4297     x = BOARD_WIDTH - 1 - x;\r
4298   }\r
4299 \r
4300   shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
4301   controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status\r
4302 \r
4303   switch (message) {\r
4304   case WM_LBUTTONDOWN:\r
4305       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4306         ClockClick(flipClock); break;\r
4307       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4308         ClockClick(!flipClock); break;\r
4309       }\r
4310     if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging\r
4311       dragInfo.start.x = dragInfo.start.y = -1;\r
4312       dragInfo.from = dragInfo.start;\r
4313     }\r
4314     if(fromX == -1 && frozen) { // not sure where this is for\r
4315                 fromX = fromY = -1; \r
4316       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4317       break;\r
4318     }\r
4319       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4320       DrawPosition(TRUE, NULL);\r
4321     break;\r
4322 \r
4323   case WM_LBUTTONUP:\r
4324       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4325       DrawPosition(TRUE, NULL);\r
4326     break;\r
4327 \r
4328   case WM_MOUSEMOVE:\r
4329     if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
4330     if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;\r
4331     MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
4332     if ((appData.animateDragging || appData.highlightDragging)\r
4333         && (wParam & MK_LBUTTON || dragging == 2)\r
4334         && dragInfo.from.x >= 0) \r
4335     {\r
4336       BOOL full_repaint = FALSE;\r
4337 \r
4338       if (appData.animateDragging) {\r
4339         dragInfo.pos = pt;\r
4340       }\r
4341       if (appData.highlightDragging) {\r
4342         HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);\r
4343         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4344             full_repaint = TRUE;\r
4345         }\r
4346       }\r
4347       \r
4348       DrawPosition( full_repaint, NULL);\r
4349       \r
4350       dragInfo.lastpos = dragInfo.pos;\r
4351     }\r
4352     break;\r
4353 \r
4354   case WM_MOUSEWHEEL: // [DM]\r
4355     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4356        /* Mouse Wheel is being rolled forward\r
4357         * Play moves forward\r
4358         */\r
4359        if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
4360                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4361        /* Mouse Wheel is being rolled backward\r
4362         * Play moves backward\r
4363         */\r
4364        if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
4365                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4366     }\r
4367     break;\r
4368 \r
4369   case WM_MBUTTONUP:\r
4370   case WM_RBUTTONUP:\r
4371     ReleaseCapture();\r
4372     RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4373     break;\r
4374  \r
4375   case WM_MBUTTONDOWN:\r
4376   case WM_RBUTTONDOWN:\r
4377     ErrorPopDown();\r
4378     ReleaseCapture();\r
4379     fromX = fromY = -1;\r
4380     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4381     dragInfo.start.x = dragInfo.start.y = -1;\r
4382     dragInfo.from = dragInfo.start;\r
4383     dragInfo.lastpos = dragInfo.pos;\r
4384     if (appData.highlightDragging) {\r
4385       ClearHighlights();\r
4386     }\r
4387     if(y == -2) {\r
4388       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4389       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4390           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
4391       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4392           if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
4393       }\r
4394       break;\r
4395     }\r
4396     DrawPosition(TRUE, NULL);\r
4397 \r
4398     menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
4399     switch (menuNr) {\r
4400     case 0:\r
4401       if (message == WM_MBUTTONDOWN) {\r
4402         buttonCount = 3;  /* even if system didn't think so */\r
4403         if (wParam & MK_SHIFT) \r
4404           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4405         else\r
4406           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4407       } else { /* message == WM_RBUTTONDOWN */\r
4408         /* Just have one menu, on the right button.  Windows users don't\r
4409            think to try the middle one, and sometimes other software steals\r
4410            it, or it doesn't really exist. */\r
4411         if(gameInfo.variant != VariantShogi)\r
4412             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4413         else\r
4414             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4415       }\r
4416       break;\r
4417     case 2:\r
4418       SetCapture(hwndMain);\r
4419       break;\r
4420     case 1:\r
4421       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4422       SetupDropMenu(hmenu);\r
4423       MenuPopup(hwnd, pt, hmenu, -1);\r
4424     default:\r
4425       break;\r
4426     }\r
4427     break;\r
4428   }\r
4429 \r
4430   recursive--;\r
4431 }\r
4432 \r
4433 /* Preprocess messages for buttons in main window */\r
4434 LRESULT CALLBACK\r
4435 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4436 {\r
4437   int id = GetWindowLongPtr(hwnd, GWLP_ID);\r
4438   int i, dir;\r
4439 \r
4440   for (i=0; i<N_BUTTONS; i++) {\r
4441     if (buttonDesc[i].id == id) break;\r
4442   }\r
4443   if (i == N_BUTTONS) return 0;\r
4444   switch (message) {\r
4445   case WM_KEYDOWN:\r
4446     switch (wParam) {\r
4447     case VK_LEFT:\r
4448     case VK_RIGHT:\r
4449       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4450       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4451       return TRUE;\r
4452     }\r
4453     break;\r
4454   case WM_CHAR:\r
4455     switch (wParam) {\r
4456     case '\r':\r
4457       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4458       return TRUE;\r
4459     default:\r
4460       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4461         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4462         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4463         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4464         SetFocus(h);\r
4465         SendMessage(h, WM_CHAR, wParam, lParam);\r
4466         return TRUE;\r
4467       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4468         TypeInEvent((char)wParam);\r
4469       }\r
4470       break;\r
4471     }\r
4472     break;\r
4473   }\r
4474   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4475 }\r
4476 \r
4477 static int promoStyle;\r
4478 \r
4479 /* Process messages for Promotion dialog box */\r
4480 LRESULT CALLBACK\r
4481 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4482 {\r
4483   char promoChar;\r
4484 \r
4485   switch (message) {\r
4486   case WM_INITDIALOG: /* message: initialize dialog box */\r
4487     /* Center the dialog over the application window */\r
4488     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4489     Translate(hDlg, DLG_PromotionKing);\r
4490     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4491       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4492        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4493        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4494                SW_SHOW : SW_HIDE);\r
4495     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4496     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4497        ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&\r
4498          PieceToChar(WhiteAngel) != '~') ||\r
4499         (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&\r
4500          PieceToChar(BlackAngel) != '~')   ) ?\r
4501                SW_SHOW : SW_HIDE);\r
4502     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4503        ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&\r
4504          PieceToChar(WhiteMarshall) != '~') ||\r
4505         (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&\r
4506          PieceToChar(BlackMarshall) != '~')   ) ?\r
4507                SW_SHOW : SW_HIDE);\r
4508     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4509     ShowWindow(GetDlgItem(hDlg, PB_Rook),   !promoStyle ? SW_SHOW : SW_HIDE);\r
4510     ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);\r
4511     if(promoStyle) {\r
4512         SetDlgItemText(hDlg, PB_Queen, "YES");\r
4513         SetDlgItemText(hDlg, PB_Knight, "NO");\r
4514         SetWindowText(hDlg, "Promote?");\r
4515     }\r
4516     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4517        gameInfo.variant == VariantSuper ?\r
4518                SW_SHOW : SW_HIDE);\r
4519     return TRUE;\r
4520 \r
4521   case WM_COMMAND: /* message: received a command */\r
4522     switch (LOWORD(wParam)) {\r
4523     case IDCANCEL:\r
4524       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4525       ClearHighlights();\r
4526       DrawPosition(FALSE, NULL);\r
4527       return TRUE;\r
4528     case PB_King:\r
4529       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4530       break;\r
4531     case PB_Queen:\r
4532       promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));\r
4533       break;\r
4534     case PB_Rook:\r
4535       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));\r
4536       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);\r
4537       break;\r
4538     case PB_Bishop:\r
4539       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));\r
4540       if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);\r
4541       break;\r
4542     case PB_Chancellor:\r
4543       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));\r
4544       break;\r
4545     case PB_Archbishop:\r
4546       promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));\r
4547       break;\r
4548     case PB_Knight:\r
4549       promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR : \r
4550                   ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));\r
4551       break;\r
4552     default:\r
4553       return FALSE;\r
4554     }\r
4555     if(promoChar == '.') return FALSE; // invalid piece chosen \r
4556     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4557     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4558     fromX = fromY = -1;\r
4559     if (!appData.highlightLastMove) {\r
4560       ClearHighlights();\r
4561       DrawPosition(FALSE, NULL);\r
4562     }\r
4563     return TRUE;\r
4564   }\r
4565   return FALSE;\r
4566 }\r
4567 \r
4568 /* Pop up promotion dialog */\r
4569 VOID\r
4570 PromotionPopup(HWND hwnd)\r
4571 {\r
4572   FARPROC lpProc;\r
4573 \r
4574   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4575   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4576     hwnd, (DLGPROC)lpProc);\r
4577   FreeProcInstance(lpProc);\r
4578 }\r
4579 \r
4580 void\r
4581 PromotionPopUp(char choice)\r
4582 {\r
4583   promoStyle = (choice == '+');\r
4584   DrawPosition(TRUE, NULL);\r
4585   PromotionPopup(hwndMain);\r
4586 }\r
4587 \r
4588 VOID\r
4589 LoadGameDialog(HWND hwnd, char* title)\r
4590 {\r
4591   UINT number = 0;\r
4592   FILE *f;\r
4593   char fileTitle[MSG_SIZ];\r
4594   f = OpenFileDialog(hwnd, "rb", "",\r
4595                      appData.oldSaveStyle ? "gam" : "pgn",\r
4596                      GAME_FILT,\r
4597                      title, &number, fileTitle, NULL);\r
4598   if (f != NULL) {\r
4599     cmailMsgLoaded = FALSE;\r
4600     if (number == 0) {\r
4601       int error = GameListBuild(f);\r
4602       if (error) {\r
4603         DisplayError(_("Cannot build game list"), error);\r
4604       } else if (!ListEmpty(&gameList) &&\r
4605                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4606         GameListPopUp(f, fileTitle);\r
4607         return;\r
4608       }\r
4609       GameListDestroy();\r
4610       number = 1;\r
4611     }\r
4612     LoadGame(f, number, fileTitle, FALSE);\r
4613   }\r
4614 }\r
4615 \r
4616 int get_term_width()\r
4617 {\r
4618     HDC hdc;\r
4619     TEXTMETRIC tm;\r
4620     RECT rc;\r
4621     HFONT hfont, hold_font;\r
4622     LOGFONT lf;\r
4623     HWND hText;\r
4624 \r
4625     if (hwndConsole)\r
4626         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4627     else\r
4628         return 79;\r
4629 \r
4630     // get the text metrics\r
4631     hdc = GetDC(hText);\r
4632     lf = font[boardSize][CONSOLE_FONT]->lf;\r
4633     if (consoleCF.dwEffects & CFE_BOLD)\r
4634         lf.lfWeight = FW_BOLD;\r
4635     if (consoleCF.dwEffects & CFE_ITALIC)\r
4636         lf.lfItalic = TRUE;\r
4637     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
4638         lf.lfStrikeOut = TRUE;\r
4639     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
4640         lf.lfUnderline = TRUE;\r
4641     hfont = CreateFontIndirect(&lf);\r
4642     hold_font = SelectObject(hdc, hfont);\r
4643     GetTextMetrics(hdc, &tm);\r
4644     SelectObject(hdc, hold_font);\r
4645     DeleteObject(hfont);\r
4646     ReleaseDC(hText, hdc);\r
4647 \r
4648     // get the rectangle\r
4649     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
4650 \r
4651     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
4652 }\r
4653 \r
4654 void UpdateICSWidth(HWND hText)\r
4655 {\r
4656     LONG old_width, new_width;\r
4657 \r
4658     new_width = get_term_width(hText, FALSE);\r
4659     old_width = GetWindowLongPtr(hText, GWLP_USERDATA);\r
4660     if (new_width != old_width)\r
4661     {\r
4662         ics_update_width(new_width);\r
4663         SetWindowLongPtr(hText, GWLP_USERDATA, new_width);\r
4664     }\r
4665 }\r
4666 \r
4667 VOID\r
4668 ChangedConsoleFont()\r
4669 {\r
4670   CHARFORMAT cfmt;\r
4671   CHARRANGE tmpsel, sel;\r
4672   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4673   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4674   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4675   PARAFORMAT paraf;\r
4676 \r
4677   cfmt.cbSize = sizeof(CHARFORMAT);\r
4678   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4679     safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
4680                sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
4681   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4682    * size.  This was undocumented in the version of MSVC++ that I had\r
4683    * when I wrote the code, but is apparently documented now.\r
4684    */\r
4685   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4686   cfmt.bCharSet = f->lf.lfCharSet;\r
4687   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4688   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4689   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4690   /* Why are the following seemingly needed too? */\r
4691   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4692   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4693   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4694   tmpsel.cpMin = 0;\r
4695   tmpsel.cpMax = -1; /*999999?*/\r
4696   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4697   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4698   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4699    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4700    */\r
4701   paraf.cbSize = sizeof(paraf);\r
4702   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4703   paraf.dxStartIndent = 0;\r
4704   paraf.dxOffset = WRAP_INDENT;\r
4705   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4706   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4707   UpdateICSWidth(hText);\r
4708 }\r
4709 \r
4710 /*---------------------------------------------------------------------------*\\r
4711  *\r
4712  * Window Proc for main window\r
4713  *\r
4714 \*---------------------------------------------------------------------------*/\r
4715 \r
4716 /* Process messages for main window, etc. */\r
4717 LRESULT CALLBACK\r
4718 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4719 {\r
4720   FARPROC lpProc;\r
4721   int wmId, wmEvent;\r
4722   char *defName;\r
4723   FILE *f;\r
4724   UINT number;\r
4725   char fileTitle[MSG_SIZ];\r
4726   static SnapData sd;\r
4727   static int peek=0;\r
4728 \r
4729   switch (message) {\r
4730 \r
4731   case WM_PAINT: /* message: repaint portion of window */\r
4732     PaintProc(hwnd);\r
4733     break;\r
4734 \r
4735   case WM_ERASEBKGND:\r
4736     if (IsIconic(hwnd)) {\r
4737       /* Cheat; change the message */\r
4738       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4739     } else {\r
4740       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4741     }\r
4742     break;\r
4743 \r
4744   case WM_LBUTTONDOWN:\r
4745   case WM_MBUTTONDOWN:\r
4746   case WM_RBUTTONDOWN:\r
4747   case WM_LBUTTONUP:\r
4748   case WM_MBUTTONUP:\r
4749   case WM_RBUTTONUP:\r
4750   case WM_MOUSEMOVE:\r
4751   case WM_MOUSEWHEEL:\r
4752     MouseEvent(hwnd, message, wParam, lParam);\r
4753     break;\r
4754 \r
4755   case WM_KEYUP:\r
4756     if((char)wParam == '\b') {\r
4757       ForwardEvent(); peek = 0;\r
4758     }\r
4759 \r
4760     JAWS_KBUP_NAVIGATION\r
4761 \r
4762     break;\r
4763 \r
4764   case WM_KEYDOWN:\r
4765     if((char)wParam == '\b') {\r
4766       if(!peek) BackwardEvent(), peek = 1;\r
4767     }\r
4768 \r
4769     JAWS_KBDOWN_NAVIGATION\r
4770 \r
4771     break;\r
4772 \r
4773   case WM_CHAR:\r
4774     \r
4775     JAWS_ALT_INTERCEPT\r
4776 \r
4777     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
4778         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
4779         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4780         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4781         SetFocus(h);\r
4782         SendMessage(h, message, wParam, lParam);\r
4783     } else if(lParam != KF_REPEAT) {\r
4784         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4785                 TypeInEvent((char)wParam);\r
4786         } else if((char)wParam == 003) CopyGameToClipboard();\r
4787          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
4788     }\r
4789 \r
4790     break;\r
4791 \r
4792   case WM_PALETTECHANGED:\r
4793     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4794       int nnew;\r
4795       HDC hdc = GetDC(hwndMain);\r
4796       SelectPalette(hdc, hPal, TRUE);\r
4797       nnew = RealizePalette(hdc);\r
4798       if (nnew > 0) {\r
4799         paletteChanged = TRUE;\r
4800 \r
4801         InvalidateRect(hwnd, &boardRect, FALSE);\r
4802       }\r
4803       ReleaseDC(hwnd, hdc);\r
4804     }\r
4805     break;\r
4806 \r
4807   case WM_QUERYNEWPALETTE:\r
4808     if (!appData.monoMode /*&& paletteChanged*/) {\r
4809       int nnew;\r
4810       HDC hdc = GetDC(hwndMain);\r
4811       paletteChanged = FALSE;\r
4812       SelectPalette(hdc, hPal, FALSE);\r
4813       nnew = RealizePalette(hdc);\r
4814       if (nnew > 0) {\r
4815         InvalidateRect(hwnd, &boardRect, FALSE);\r
4816       }\r
4817       ReleaseDC(hwnd, hdc);\r
4818       return TRUE;\r
4819     }\r
4820     return FALSE;\r
4821 \r
4822   case WM_COMMAND: /* message: command from application menu */\r
4823     wmId    = LOWORD(wParam);\r
4824     wmEvent = HIWORD(wParam);\r
4825 \r
4826     switch (wmId) {\r
4827     case IDM_NewGame:\r
4828       ResetGameEvent();\r
4829       SAY("new game enter a move to play against the computer with white");\r
4830       break;\r
4831 \r
4832     case IDM_NewGameFRC:\r
4833       if( NewGameFRC() == 0 ) {\r
4834         ResetGameEvent();\r
4835       }\r
4836       break;\r
4837 \r
4838     case IDM_NewVariant:\r
4839       NewVariantPopup(hwnd);\r
4840       break;\r
4841 \r
4842     case IDM_LoadGame:\r
4843       LoadGameDialog(hwnd, _("Load Game from File"));\r
4844       break;\r
4845 \r
4846     case IDM_LoadNextGame:\r
4847       ReloadGame(1);\r
4848       break;\r
4849 \r
4850     case IDM_LoadPrevGame:\r
4851       ReloadGame(-1);\r
4852       break;\r
4853 \r
4854     case IDM_ReloadGame:\r
4855       ReloadGame(0);\r
4856       break;\r
4857 \r
4858     case IDM_LoadPosition:\r
4859       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4860         Reset(FALSE, TRUE);\r
4861       }\r
4862       number = 1;\r
4863       f = OpenFileDialog(hwnd, "rb", "",\r
4864                          appData.oldSaveStyle ? "pos" : "fen",\r
4865                          POSITION_FILT,\r
4866                          _("Load Position from File"), &number, fileTitle, NULL);\r
4867       if (f != NULL) {\r
4868         LoadPosition(f, number, fileTitle);\r
4869       }\r
4870       break;\r
4871 \r
4872     case IDM_LoadNextPosition:\r
4873       ReloadPosition(1);\r
4874       break;\r
4875 \r
4876     case IDM_LoadPrevPosition:\r
4877       ReloadPosition(-1);\r
4878       break;\r
4879 \r
4880     case IDM_ReloadPosition:\r
4881       ReloadPosition(0);\r
4882       break;\r
4883 \r
4884     case IDM_SaveGame:\r
4885       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4886       f = OpenFileDialog(hwnd, "a", defName,\r
4887                          appData.oldSaveStyle ? "gam" : "pgn",\r
4888                          GAME_FILT,\r
4889                          _("Save Game to File"), NULL, fileTitle, NULL);\r
4890       if (f != NULL) {\r
4891         SaveGame(f, 0, "");\r
4892       }\r
4893       break;\r
4894 \r
4895     case IDM_SavePosition:\r
4896       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4897       f = OpenFileDialog(hwnd, "a", defName,\r
4898                          appData.oldSaveStyle ? "pos" : "fen",\r
4899                          POSITION_FILT,\r
4900                          _("Save Position to File"), NULL, fileTitle, NULL);\r
4901       if (f != NULL) {\r
4902         SavePosition(f, 0, "");\r
4903       }\r
4904       break;\r
4905 \r
4906     case IDM_SaveDiagram:\r
4907       defName = "diagram";\r
4908       f = OpenFileDialog(hwnd, "wb", defName,\r
4909                          "bmp",\r
4910                          DIAGRAM_FILT,\r
4911                          _("Save Diagram to File"), NULL, fileTitle, NULL);\r
4912       if (f != NULL) {\r
4913         SaveDiagram(f);\r
4914       }\r
4915       break;\r
4916 \r
4917     case IDM_CreateBook:\r
4918       CreateBookEvent();\r
4919       break;\r
4920 \r
4921     case IDM_CopyGame:\r
4922       CopyGameToClipboard();\r
4923       break;\r
4924 \r
4925     case IDM_PasteGame:\r
4926       PasteGameFromClipboard();\r
4927       break;\r
4928 \r
4929     case IDM_CopyGameListToClipboard:\r
4930       CopyGameListToClipboard();\r
4931       break;\r
4932 \r
4933     /* [AS] Autodetect FEN or PGN data */\r
4934     case IDM_PasteAny:\r
4935       PasteGameOrFENFromClipboard();\r
4936       break;\r
4937 \r
4938     /* [AS] Move history */\r
4939     case IDM_ShowMoveHistory:\r
4940         if( MoveHistoryIsUp() ) {\r
4941             MoveHistoryPopDown();\r
4942         }\r
4943         else {\r
4944             MoveHistoryPopUp();\r
4945         }\r
4946         break;\r
4947 \r
4948     /* [AS] Eval graph */\r
4949     case IDM_ShowEvalGraph:\r
4950         if( EvalGraphIsUp() ) {\r
4951             EvalGraphPopDown();\r
4952         }\r
4953         else {\r
4954             EvalGraphPopUp();\r
4955             SetFocus(hwndMain);\r
4956         }\r
4957         break;\r
4958 \r
4959     /* [AS] Engine output */\r
4960     case IDM_ShowEngineOutput:\r
4961         if( EngineOutputIsUp() ) {\r
4962             EngineOutputPopDown();\r
4963         }\r
4964         else {\r
4965             EngineOutputPopUp();\r
4966         }\r
4967         break;\r
4968 \r
4969     /* [AS] User adjudication */\r
4970     case IDM_UserAdjudication_White:\r
4971         UserAdjudicationEvent( +1 );\r
4972         break;\r
4973 \r
4974     case IDM_UserAdjudication_Black:\r
4975         UserAdjudicationEvent( -1 );\r
4976         break;\r
4977 \r
4978     case IDM_UserAdjudication_Draw:\r
4979         UserAdjudicationEvent( 0 );\r
4980         break;\r
4981 \r
4982     /* [AS] Game list options dialog */\r
4983     case IDM_GameListOptions:\r
4984       GameListOptions();\r
4985       break;\r
4986 \r
4987     case IDM_NewChat:\r
4988       ChatPopUp(NULL);\r
4989       break;\r
4990 \r
4991     case IDM_CopyPosition:\r
4992       CopyFENToClipboard();\r
4993       break;\r
4994 \r
4995     case IDM_PastePosition:\r
4996       PasteFENFromClipboard();\r
4997       break;\r
4998 \r
4999     case IDM_MailMove:\r
5000       MailMoveEvent();\r
5001       break;\r
5002 \r
5003     case IDM_ReloadCMailMsg:\r
5004       Reset(TRUE, TRUE);\r
5005       ReloadCmailMsgEvent(FALSE);\r
5006       break;\r
5007 \r
5008     case IDM_Minimize:\r
5009       ShowWindow(hwnd, SW_MINIMIZE);\r
5010       break;\r
5011 \r
5012     case IDM_Exit:\r
5013       ExitEvent(0);\r
5014       break;\r
5015 \r
5016     case IDM_MachineWhite:\r
5017       MachineWhiteEvent();\r
5018       /*\r
5019        * refresh the tags dialog only if it's visible\r
5020        */\r
5021       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5022           char *tags;\r
5023           tags = PGNTags(&gameInfo);\r
5024           TagsPopUp(tags, CmailMsg());\r
5025           free(tags);\r
5026       }\r
5027       SAY("computer starts playing white");\r
5028       break;\r
5029 \r
5030     case IDM_MachineBlack:\r
5031       MachineBlackEvent();\r
5032       /*\r
5033        * refresh the tags dialog only if it's visible\r
5034        */\r
5035       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5036           char *tags;\r
5037           tags = PGNTags(&gameInfo);\r
5038           TagsPopUp(tags, CmailMsg());\r
5039           free(tags);\r
5040       }\r
5041       SAY("computer starts playing black");\r
5042       break;\r
5043 \r
5044     case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
5045       MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
5046       break;\r
5047 \r
5048     case IDM_TwoMachines:\r
5049       TwoMachinesEvent();\r
5050       /*\r
5051        * refresh the tags dialog only if it's visible\r
5052        */\r
5053       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5054           char *tags;\r
5055           tags = PGNTags(&gameInfo);\r
5056           TagsPopUp(tags, CmailMsg());\r
5057           free(tags);\r
5058       }\r
5059       SAY("computer starts playing both sides");\r
5060       break;\r
5061 \r
5062     case IDM_AnalysisMode:\r
5063       if(AnalyzeModeEvent()) {\r
5064         SAY("analyzing current position");\r
5065       }\r
5066       break;\r
5067 \r
5068     case IDM_AnalyzeFile:\r
5069       AnalyzeFileEvent();\r
5070       break;\r
5071 \r
5072     case IDM_IcsClient:\r
5073       IcsClientEvent();\r
5074       break;\r
5075 \r
5076     case IDM_EditGame:\r
5077     case IDM_EditGame2:\r
5078       EditGameEvent();\r
5079       SAY("edit game");\r
5080       break;\r
5081 \r
5082     case IDM_EditPosition:\r
5083     case IDM_EditPosition2:\r
5084       EditPositionEvent();\r
5085       SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
5086       break;\r
5087 \r
5088     case IDM_Training:\r
5089       TrainingEvent();\r
5090       break;\r
5091 \r
5092     case IDM_ShowGameList:\r
5093       ShowGameListProc();\r
5094       break;\r
5095 \r
5096     case IDM_EditProgs1:\r
5097       EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);\r
5098       break;\r
5099 \r
5100     case IDM_LoadProg1:\r
5101      LoadEnginePopUp(hwndMain, 0);\r
5102       break;\r
5103 \r
5104     case IDM_LoadProg2:\r
5105      LoadEnginePopUp(hwndMain, 1);\r
5106       break;\r
5107 \r
5108     case IDM_EditServers:\r
5109       EditTagsPopUp(icsNames, &icsNames);\r
5110       break;\r
5111 \r
5112     case IDM_EditTags:\r
5113     case IDM_Tags:\r
5114       EditTagsProc();\r
5115       break;\r
5116 \r
5117     case IDM_EditBook:\r
5118       EditBookEvent();\r
5119       break;\r
5120 \r
5121     case IDM_EditComment:\r
5122     case IDM_Comment:\r
5123       if (commentUp && editComment) {\r
5124         CommentPopDown();\r
5125       } else {\r
5126         EditCommentEvent();\r
5127       }\r
5128       break;\r
5129 \r
5130     case IDM_Pause:\r
5131       PauseEvent();\r
5132       break;\r
5133 \r
5134     case IDM_Accept:\r
5135       AcceptEvent();\r
5136       break;\r
5137 \r
5138     case IDM_Decline:\r
5139       DeclineEvent();\r
5140       break;\r
5141 \r
5142     case IDM_Rematch:\r
5143 \r
5144       RematchEvent();\r
5145       break;\r
5146 \r
5147     case IDM_CallFlag:\r
5148       CallFlagEvent();\r
5149       break;\r
5150 \r
5151     case IDM_Draw:\r
5152       DrawEvent();\r
5153       break;\r
5154 \r
5155     case IDM_Adjourn:\r
5156       AdjournEvent();\r
5157       break;\r
5158 \r
5159     case IDM_Abort:\r
5160       AbortEvent();\r
5161       break;\r
5162 \r
5163     case IDM_Resign:\r
5164       ResignEvent();\r
5165       break;\r
5166 \r
5167     case IDM_StopObserving:\r
5168       StopObservingEvent();\r
5169       break;\r
5170 \r
5171     case IDM_StopExamining:\r
5172       StopExaminingEvent();\r
5173       break;\r
5174 \r
5175     case IDM_Upload:\r
5176       UploadGameEvent();\r
5177       break;\r
5178 \r
5179     case IDM_TypeInMove:\r
5180       TypeInEvent('\000');\r
5181       break;\r
5182 \r
5183     case IDM_TypeInName:\r
5184       PopUpNameDialog('\000');\r
5185       break;\r
5186 \r
5187     case IDM_Backward:\r
5188       BackwardEvent();\r
5189       SetFocus(hwndMain);\r
5190       break;\r
5191 \r
5192     JAWS_MENU_ITEMS\r
5193 \r
5194     case IDM_Forward:\r
5195       ForwardEvent();\r
5196       SetFocus(hwndMain);\r
5197       break;\r
5198 \r
5199     case IDM_ToStart:\r
5200       ToStartEvent();\r
5201       SetFocus(hwndMain);\r
5202       break;\r
5203 \r
5204     case IDM_ToEnd:\r
5205       ToEndEvent();\r
5206       SetFocus(hwndMain);\r
5207       break;\r
5208 \r
5209     case OPT_GameListNext: // [HGM] forward these two accelerators to Game List\r
5210     case OPT_GameListPrev:\r
5211       if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);\r
5212       break;\r
5213 \r
5214     case IDM_Revert:\r
5215       RevertEvent(FALSE);\r
5216       break;\r
5217 \r
5218     case IDM_Annotate: // [HGM] vari: revert with annotation\r
5219       RevertEvent(TRUE);\r
5220       break;\r
5221 \r
5222     case IDM_TruncateGame:\r
5223       TruncateGameEvent();\r
5224       break;\r
5225 \r
5226     case IDM_MoveNow:\r
5227       MoveNowEvent();\r
5228       break;\r
5229 \r
5230     case IDM_RetractMove:\r
5231       RetractMoveEvent();\r
5232       break;\r
5233 \r
5234     case IDM_FlipView:\r
5235       flipView = !flipView;\r
5236       DrawPosition(FALSE, NULL);\r
5237       break;\r
5238 \r
5239     case IDM_FlipClock:\r
5240       flipClock = !flipClock;\r
5241       DisplayBothClocks();\r
5242       DisplayLogos();\r
5243       break;\r
5244 \r
5245     case IDM_MuteSounds:\r
5246       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5247       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5248                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5249       break;\r
5250 \r
5251     case IDM_GeneralOptions:\r
5252       GeneralOptionsPopup(hwnd);\r
5253       DrawPosition(TRUE, NULL);\r
5254       break;\r
5255 \r
5256     case IDM_BoardOptions:\r
5257       BoardOptionsPopup(hwnd);\r
5258       break;\r
5259 \r
5260     case IDM_ThemeOptions:\r
5261       ThemeOptionsPopup(hwnd);\r
5262       break;\r
5263 \r
5264     case IDM_EnginePlayOptions:\r
5265       EnginePlayOptionsPopup(hwnd);\r
5266       break;\r
5267 \r
5268     case IDM_Engine1Options:\r
5269       EngineOptionsPopup(hwnd, &first);\r
5270       break;\r
5271 \r
5272     case IDM_Engine2Options:\r
5273       savedHwnd = hwnd;\r
5274       if(WaitForEngine(&second, SettingsMenuIfReady)) break;\r
5275       EngineOptionsPopup(hwnd, &second);\r
5276       break;\r
5277 \r
5278     case IDM_OptionsUCI:\r
5279       UciOptionsPopup(hwnd);\r
5280       break;\r
5281 \r
5282     case IDM_Tourney:\r
5283       TourneyPopup(hwnd);\r
5284       break;\r
5285 \r
5286     case IDM_IcsOptions:\r
5287       IcsOptionsPopup(hwnd);\r
5288       break;\r
5289 \r
5290     case IDM_Fonts:\r
5291       FontsOptionsPopup(hwnd);\r
5292       break;\r
5293 \r
5294     case IDM_Sounds:\r
5295       SoundOptionsPopup(hwnd);\r
5296       break;\r
5297 \r
5298     case IDM_CommPort:\r
5299       CommPortOptionsPopup(hwnd);\r
5300       break;\r
5301 \r
5302     case IDM_LoadOptions:\r
5303       LoadOptionsPopup(hwnd);\r
5304       break;\r
5305 \r
5306     case IDM_SaveOptions:\r
5307       SaveOptionsPopup(hwnd);\r
5308       break;\r
5309 \r
5310     case IDM_TimeControl:\r
5311       TimeControlOptionsPopup(hwnd);\r
5312       break;\r
5313 \r
5314     case IDM_SaveSettings:\r
5315       SaveSettings(settingsFileName);\r
5316       break;\r
5317 \r
5318     case IDM_SaveSettingsOnExit:\r
5319       saveSettingsOnExit = !saveSettingsOnExit;\r
5320       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5321                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5322                                          MF_CHECKED : MF_UNCHECKED));\r
5323       break;\r
5324 \r
5325     case IDM_Hint:\r
5326       HintEvent();\r
5327       break;\r
5328 \r
5329     case IDM_Book:\r
5330       BookEvent();\r
5331       break;\r
5332 \r
5333     case IDM_AboutGame:\r
5334       AboutGameEvent();\r
5335       break;\r
5336 \r
5337     case IDM_Debug:\r
5338       appData.debugMode = !appData.debugMode;\r
5339       if (appData.debugMode) {\r
5340         char dir[MSG_SIZ];\r
5341         GetCurrentDirectory(MSG_SIZ, dir);\r
5342         SetCurrentDirectory(installDir);\r
5343         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5344         SetCurrentDirectory(dir);\r
5345         setbuf(debugFP, NULL);\r
5346       } else {\r
5347         fclose(debugFP);\r
5348         debugFP = NULL;\r
5349       }\r
5350       break;\r
5351 \r
5352     case IDM_HELPCONTENTS:\r
5353       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5354           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5355           MessageBox (GetFocus(),\r
5356                     _("Unable to activate help"),\r
5357                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5358       }\r
5359       break;\r
5360 \r
5361     case IDM_HELPSEARCH:\r
5362         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5363             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5364         MessageBox (GetFocus(),\r
5365                     _("Unable to activate help"),\r
5366                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5367       }\r
5368       break;\r
5369 \r
5370     case IDM_HELPHELP:\r
5371       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5372         MessageBox (GetFocus(),\r
5373                     _("Unable to activate help"),\r
5374                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5375       }\r
5376       break;\r
5377 \r
5378     case IDM_ABOUT:\r
5379       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5380       DialogBox(hInst, \r
5381         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5382         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5383       FreeProcInstance(lpProc);\r
5384       break;\r
5385 \r
5386     case IDM_DirectCommand1:\r
5387       AskQuestionEvent(_("Direct Command"),\r
5388                        _("Send to chess program:"), "", "1");\r
5389       break;\r
5390     case IDM_DirectCommand2:\r
5391       AskQuestionEvent(_("Direct Command"),\r
5392                        _("Send to second chess program:"), "", "2");\r
5393       break;\r
5394 \r
5395     case EP_WhitePawn:\r
5396       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5397       fromX = fromY = -1;\r
5398       break;\r
5399 \r
5400     case EP_WhiteKnight:\r
5401       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5402       fromX = fromY = -1;\r
5403       break;\r
5404 \r
5405     case EP_WhiteBishop:\r
5406       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5407       fromX = fromY = -1;\r
5408       break;\r
5409 \r
5410     case EP_WhiteRook:\r
5411       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5412       fromX = fromY = -1;\r
5413       break;\r
5414 \r
5415     case EP_WhiteQueen:\r
5416       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5417       fromX = fromY = -1;\r
5418       break;\r
5419 \r
5420     case EP_WhiteFerz:\r
5421       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5422       fromX = fromY = -1;\r
5423       break;\r
5424 \r
5425     case EP_WhiteWazir:\r
5426       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5427       fromX = fromY = -1;\r
5428       break;\r
5429 \r
5430     case EP_WhiteAlfil:\r
5431       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5432       fromX = fromY = -1;\r
5433       break;\r
5434 \r
5435     case EP_WhiteCannon:\r
5436       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5437       fromX = fromY = -1;\r
5438       break;\r
5439 \r
5440     case EP_WhiteCardinal:\r
5441       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5442       fromX = fromY = -1;\r
5443       break;\r
5444 \r
5445     case EP_WhiteMarshall:\r
5446       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5447       fromX = fromY = -1;\r
5448       break;\r
5449 \r
5450     case EP_WhiteKing:\r
5451       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5452       fromX = fromY = -1;\r
5453       break;\r
5454 \r
5455     case EP_BlackPawn:\r
5456       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5457       fromX = fromY = -1;\r
5458       break;\r
5459 \r
5460     case EP_BlackKnight:\r
5461       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5462       fromX = fromY = -1;\r
5463       break;\r
5464 \r
5465     case EP_BlackBishop:\r
5466       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5467       fromX = fromY = -1;\r
5468       break;\r
5469 \r
5470     case EP_BlackRook:\r
5471       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5472       fromX = fromY = -1;\r
5473       break;\r
5474 \r
5475     case EP_BlackQueen:\r
5476       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5477       fromX = fromY = -1;\r
5478       break;\r
5479 \r
5480     case EP_BlackFerz:\r
5481       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5482       fromX = fromY = -1;\r
5483       break;\r
5484 \r
5485     case EP_BlackWazir:\r
5486       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5487       fromX = fromY = -1;\r
5488       break;\r
5489 \r
5490     case EP_BlackAlfil:\r
5491       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5492       fromX = fromY = -1;\r
5493       break;\r
5494 \r
5495     case EP_BlackCannon:\r
5496       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5497       fromX = fromY = -1;\r
5498       break;\r
5499 \r
5500     case EP_BlackCardinal:\r
5501       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5502       fromX = fromY = -1;\r
5503       break;\r
5504 \r
5505     case EP_BlackMarshall:\r
5506       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5507       fromX = fromY = -1;\r
5508       break;\r
5509 \r
5510     case EP_BlackKing:\r
5511       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5512       fromX = fromY = -1;\r
5513       break;\r
5514 \r
5515     case EP_EmptySquare:\r
5516       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5517       fromX = fromY = -1;\r
5518       break;\r
5519 \r
5520     case EP_ClearBoard:\r
5521       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5522       fromX = fromY = -1;\r
5523       break;\r
5524 \r
5525     case EP_White:\r
5526       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5527       fromX = fromY = -1;\r
5528       break;\r
5529 \r
5530     case EP_Black:\r
5531       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5532       fromX = fromY = -1;\r
5533       break;\r
5534 \r
5535     case EP_Promote:\r
5536       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5537       fromX = fromY = -1;\r
5538       break;\r
5539 \r
5540     case EP_Demote:\r
5541       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5542       fromX = fromY = -1;\r
5543       break;\r
5544 \r
5545     case DP_Pawn:\r
5546       DropMenuEvent(WhitePawn, fromX, fromY);\r
5547       fromX = fromY = -1;\r
5548       break;\r
5549 \r
5550     case DP_Knight:\r
5551       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5552       fromX = fromY = -1;\r
5553       break;\r
5554 \r
5555     case DP_Bishop:\r
5556       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5557       fromX = fromY = -1;\r
5558       break;\r
5559 \r
5560     case DP_Rook:\r
5561       DropMenuEvent(WhiteRook, fromX, fromY);\r
5562       fromX = fromY = -1;\r
5563       break;\r
5564 \r
5565     case DP_Queen:\r
5566       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5567       fromX = fromY = -1;\r
5568       break;\r
5569 \r
5570     case IDM_English:\r
5571       barbaric = 0; appData.language = "";\r
5572       TranslateMenus(0);\r
5573       CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5574       CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
5575       lastChecked = wmId;\r
5576       break;\r
5577 \r
5578     default:\r
5579       if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)\r
5580           RecentEngineEvent(wmId - IDM_RecentEngines);\r
5581       else\r
5582       if(wmId > IDM_English && wmId < IDM_English+20) {\r
5583           LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
5584           TranslateMenus(0);\r
5585           CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
5586           CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
5587           lastChecked = wmId;\r
5588           break;\r
5589       }\r
5590       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5591     }\r
5592     break;\r
5593 \r
5594   case WM_TIMER:\r
5595     switch (wParam) {\r
5596     case CLOCK_TIMER_ID:\r
5597       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5598       clockTimerEvent = 0;\r
5599       DecrementClocks(); /* call into back end */\r
5600       break;\r
5601     case LOAD_GAME_TIMER_ID:\r
5602       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5603       loadGameTimerEvent = 0;\r
5604       AutoPlayGameLoop(); /* call into back end */\r
5605       break;\r
5606     case ANALYSIS_TIMER_ID:\r
5607       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
5608                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
5609         AnalysisPeriodicEvent(0);\r
5610       } else {\r
5611         KillTimer(hwnd, analysisTimerEvent);\r
5612         analysisTimerEvent = 0;\r
5613       }\r
5614       break;\r
5615     case DELAYED_TIMER_ID:\r
5616       KillTimer(hwnd, delayedTimerEvent);\r
5617       delayedTimerEvent = 0;\r
5618       delayedTimerCallback();\r
5619       break;\r
5620     }\r
5621     break;\r
5622 \r
5623   case WM_USER_Input:\r
5624     InputEvent(hwnd, message, wParam, lParam);\r
5625     break;\r
5626 \r
5627   /* [AS] Also move "attached" child windows */\r
5628   case WM_WINDOWPOSCHANGING:\r
5629 \r
5630     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5631         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5632 \r
5633         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5634             /* Window is moving */\r
5635             RECT rcMain;\r
5636 \r
5637 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
5638             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
5639             rcMain.right  = wpMain.x + wpMain.width;\r
5640             rcMain.top    = wpMain.y;\r
5641             rcMain.bottom = wpMain.y + wpMain.height;\r
5642             \r
5643             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5644             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5645             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5646             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
5647             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
5648             wpMain.x = lpwp->x;\r
5649             wpMain.y = lpwp->y;\r
5650         }\r
5651     }\r
5652     break;\r
5653 \r
5654   /* [AS] Snapping */\r
5655   case WM_ENTERSIZEMOVE:\r
5656     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
5657     if (hwnd == hwndMain) {\r
5658       doingSizing = TRUE;\r
5659       lastSizing = 0;\r
5660     }\r
5661     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5662     break;\r
5663 \r
5664   case WM_SIZING:\r
5665     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
5666     if (hwnd == hwndMain) {\r
5667       lastSizing = wParam;\r
5668     }\r
5669     break;\r
5670 \r
5671   case WM_MOVING:\r
5672     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
5673       return OnMoving( &sd, hwnd, wParam, lParam );\r
5674 \r
5675   case WM_EXITSIZEMOVE:\r
5676     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
5677     if (hwnd == hwndMain) {\r
5678       RECT client;\r
5679       doingSizing = FALSE;\r
5680       InvalidateRect(hwnd, &boardRect, FALSE);\r
5681       GetClientRect(hwnd, &client);\r
5682       ResizeBoard(client.right, client.bottom, lastSizing);\r
5683       lastSizing = 0;\r
5684       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
5685     }\r
5686     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5687     break;\r
5688 \r
5689   case WM_DESTROY: /* message: window being destroyed */\r
5690     PostQuitMessage(0);\r
5691     break;\r
5692 \r
5693   case WM_CLOSE:\r
5694     if (hwnd == hwndMain) {\r
5695       ExitEvent(0);\r
5696     }\r
5697     break;\r
5698 \r
5699   default:      /* Passes it on if unprocessed */\r
5700     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5701   }\r
5702   return 0;\r
5703 }\r
5704 \r
5705 /*---------------------------------------------------------------------------*\\r
5706  *\r
5707  * Misc utility routines\r
5708  *\r
5709 \*---------------------------------------------------------------------------*/\r
5710 \r
5711 /*\r
5712  * Decent random number generator, at least not as bad as Windows\r
5713  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5714  */\r
5715 unsigned int randstate;\r
5716 \r
5717 int\r
5718 myrandom(void)\r
5719 {\r
5720   randstate = randstate * 1664525 + 1013904223;\r
5721   return (int) randstate & 0x7fffffff;\r
5722 }\r
5723 \r
5724 void\r
5725 mysrandom(unsigned int seed)\r
5726 {\r
5727   randstate = seed;\r
5728 }\r
5729 \r
5730 \r
5731 /* \r
5732  * returns TRUE if user selects a different color, FALSE otherwise \r
5733  */\r
5734 \r
5735 BOOL\r
5736 ChangeColor(HWND hwnd, COLORREF *which)\r
5737 {\r
5738   static BOOL firstTime = TRUE;\r
5739   static DWORD customColors[16];\r
5740   CHOOSECOLOR cc;\r
5741   COLORREF newcolor;\r
5742   int i;\r
5743   ColorClass ccl;\r
5744 \r
5745   if (firstTime) {\r
5746     /* Make initial colors in use available as custom colors */\r
5747     /* Should we put the compiled-in defaults here instead? */\r
5748     i = 0;\r
5749     customColors[i++] = lightSquareColor & 0xffffff;\r
5750     customColors[i++] = darkSquareColor & 0xffffff;\r
5751     customColors[i++] = whitePieceColor & 0xffffff;\r
5752     customColors[i++] = blackPieceColor & 0xffffff;\r
5753     customColors[i++] = highlightSquareColor & 0xffffff;\r
5754     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5755 \r
5756     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5757       customColors[i++] = textAttribs[ccl].color;\r
5758     }\r
5759     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5760     firstTime = FALSE;\r
5761   }\r
5762 \r
5763   cc.lStructSize = sizeof(cc);\r
5764   cc.hwndOwner = hwnd;\r
5765   cc.hInstance = NULL;\r
5766   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5767   cc.lpCustColors = (LPDWORD) customColors;\r
5768   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5769 \r
5770   if (!ChooseColor(&cc)) return FALSE;\r
5771 \r
5772   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5773   if (newcolor == *which) return FALSE;\r
5774   *which = newcolor;\r
5775   return TRUE;\r
5776 \r
5777   /*\r
5778   InitDrawingColors();\r
5779   InvalidateRect(hwnd, &boardRect, FALSE);\r
5780   */\r
5781 }\r
5782 \r
5783 BOOLEAN\r
5784 MyLoadSound(MySound *ms)\r
5785 {\r
5786   BOOL ok = FALSE;\r
5787   struct stat st;\r
5788   FILE *f;\r
5789 \r
5790   if (ms->data && ms->flag) free(ms->data);\r
5791   ms->data = NULL;\r
5792 \r
5793   switch (ms->name[0]) {\r
5794   case NULLCHAR:\r
5795     /* Silence */\r
5796     ok = TRUE;\r
5797     break;\r
5798   case '$':\r
5799     /* System sound from Control Panel.  Don't preload here. */\r
5800     ok = TRUE;\r
5801     break;\r
5802   case '!':\r
5803     if (ms->name[1] == NULLCHAR) {\r
5804       /* "!" alone = silence */\r
5805       ok = TRUE;\r
5806     } else {\r
5807       /* Builtin wave resource.  Error if not found. */\r
5808       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5809       if (h == NULL) break;\r
5810       ms->data = (void *)LoadResource(hInst, h);\r
5811       ms->flag = 0; // not maloced, so cannot be freed!\r
5812       if (h == NULL) break;\r
5813       ok = TRUE;\r
5814     }\r
5815     break;\r
5816   default:\r
5817     /* .wav file.  Error if not found. */\r
5818     f = fopen(ms->name, "rb");\r
5819     if (f == NULL) break;\r
5820     if (fstat(fileno(f), &st) < 0) break;\r
5821     ms->data = malloc(st.st_size);\r
5822     ms->flag = 1;\r
5823     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5824     fclose(f);\r
5825     ok = TRUE;\r
5826     break;\r
5827   }\r
5828   if (!ok) {\r
5829     char buf[MSG_SIZ];\r
5830       snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
5831     DisplayError(buf, GetLastError());\r
5832   }\r
5833   return ok;\r
5834 }\r
5835 \r
5836 BOOLEAN\r
5837 MyPlaySound(MySound *ms)\r
5838 {\r
5839   BOOLEAN ok = FALSE;\r
5840 \r
5841   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
5842   switch (ms->name[0]) {\r
5843   case NULLCHAR:\r
5844         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
5845     /* Silence */\r
5846     ok = TRUE;\r
5847     break;\r
5848   case '$':\r
5849     /* System sound from Control Panel (deprecated feature).\r
5850        "$" alone or an unset sound name gets default beep (still in use). */\r
5851     if (ms->name[1]) {\r
5852       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5853     }\r
5854     if (!ok) ok = MessageBeep(MB_OK);\r
5855     break; \r
5856   case '!':\r
5857     /* Builtin wave resource, or "!" alone for silence */\r
5858     if (ms->name[1]) {\r
5859       if (ms->data == NULL) return FALSE;\r
5860       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5861     } else {\r
5862       ok = TRUE;\r
5863     }\r
5864     break;\r
5865   default:\r
5866     /* .wav file.  Error if not found. */\r
5867     if (ms->data == NULL) return FALSE;\r
5868     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5869     break;\r
5870   }\r
5871   /* Don't print an error: this can happen innocently if the sound driver\r
5872      is busy; for instance, if another instance of WinBoard is playing\r
5873      a sound at about the same time. */\r
5874   return ok;\r
5875 }\r
5876 \r
5877 \r
5878 LRESULT CALLBACK\r
5879 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5880 {\r
5881   BOOL ok;\r
5882   OPENFILENAME *ofn;\r
5883   static UINT *number; /* gross that this is static */\r
5884 \r
5885   switch (message) {\r
5886   case WM_INITDIALOG: /* message: initialize dialog box */\r
5887     /* Center the dialog over the application window */\r
5888     ofn = (OPENFILENAME *) lParam;\r
5889     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5890       number = (UINT *) ofn->lCustData;\r
5891       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5892     } else {\r
5893       number = NULL;\r
5894     }\r
5895     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5896     Translate(hDlg, 1536);\r
5897     return FALSE;  /* Allow for further processing */\r
5898 \r
5899   case WM_COMMAND:\r
5900     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5901       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5902     }\r
5903     return FALSE;  /* Allow for further processing */\r
5904   }\r
5905   return FALSE;\r
5906 }\r
5907 \r
5908 UINT APIENTRY\r
5909 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5910 {\r
5911   static UINT *number;\r
5912   OPENFILENAME *ofname;\r
5913   OFNOTIFY *ofnot;\r
5914   switch (uiMsg) {\r
5915   case WM_INITDIALOG:\r
5916     Translate(hdlg, DLG_IndexNumber);\r
5917     ofname = (OPENFILENAME *)lParam;\r
5918     number = (UINT *)(ofname->lCustData);\r
5919     break;\r
5920   case WM_NOTIFY:\r
5921     ofnot = (OFNOTIFY *)lParam;\r
5922     if (ofnot->hdr.code == CDN_FILEOK) {\r
5923       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5924     }\r
5925     break;\r
5926   }\r
5927   return 0;\r
5928 }\r
5929 \r
5930 \r
5931 FILE *\r
5932 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
5933                char *nameFilt, char *dlgTitle, UINT *number,\r
5934                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5935 {\r
5936   OPENFILENAME openFileName;\r
5937   char buf1[MSG_SIZ];\r
5938   FILE *f;\r
5939 \r
5940   if (fileName == NULL) fileName = buf1;\r
5941   if (defName == NULL) {\r
5942     safeStrCpy(fileName, "*.", 3 );\r
5943     strcat(fileName, defExt);\r
5944   } else {\r
5945     safeStrCpy(fileName, defName, MSG_SIZ );\r
5946   }\r
5947     if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );\r
5948   if (number) *number = 0;\r
5949 \r
5950   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5951   openFileName.hwndOwner         = hwnd;\r
5952   openFileName.hInstance         = (HANDLE) hInst;\r
5953   openFileName.lpstrFilter       = nameFilt;\r
5954   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5955   openFileName.nMaxCustFilter    = 0L;\r
5956   openFileName.nFilterIndex      = 1L;\r
5957   openFileName.lpstrFile         = fileName;\r
5958   openFileName.nMaxFile          = MSG_SIZ;\r
5959   openFileName.lpstrFileTitle    = fileTitle;\r
5960   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5961   openFileName.lpstrInitialDir   = NULL;\r
5962   openFileName.lpstrTitle        = dlgTitle;\r
5963   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5964     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
5965     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5966     | (oldDialog ? 0 : OFN_EXPLORER);\r
5967   openFileName.nFileOffset       = 0;\r
5968   openFileName.nFileExtension    = 0;\r
5969   openFileName.lpstrDefExt       = defExt;\r
5970   openFileName.lCustData         = (LONG) number;\r
5971   openFileName.lpfnHook          = oldDialog ?\r
5972     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5973   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5974 \r
5975   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
5976                         GetOpenFileName(&openFileName)) {\r
5977     /* open the file */\r
5978     f = fopen(openFileName.lpstrFile, write);\r
5979     if (f == NULL) {\r
5980       MessageBox(hwnd, _("File open failed"), NULL,\r
5981                  MB_OK|MB_ICONEXCLAMATION);\r
5982       return NULL;\r
5983     }\r
5984   } else {\r
5985     int err = CommDlgExtendedError();\r
5986     if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
5987     return FALSE;\r
5988   }\r
5989   return f;\r
5990 }\r
5991 \r
5992 \r
5993 \r
5994 VOID APIENTRY\r
5995 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5996 {\r
5997   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5998 \r
5999   /*\r
6000    * Get the first pop-up menu in the menu template. This is the\r
6001    * menu that TrackPopupMenu displays.\r
6002    */\r
6003   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6004   TranslateOneMenu(10, hmenuTrackPopup);\r
6005 \r
6006   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6007 \r
6008   /*\r
6009    * TrackPopup uses screen coordinates, so convert the\r
6010    * coordinates of the mouse click to screen coordinates.\r
6011    */\r
6012   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6013 \r
6014   /* Draw and track the floating pop-up menu. */\r
6015   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6016                  pt.x, pt.y, 0, hwnd, NULL);\r
6017 \r
6018   /* Destroy the menu.*/\r
6019   DestroyMenu(hmenu);\r
6020 }\r
6021    \r
6022 typedef struct {\r
6023   HWND hDlg, hText;\r
6024   int sizeX, sizeY, newSizeX, newSizeY;\r
6025   HDWP hdwp;\r
6026 } ResizeEditPlusButtonsClosure;\r
6027 \r
6028 BOOL CALLBACK\r
6029 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6030 {\r
6031   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6032   RECT rect;\r
6033   POINT pt;\r
6034 \r
6035   if (hChild == cl->hText) return TRUE;\r
6036   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6037   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6038   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6039   ScreenToClient(cl->hDlg, &pt);\r
6040   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6041     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6042   return TRUE;\r
6043 }\r
6044 \r
6045 /* Resize a dialog that has a (rich) edit field filling most of\r
6046    the top, with a row of buttons below */\r
6047 VOID\r
6048 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6049 {\r
6050   RECT rectText;\r
6051   int newTextHeight, newTextWidth;\r
6052   ResizeEditPlusButtonsClosure cl;\r
6053   \r
6054   /*if (IsIconic(hDlg)) return;*/\r
6055   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6056   \r
6057   cl.hdwp = BeginDeferWindowPos(8);\r
6058 \r
6059   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6060   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6061   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6062   if (newTextHeight < 0) {\r
6063     newSizeY += -newTextHeight;\r
6064     newTextHeight = 0;\r
6065   }\r
6066   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6067     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6068 \r
6069   cl.hDlg = hDlg;\r
6070   cl.hText = hText;\r
6071   cl.sizeX = sizeX;\r
6072   cl.sizeY = sizeY;\r
6073   cl.newSizeX = newSizeX;\r
6074   cl.newSizeY = newSizeY;\r
6075   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6076 \r
6077   EndDeferWindowPos(cl.hdwp);\r
6078 }\r
6079 \r
6080 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6081 {\r
6082     RECT    rChild, rParent;\r
6083     int     wChild, hChild, wParent, hParent;\r
6084     int     wScreen, hScreen, xNew, yNew;\r
6085     HDC     hdc;\r
6086 \r
6087     /* Get the Height and Width of the child window */\r
6088     GetWindowRect (hwndChild, &rChild);\r
6089     wChild = rChild.right - rChild.left;\r
6090     hChild = rChild.bottom - rChild.top;\r
6091 \r
6092     /* Get the Height and Width of the parent window */\r
6093     GetWindowRect (hwndParent, &rParent);\r
6094     wParent = rParent.right - rParent.left;\r
6095     hParent = rParent.bottom - rParent.top;\r
6096 \r
6097     /* Get the display limits */\r
6098     hdc = GetDC (hwndChild);\r
6099     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6100     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6101     ReleaseDC(hwndChild, hdc);\r
6102 \r
6103     /* Calculate new X position, then adjust for screen */\r
6104     xNew = rParent.left + ((wParent - wChild) /2);\r
6105     if (xNew < 0) {\r
6106         xNew = 0;\r
6107     } else if ((xNew+wChild) > wScreen) {\r
6108         xNew = wScreen - wChild;\r
6109     }\r
6110 \r
6111     /* Calculate new Y position, then adjust for screen */\r
6112     if( mode == 0 ) {\r
6113         yNew = rParent.top  + ((hParent - hChild) /2);\r
6114     }\r
6115     else {\r
6116         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6117     }\r
6118 \r
6119     if (yNew < 0) {\r
6120         yNew = 0;\r
6121     } else if ((yNew+hChild) > hScreen) {\r
6122         yNew = hScreen - hChild;\r
6123     }\r
6124 \r
6125     /* Set it, and return */\r
6126     return SetWindowPos (hwndChild, NULL,\r
6127                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6128 }\r
6129 \r
6130 /* Center one window over another */\r
6131 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6132 {\r
6133     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6134 }\r
6135 \r
6136 /*---------------------------------------------------------------------------*\\r
6137  *\r
6138  * Startup Dialog functions\r
6139  *\r
6140 \*---------------------------------------------------------------------------*/\r
6141 void\r
6142 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6143 {\r
6144   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6145 \r
6146   while (*cd != NULL) {\r
6147     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
6148     cd++;\r
6149   }\r
6150 }\r
6151 \r
6152 void\r
6153 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6154 {\r
6155   char buf1[MAX_ARG_LEN];\r
6156   int len;\r
6157 \r
6158   if (str[0] == '@') {\r
6159     FILE* f = fopen(str + 1, "r");\r
6160     if (f == NULL) {\r
6161       DisplayFatalError(str + 1, errno, 2);\r
6162       return;\r
6163     }\r
6164     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6165     fclose(f);\r
6166     buf1[len] = NULLCHAR;\r
6167     str = buf1;\r
6168   }\r
6169 \r
6170   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6171 \r
6172   for (;;) {\r
6173     char buf[MSG_SIZ];\r
6174     char *end = strchr(str, '\n');\r
6175     if (end == NULL) return;\r
6176     memcpy(buf, str, end - str);\r
6177     buf[end - str] = NULLCHAR;\r
6178     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6179     str = end + 1;\r
6180   }\r
6181 }\r
6182 \r
6183 void\r
6184 SetStartupDialogEnables(HWND hDlg)\r
6185 {\r
6186   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6187     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6188     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6189   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6190     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6191   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6192     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6193   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6194     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6195   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6196     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6197     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6198     IsDlgButtonChecked(hDlg, OPT_View));\r
6199 }\r
6200 \r
6201 char *\r
6202 QuoteForFilename(char *filename)\r
6203 {\r
6204   int dquote, space;\r
6205   dquote = strchr(filename, '"') != NULL;\r
6206   space = strchr(filename, ' ') != NULL;\r
6207   if (dquote || space) {\r
6208     if (dquote) {\r
6209       return "'";\r
6210     } else {\r
6211       return "\"";\r
6212     }\r
6213   } else {\r
6214     return "";\r
6215   }\r
6216 }\r
6217 \r
6218 VOID\r
6219 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6220 {\r
6221   char buf[MSG_SIZ];\r
6222   char *q;\r
6223 \r
6224   InitComboStringsFromOption(hwndCombo, nthnames);\r
6225   q = QuoteForFilename(nthcp);\r
6226     snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
6227   if (*nthdir != NULLCHAR) {\r
6228     q = QuoteForFilename(nthdir);\r
6229       snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
6230   }\r
6231   if (*nthcp == NULLCHAR) {\r
6232     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6233   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6234     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6235     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6236   }\r
6237 }\r
6238 \r
6239 LRESULT CALLBACK\r
6240 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6241 {\r
6242   char buf[MSG_SIZ];\r
6243   HANDLE hwndCombo;\r
6244   char *p;\r
6245 \r
6246   switch (message) {\r
6247   case WM_INITDIALOG:\r
6248     /* Center the dialog */\r
6249     CenterWindow (hDlg, GetDesktopWindow());\r
6250     Translate(hDlg, DLG_Startup);\r
6251     /* Initialize the dialog items */\r
6252     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6253                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6254                   firstChessProgramNames);\r
6255     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6256                   appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,\r
6257                   singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo\r
6258     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6259     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6260       snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6261     if (*appData.icsHelper != NULLCHAR) {\r
6262       char *q = QuoteForFilename(appData.icsHelper);\r
6263       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6264     }\r
6265     if (*appData.icsHost == NULLCHAR) {\r
6266       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6267       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6268     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6269       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6270       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6271     }\r
6272 \r
6273     if (appData.icsActive) {\r
6274       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6275     }\r
6276     else if (appData.noChessProgram) {\r
6277       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6278     }\r
6279     else {\r
6280       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6281     }\r
6282 \r
6283     SetStartupDialogEnables(hDlg);\r
6284     return TRUE;\r
6285 \r
6286   case WM_COMMAND:\r
6287     switch (LOWORD(wParam)) {\r
6288     case IDOK:\r
6289       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6290         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6291         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6292         p = buf;\r
6293         comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox\r
6294         ParseArgs(StringGet, &p);\r
6295         safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
6296         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6297         p = buf;\r
6298         SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...\r
6299         ParseArgs(StringGet, &p);\r
6300         SwapEngines(singleList); // ... and then make it 'second'\r
6301 \r
6302         appData.noChessProgram = FALSE;\r
6303         appData.icsActive = FALSE;\r
6304       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6305         safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
6306         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6307         p = buf;\r
6308         ParseArgs(StringGet, &p);\r
6309         if (appData.zippyPlay) {\r
6310           safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
6311           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6312           p = buf;\r
6313           ParseArgs(StringGet, &p);\r
6314         }\r
6315       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6316         appData.noChessProgram = TRUE;\r
6317         appData.icsActive = FALSE;\r
6318       } else {\r
6319         MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
6320                    _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
6321         return TRUE;\r
6322       }\r
6323       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6324         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6325         p = buf;\r
6326         ParseArgs(StringGet, &p);\r
6327       }\r
6328       EndDialog(hDlg, TRUE);\r
6329       return TRUE;\r
6330 \r
6331     case IDCANCEL:\r
6332       ExitEvent(0);\r
6333       return TRUE;\r
6334 \r
6335     case IDM_HELPCONTENTS:\r
6336       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6337         MessageBox (GetFocus(),\r
6338                     _("Unable to activate help"),\r
6339                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6340       }\r
6341       break;\r
6342 \r
6343     default:\r
6344       SetStartupDialogEnables(hDlg);\r
6345       break;\r
6346     }\r
6347     break;\r
6348   }\r
6349   return FALSE;\r
6350 }\r
6351 \r
6352 /*---------------------------------------------------------------------------*\\r
6353  *\r
6354  * About box dialog functions\r
6355  *\r
6356 \*---------------------------------------------------------------------------*/\r
6357 \r
6358 /* Process messages for "About" dialog box */\r
6359 LRESULT CALLBACK\r
6360 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6361 {\r
6362   switch (message) {\r
6363   case WM_INITDIALOG: /* message: initialize dialog box */\r
6364     /* Center the dialog over the application window */\r
6365     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6366     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6367     Translate(hDlg, ABOUTBOX);\r
6368     JAWS_COPYRIGHT\r
6369     return (TRUE);\r
6370 \r
6371   case WM_COMMAND: /* message: received a command */\r
6372     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6373         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6374       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6375       return (TRUE);\r
6376     }\r
6377     break;\r
6378   }\r
6379   return (FALSE);\r
6380 }\r
6381 \r
6382 /*---------------------------------------------------------------------------*\\r
6383  *\r
6384  * Comment Dialog functions\r
6385  *\r
6386 \*---------------------------------------------------------------------------*/\r
6387 \r
6388 LRESULT CALLBACK\r
6389 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6390 {\r
6391   static HANDLE hwndText = NULL;\r
6392   int len, newSizeX, newSizeY, flags;\r
6393   static int sizeX, sizeY;\r
6394   char *str;\r
6395   RECT rect;\r
6396   MINMAXINFO *mmi;\r
6397 \r
6398   switch (message) {\r
6399   case WM_INITDIALOG: /* message: initialize dialog box */\r
6400     /* Initialize the dialog items */\r
6401     Translate(hDlg, DLG_EditComment);\r
6402     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6403     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6404     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6405     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6406     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6407     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6408     SetWindowText(hDlg, commentTitle);\r
6409     if (editComment) {\r
6410       SetFocus(hwndText);\r
6411     } else {\r
6412       SetFocus(GetDlgItem(hDlg, IDOK));\r
6413     }\r
6414     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6415                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6416                 MAKELPARAM(FALSE, 0));\r
6417     /* Size and position the dialog */\r
6418     if (!commentDialog) {\r
6419       commentDialog = hDlg;\r
6420       flags = SWP_NOZORDER;\r
6421       GetClientRect(hDlg, &rect);\r
6422       sizeX = rect.right;\r
6423       sizeY = rect.bottom;\r
6424       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6425           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6426         WINDOWPLACEMENT wp;\r
6427         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6428         wp.length = sizeof(WINDOWPLACEMENT);\r
6429         wp.flags = 0;\r
6430         wp.showCmd = SW_SHOW;\r
6431         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6432         wp.rcNormalPosition.left = wpComment.x;\r
6433         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6434         wp.rcNormalPosition.top = wpComment.y;\r
6435         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6436         SetWindowPlacement(hDlg, &wp);\r
6437 \r
6438         GetClientRect(hDlg, &rect);\r
6439         newSizeX = rect.right;\r
6440         newSizeY = rect.bottom;\r
6441         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6442                               newSizeX, newSizeY);\r
6443         sizeX = newSizeX;\r
6444         sizeY = newSizeY;\r
6445       }\r
6446     }\r
6447     SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );\r
6448     return FALSE;\r
6449 \r
6450   case WM_COMMAND: /* message: received a command */\r
6451     switch (LOWORD(wParam)) {\r
6452     case IDOK:\r
6453       if (editComment) {\r
6454         char *p, *q;\r
6455         /* Read changed options from the dialog box */\r
6456         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6457         len = GetWindowTextLength(hwndText);\r
6458         str = (char *) malloc(len + 1);\r
6459         GetWindowText(hwndText, str, len + 1);\r
6460         p = q = str;\r
6461         while (*q) {\r
6462           if (*q == '\r')\r
6463             q++;\r
6464           else\r
6465             *p++ = *q++;\r
6466         }\r
6467         *p = NULLCHAR;\r
6468         ReplaceComment(commentIndex, str);\r
6469         free(str);\r
6470       }\r
6471       CommentPopDown();\r
6472       return TRUE;\r
6473 \r
6474     case IDCANCEL:\r
6475     case OPT_CancelComment:\r
6476       CommentPopDown();\r
6477       return TRUE;\r
6478 \r
6479     case OPT_ClearComment:\r
6480       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6481       break;\r
6482 \r
6483     case OPT_EditComment:\r
6484       EditCommentEvent();\r
6485       return TRUE;\r
6486 \r
6487     default:\r
6488       break;\r
6489     }\r
6490     break;\r
6491 \r
6492   case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
6493         if( wParam == OPT_CommentText ) {\r
6494             MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
6495 \r
6496             if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||\r
6497                 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {\r
6498                 POINTL pt;\r
6499                 LRESULT index;\r
6500 \r
6501                 pt.x = LOWORD( lpMF->lParam );\r
6502                 pt.y = HIWORD( lpMF->lParam );\r
6503 \r
6504                 if(lpMF->msg == WM_CHAR) {\r
6505                         CHARRANGE sel;\r
6506                         SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );\r
6507                         index = sel.cpMin;\r
6508                 } else\r
6509                 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
6510 \r
6511                 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
6512                 len = GetWindowTextLength(hwndText);\r
6513                 str = (char *) malloc(len + 1);\r
6514                 GetWindowText(hwndText, str, len + 1);\r
6515                 ReplaceComment(commentIndex, str);\r
6516                 if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
6517                 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
6518                 free(str);\r
6519 \r
6520                 /* Zap the message for good: apparently, returning non-zero is not enough */\r
6521                 lpMF->msg = WM_USER;\r
6522 \r
6523                 return TRUE;\r
6524             }\r
6525         }\r
6526         break;\r
6527 \r
6528   case WM_SIZE:\r
6529     newSizeX = LOWORD(lParam);\r
6530     newSizeY = HIWORD(lParam);\r
6531     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6532     sizeX = newSizeX;\r
6533     sizeY = newSizeY;\r
6534     break;\r
6535 \r
6536   case WM_GETMINMAXINFO:\r
6537     /* Prevent resizing window too small */\r
6538     mmi = (MINMAXINFO *) lParam;\r
6539     mmi->ptMinTrackSize.x = 100;\r
6540     mmi->ptMinTrackSize.y = 100;\r
6541     break;\r
6542   }\r
6543   return FALSE;\r
6544 }\r
6545 \r
6546 VOID\r
6547 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6548 {\r
6549   FARPROC lpProc;\r
6550   char *p, *q;\r
6551 \r
6552   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6553 \r
6554   if (str == NULL) str = "";\r
6555   p = (char *) malloc(2 * strlen(str) + 2);\r
6556   q = p;\r
6557   while (*str) {\r
6558     if (*str == '\n') *q++ = '\r';\r
6559     *q++ = *str++;\r
6560   }\r
6561   *q = NULLCHAR;\r
6562   if (commentText != NULL) free(commentText);\r
6563 \r
6564   commentIndex = index;\r
6565   commentTitle = title;\r
6566   commentText = p;\r
6567   editComment = edit;\r
6568 \r
6569   if (commentDialog) {\r
6570     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6571     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6572   } else {\r
6573     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6574     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6575                  hwndMain, (DLGPROC)lpProc);\r
6576     FreeProcInstance(lpProc);\r
6577   }\r
6578   commentUp = TRUE;\r
6579 }\r
6580 \r
6581 \r
6582 /*---------------------------------------------------------------------------*\\r
6583  *\r
6584  * Type-in move dialog functions\r
6585  * \r
6586 \*---------------------------------------------------------------------------*/\r
6587 \r
6588 LRESULT CALLBACK\r
6589 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6590 {\r
6591   char move[MSG_SIZ];\r
6592   HWND hInput;\r
6593 \r
6594   switch (message) {\r
6595   case WM_INITDIALOG:\r
6596     move[0] = (char) lParam;\r
6597     move[1] = NULLCHAR;\r
6598     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6599     Translate(hDlg, DLG_TypeInMove);\r
6600     hInput = GetDlgItem(hDlg, OPT_Move);\r
6601     SetWindowText(hInput, move);\r
6602     SetFocus(hInput);\r
6603     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6604     return FALSE;\r
6605 \r
6606   case WM_COMMAND:\r
6607     switch (LOWORD(wParam)) {\r
6608     case IDOK:\r
6609 \r
6610       shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status\r
6611       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6612       TypeInDoneEvent(move);\r
6613       EndDialog(hDlg, TRUE);\r
6614       return TRUE;\r
6615     case IDCANCEL:\r
6616       EndDialog(hDlg, FALSE);\r
6617       return TRUE;\r
6618     default:\r
6619       break;\r
6620     }\r
6621     break;\r
6622   }\r
6623   return FALSE;\r
6624 }\r
6625 \r
6626 VOID\r
6627 PopUpMoveDialog(char firstchar)\r
6628 {\r
6629     FARPROC lpProc;\r
6630 \r
6631       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6632       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6633         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6634       FreeProcInstance(lpProc);\r
6635 }\r
6636 \r
6637 /*---------------------------------------------------------------------------*\\r
6638  *\r
6639  * Type-in name dialog functions\r
6640  * \r
6641 \*---------------------------------------------------------------------------*/\r
6642 \r
6643 LRESULT CALLBACK\r
6644 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6645 {\r
6646   char move[MSG_SIZ];\r
6647   HWND hInput;\r
6648 \r
6649   switch (message) {\r
6650   case WM_INITDIALOG:\r
6651     move[0] = (char) lParam;\r
6652     move[1] = NULLCHAR;\r
6653     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6654     Translate(hDlg, DLG_TypeInName);\r
6655     hInput = GetDlgItem(hDlg, OPT_Name);\r
6656     SetWindowText(hInput, move);\r
6657     SetFocus(hInput);\r
6658     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6659     return FALSE;\r
6660 \r
6661   case WM_COMMAND:\r
6662     switch (LOWORD(wParam)) {\r
6663     case IDOK:\r
6664       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
6665       appData.userName = strdup(move);\r
6666       SetUserLogo();\r
6667       SetGameInfo();\r
6668       if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
6669         snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
6670         DisplayTitle(move);\r
6671       }\r
6672 \r
6673 \r
6674       EndDialog(hDlg, TRUE);\r
6675       return TRUE;\r
6676     case IDCANCEL:\r
6677       EndDialog(hDlg, FALSE);\r
6678       return TRUE;\r
6679     default:\r
6680       break;\r
6681     }\r
6682     break;\r
6683   }\r
6684   return FALSE;\r
6685 }\r
6686 \r
6687 VOID\r
6688 PopUpNameDialog(char firstchar)\r
6689 {\r
6690     FARPROC lpProc;\r
6691     \r
6692       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
6693       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
6694         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6695       FreeProcInstance(lpProc);\r
6696 }\r
6697 \r
6698 /*---------------------------------------------------------------------------*\\r
6699  *\r
6700  *  Error dialogs\r
6701  * \r
6702 \*---------------------------------------------------------------------------*/\r
6703 \r
6704 /* Nonmodal error box */\r
6705 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6706                              WPARAM wParam, LPARAM lParam);\r
6707 \r
6708 VOID\r
6709 ErrorPopUp(char *title, char *content)\r
6710 {\r
6711   FARPROC lpProc;\r
6712   char *p, *q;\r
6713   BOOLEAN modal = hwndMain == NULL;\r
6714 \r
6715   p = content;\r
6716   q = errorMessage;\r
6717   while (*p) {\r
6718     if (*p == '\n') {\r
6719       if (modal) {\r
6720         *q++ = ' ';\r
6721         p++;\r
6722       } else {\r
6723         *q++ = '\r';\r
6724         *q++ = *p++;\r
6725       }\r
6726     } else {\r
6727       *q++ = *p++;\r
6728     }\r
6729   }\r
6730   *q = NULLCHAR;\r
6731   strncpy(errorTitle, title, sizeof(errorTitle));\r
6732   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6733   \r
6734   if (modal) {\r
6735     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6736   } else {\r
6737     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6738     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6739                  hwndMain, (DLGPROC)lpProc);\r
6740     FreeProcInstance(lpProc);\r
6741   }\r
6742 }\r
6743 \r
6744 VOID\r
6745 ErrorPopDown()\r
6746 {\r
6747   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6748   if (errorDialog == NULL) return;\r
6749   DestroyWindow(errorDialog);\r
6750   errorDialog = NULL;\r
6751   if(errorExitStatus) ExitEvent(errorExitStatus);\r
6752 }\r
6753 \r
6754 LRESULT CALLBACK\r
6755 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6756 {\r
6757   HANDLE hwndText;\r
6758   RECT rChild;\r
6759 \r
6760   switch (message) {\r
6761   case WM_INITDIALOG:\r
6762     GetWindowRect(hDlg, &rChild);\r
6763 \r
6764     /*\r
6765     SetWindowPos(hDlg, NULL, rChild.left,\r
6766       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6767       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6768     */\r
6769 \r
6770     /* \r
6771         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6772         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6773         and it doesn't work when you resize the dialog.\r
6774         For now, just give it a default position.\r
6775     */\r
6776     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6777     Translate(hDlg, DLG_Error);\r
6778 \r
6779     errorDialog = hDlg;\r
6780     SetWindowText(hDlg, errorTitle);\r
6781     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6782     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6783     return FALSE;\r
6784 \r
6785   case WM_COMMAND:\r
6786     switch (LOWORD(wParam)) {\r
6787     case IDOK:\r
6788     case IDCANCEL:\r
6789       if (errorDialog == hDlg) errorDialog = NULL;\r
6790       DestroyWindow(hDlg);\r
6791       return TRUE;\r
6792 \r
6793     default:\r
6794       break;\r
6795     }\r
6796     break;\r
6797   }\r
6798   return FALSE;\r
6799 }\r
6800 \r
6801 #ifdef GOTHIC\r
6802 HWND gothicDialog = NULL;\r
6803 \r
6804 LRESULT CALLBACK\r
6805 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6806 {\r
6807   HANDLE hwndText;\r
6808   RECT rChild;\r
6809   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6810 \r
6811   switch (message) {\r
6812   case WM_INITDIALOG:\r
6813     GetWindowRect(hDlg, &rChild);\r
6814 \r
6815     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
6816                                                              SWP_NOZORDER);\r
6817 \r
6818     /* \r
6819         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6820         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6821         and it doesn't work when you resize the dialog.\r
6822         For now, just give it a default position.\r
6823     */\r
6824     gothicDialog = hDlg;\r
6825     SetWindowText(hDlg, errorTitle);\r
6826     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6827     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6828     return FALSE;\r
6829 \r
6830   case WM_COMMAND:\r
6831     switch (LOWORD(wParam)) {\r
6832     case IDOK:\r
6833     case IDCANCEL:\r
6834       if (errorDialog == hDlg) errorDialog = NULL;\r
6835       DestroyWindow(hDlg);\r
6836       return TRUE;\r
6837 \r
6838     default:\r
6839       break;\r
6840     }\r
6841     break;\r
6842   }\r
6843   return FALSE;\r
6844 }\r
6845 \r
6846 VOID\r
6847 GothicPopUp(char *title, VariantClass variant)\r
6848 {\r
6849   FARPROC lpProc;\r
6850   static char *lastTitle;\r
6851 \r
6852   strncpy(errorTitle, title, sizeof(errorTitle));\r
6853   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6854 \r
6855   if(lastTitle != title && gothicDialog != NULL) {\r
6856     DestroyWindow(gothicDialog);\r
6857     gothicDialog = NULL;\r
6858   }\r
6859   if(variant != VariantNormal && gothicDialog == NULL) {\r
6860     title = lastTitle;\r
6861     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6862     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6863                  hwndMain, (DLGPROC)lpProc);\r
6864     FreeProcInstance(lpProc);\r
6865   }\r
6866 }\r
6867 #endif\r
6868 \r
6869 /*---------------------------------------------------------------------------*\\r
6870  *\r
6871  *  Ics Interaction console functions\r
6872  *\r
6873 \*---------------------------------------------------------------------------*/\r
6874 \r
6875 #define HISTORY_SIZE 64\r
6876 static char *history[HISTORY_SIZE];\r
6877 int histIn = 0, histP = 0;\r
6878 \r
6879 \r
6880 VOID\r
6881 SaveInHistory(char *cmd)\r
6882 {\r
6883   if (history[histIn] != NULL) {\r
6884     free(history[histIn]);\r
6885     history[histIn] = NULL;\r
6886   }\r
6887   if (*cmd == NULLCHAR) return;\r
6888   history[histIn] = StrSave(cmd);\r
6889   histIn = (histIn + 1) % HISTORY_SIZE;\r
6890   if (history[histIn] != NULL) {\r
6891     free(history[histIn]);\r
6892 \r
6893     history[histIn] = NULL;\r
6894   }\r
6895   histP = histIn;\r
6896 }\r
6897 \r
6898 char *\r
6899 PrevInHistory(char *cmd)\r
6900 {\r
6901   int newhp;\r
6902   if (histP == histIn) {\r
6903     if (history[histIn] != NULL) free(history[histIn]);\r
6904     history[histIn] = StrSave(cmd);\r
6905   }\r
6906   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6907   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6908   histP = newhp;\r
6909   return history[histP];\r
6910 }\r
6911 \r
6912 char *\r
6913 NextInHistory()\r
6914 {\r
6915   if (histP == histIn) return NULL;\r
6916   histP = (histP + 1) % HISTORY_SIZE;\r
6917   return history[histP];   \r
6918 }\r
6919 \r
6920 HMENU\r
6921 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6922 {\r
6923   HMENU hmenu, h;\r
6924   int i = 0;\r
6925   hmenu = LoadMenu(hInst, "TextMenu");\r
6926   h = GetSubMenu(hmenu, 0);\r
6927   while (e->item) {\r
6928     if (strcmp(e->item, "-") == 0) {\r
6929       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6930     } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
6931       int flags = MF_STRING, j = 0;\r
6932       if (e->item[0] == '|') {\r
6933         flags |= MF_MENUBARBREAK;\r
6934         j++;\r
6935       }\r
6936       if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
6937       AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
6938     }\r
6939     e++;\r
6940     i++;\r
6941   } \r
6942   return hmenu;\r
6943 }\r
6944 \r
6945 WNDPROC consoleTextWindowProc;\r
6946 \r
6947 void\r
6948 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6949 {\r
6950   char buf[MSG_SIZ], name[MSG_SIZ];\r
6951   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6952   CHARRANGE sel;\r
6953 \r
6954   if (!getname) {\r
6955     SetWindowText(hInput, command);\r
6956     if (immediate) {\r
6957       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6958     } else {\r
6959       sel.cpMin = 999999;\r
6960       sel.cpMax = 999999;\r
6961       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6962       SetFocus(hInput);\r
6963     }\r
6964     return;\r
6965   }    \r
6966   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6967   if (sel.cpMin == sel.cpMax) {\r
6968     /* Expand to surrounding word */\r
6969     TEXTRANGE tr;\r
6970     do {\r
6971       tr.chrg.cpMax = sel.cpMin;\r
6972       tr.chrg.cpMin = --sel.cpMin;\r
6973       if (sel.cpMin < 0) break;\r
6974       tr.lpstrText = name;\r
6975       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6976     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6977     sel.cpMin++;\r
6978 \r
6979     do {\r
6980       tr.chrg.cpMin = sel.cpMax;\r
6981       tr.chrg.cpMax = ++sel.cpMax;\r
6982       tr.lpstrText = name;\r
6983       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6984     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6985     sel.cpMax--;\r
6986 \r
6987     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6988       MessageBeep(MB_ICONEXCLAMATION);\r
6989       return;\r
6990     }\r
6991     tr.chrg = sel;\r
6992     tr.lpstrText = name;\r
6993     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6994   } else {\r
6995     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6996       MessageBeep(MB_ICONEXCLAMATION);\r
6997       return;\r
6998     }\r
6999     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7000   }\r
7001   if (immediate) {\r
7002     if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else\r
7003     snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
7004     SetWindowText(hInput, buf);\r
7005     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7006   } else {\r
7007     if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
7008       snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
7009     SetWindowText(hInput, buf);\r
7010     sel.cpMin = 999999;\r
7011     sel.cpMax = 999999;\r
7012     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7013     SetFocus(hInput);\r
7014   }\r
7015 }\r
7016 \r
7017 LRESULT CALLBACK \r
7018 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7019 {\r
7020   HWND hInput;\r
7021   CHARRANGE sel;\r
7022 \r
7023   switch (message) {\r
7024   case WM_KEYDOWN:\r
7025     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7026     if(wParam=='R') return 0;\r
7027     switch (wParam) {\r
7028     case VK_PRIOR:\r
7029       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7030       return 0;\r
7031     case VK_NEXT:\r
7032       sel.cpMin = 999999;\r
7033       sel.cpMax = 999999;\r
7034       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7035       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7036       return 0;\r
7037     }\r
7038     break;\r
7039   case WM_CHAR:\r
7040    if(wParam != '\022') {\r
7041     if (wParam == '\t') {\r
7042       if (GetKeyState(VK_SHIFT) < 0) {\r
7043         /* shifted */\r
7044         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7045         if (buttonDesc[0].hwnd) {\r
7046           SetFocus(buttonDesc[0].hwnd);\r
7047         } else {\r
7048           SetFocus(hwndMain);\r
7049         }\r
7050       } else {\r
7051         /* unshifted */\r
7052         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7053       }\r
7054     } else {\r
7055       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7056       JAWS_DELETE( SetFocus(hInput); )\r
7057       SendMessage(hInput, message, wParam, lParam);\r
7058     }\r
7059     return 0;\r
7060    } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu\r
7061    lParam = -1;\r
7062   case WM_RBUTTONDOWN:\r
7063     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7064       /* Move selection here if it was empty */\r
7065       POINT pt;\r
7066       pt.x = LOWORD(lParam);\r
7067       pt.y = HIWORD(lParam);\r
7068       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7069       if (sel.cpMin == sel.cpMax) {\r
7070         if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7071         sel.cpMax = sel.cpMin;\r
7072         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7073       }\r
7074       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7075 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
7076       POINT pt;\r
7077       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7078       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7079       if (sel.cpMin == sel.cpMax) {\r
7080         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7081         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7082       }\r
7083       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7084         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7085       }\r
7086       pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
7087       pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
7088       PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
7089       MenuPopup(hwnd, pt, hmenu, -1);\r
7090 }\r
7091     }\r
7092     return 0;\r
7093   case WM_RBUTTONUP:\r
7094     if (GetKeyState(VK_SHIFT) & ~1) {\r
7095       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7096         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7097     }\r
7098     return 0;\r
7099   case WM_PASTE:\r
7100     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7101     SetFocus(hInput);\r
7102     return SendMessage(hInput, message, wParam, lParam);\r
7103   case WM_MBUTTONDOWN:\r
7104     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7105   case WM_COMMAND:\r
7106     switch (LOWORD(wParam)) {\r
7107     case IDM_QuickPaste:\r
7108       {\r
7109         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7110         if (sel.cpMin == sel.cpMax) {\r
7111           MessageBeep(MB_ICONEXCLAMATION);\r
7112           return 0;\r
7113         }\r
7114         SendMessage(hwnd, WM_COPY, 0, 0);\r
7115         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7116         SendMessage(hInput, WM_PASTE, 0, 0);\r
7117         SetFocus(hInput);\r
7118         return 0;\r
7119       }\r
7120     case IDM_Cut:\r
7121       SendMessage(hwnd, WM_CUT, 0, 0);\r
7122       return 0;\r
7123     case IDM_Paste:\r
7124       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7125       return 0;\r
7126     case IDM_Copy:\r
7127       SendMessage(hwnd, WM_COPY, 0, 0);\r
7128       return 0;\r
7129     default:\r
7130       {\r
7131         int i = LOWORD(wParam) - IDM_CommandX;\r
7132         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7133             icsTextMenuEntry[i].command != NULL) {\r
7134           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7135                    icsTextMenuEntry[i].getname,\r
7136                    icsTextMenuEntry[i].immediate);\r
7137           return 0;\r
7138         }\r
7139       }\r
7140       break;\r
7141     }\r
7142     break;\r
7143   }\r
7144   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7145 }\r
7146 \r
7147 WNDPROC consoleInputWindowProc;\r
7148 \r
7149 LRESULT CALLBACK\r
7150 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7151 {\r
7152   char buf[MSG_SIZ];\r
7153   char *p;\r
7154   static BOOL sendNextChar = FALSE;\r
7155   static BOOL quoteNextChar = FALSE;\r
7156   InputSource *is = consoleInputSource;\r
7157   CHARFORMAT cf;\r
7158   CHARRANGE sel;\r
7159 \r
7160   switch (message) {\r
7161   case WM_CHAR:\r
7162     if (!appData.localLineEditing || sendNextChar) {\r
7163       is->buf[0] = (CHAR) wParam;\r
7164       is->count = 1;\r
7165       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7166       sendNextChar = FALSE;\r
7167       return 0;\r
7168     }\r
7169     if (quoteNextChar) {\r
7170       buf[0] = (char) wParam;\r
7171       buf[1] = NULLCHAR;\r
7172       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7173       quoteNextChar = FALSE;\r
7174       return 0;\r
7175     }\r
7176     switch (wParam) {\r
7177     case '\r':   /* Enter key */\r
7178       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7179       if (consoleEcho) SaveInHistory(is->buf);\r
7180       is->buf[is->count++] = '\n';\r
7181       is->buf[is->count] = NULLCHAR;\r
7182       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7183       if (consoleEcho) {\r
7184         ConsoleOutput(is->buf, is->count, TRUE);\r
7185       } else if (appData.localLineEditing) {\r
7186         ConsoleOutput("\n", 1, TRUE);\r
7187       }\r
7188       /* fall thru */\r
7189     case '\033': /* Escape key */\r
7190       SetWindowText(hwnd, "");\r
7191       cf.cbSize = sizeof(CHARFORMAT);\r
7192       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7193       if (consoleEcho) {\r
7194         cf.crTextColor = textAttribs[ColorNormal].color;\r
7195       } else {\r
7196         cf.crTextColor = COLOR_ECHOOFF;\r
7197       }\r
7198       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7199       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7200       return 0;\r
7201     case '\t':   /* Tab key */\r
7202       if (GetKeyState(VK_SHIFT) < 0) {\r
7203         /* shifted */\r
7204         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7205       } else {\r
7206         /* unshifted */\r
7207         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7208         if (buttonDesc[0].hwnd) {\r
7209           SetFocus(buttonDesc[0].hwnd);\r
7210         } else {\r
7211           SetFocus(hwndMain);\r
7212         }\r
7213       }\r
7214       return 0;\r
7215     case '\023': /* Ctrl+S */\r
7216       sendNextChar = TRUE;\r
7217       return 0;\r
7218     case '\021': /* Ctrl+Q */\r
7219       quoteNextChar = TRUE;\r
7220       return 0;\r
7221     JAWS_REPLAY\r
7222     default:\r
7223       break;\r
7224     }\r
7225     break;\r
7226   case WM_KEYDOWN:\r
7227     switch (wParam) {\r
7228     case VK_UP:\r
7229       GetWindowText(hwnd, buf, MSG_SIZ);\r
7230       p = PrevInHistory(buf);\r
7231       if (p != NULL) {\r
7232         SetWindowText(hwnd, p);\r
7233         sel.cpMin = 999999;\r
7234         sel.cpMax = 999999;\r
7235         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7236         return 0;\r
7237       }\r
7238       break;\r
7239     case VK_DOWN:\r
7240       p = NextInHistory();\r
7241       if (p != NULL) {\r
7242         SetWindowText(hwnd, p);\r
7243         sel.cpMin = 999999;\r
7244         sel.cpMax = 999999;\r
7245         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7246         return 0;\r
7247       }\r
7248       break;\r
7249     case VK_HOME:\r
7250     case VK_END:\r
7251       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7252       /* fall thru */\r
7253     case VK_PRIOR:\r
7254     case VK_NEXT:\r
7255       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7256       return 0;\r
7257     }\r
7258     break;\r
7259   case WM_MBUTTONDOWN:\r
7260     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7261       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7262     break;\r
7263   case WM_RBUTTONUP:\r
7264     if (GetKeyState(VK_SHIFT) & ~1) {\r
7265       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7266         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7267     } else {\r
7268       POINT pt;\r
7269       HMENU hmenu;\r
7270       hmenu = LoadMenu(hInst, "InputMenu");\r
7271       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7272       if (sel.cpMin == sel.cpMax) {\r
7273         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7274         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7275       }\r
7276       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7277         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7278       }\r
7279       pt.x = LOWORD(lParam);\r
7280       pt.y = HIWORD(lParam);\r
7281       MenuPopup(hwnd, pt, hmenu, -1);\r
7282     }\r
7283     return 0;\r
7284   case WM_COMMAND:\r
7285     switch (LOWORD(wParam)) { \r
7286     case IDM_Undo:\r
7287       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7288       return 0;\r
7289     case IDM_SelectAll:\r
7290       sel.cpMin = 0;\r
7291       sel.cpMax = -1; /*999999?*/\r
7292       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7293       return 0;\r
7294     case IDM_Cut:\r
7295       SendMessage(hwnd, WM_CUT, 0, 0);\r
7296       return 0;\r
7297     case IDM_Paste:\r
7298       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7299       return 0;\r
7300     case IDM_Copy:\r
7301       SendMessage(hwnd, WM_COPY, 0, 0);\r
7302       return 0;\r
7303     }\r
7304     break;\r
7305   }\r
7306   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7307 }\r
7308 \r
7309 #define CO_MAX  100000\r
7310 #define CO_TRIM   1000\r
7311 \r
7312 LRESULT CALLBACK\r
7313 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7314 {\r
7315   static SnapData sd;\r
7316   HWND hText, hInput;\r
7317   RECT rect;\r
7318   static int sizeX, sizeY;\r
7319   int newSizeX, newSizeY;\r
7320   MINMAXINFO *mmi;\r
7321   WORD wMask;\r
7322 \r
7323   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7324   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7325 \r
7326   switch (message) {\r
7327   case WM_NOTIFY:\r
7328     if (((NMHDR*)lParam)->code == EN_LINK)\r
7329     {\r
7330       ENLINK *pLink = (ENLINK*)lParam;\r
7331       if (pLink->msg == WM_LBUTTONUP)\r
7332       {\r
7333         TEXTRANGE tr;\r
7334 \r
7335         tr.chrg = pLink->chrg;\r
7336         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7337         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7338         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7339         free(tr.lpstrText);\r
7340       }\r
7341     }\r
7342     break;\r
7343   case WM_INITDIALOG: /* message: initialize dialog box */\r
7344     hwndConsole = hDlg;\r
7345     SetFocus(hInput);\r
7346     consoleTextWindowProc = (WNDPROC)\r
7347       SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);\r
7348     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7349     consoleInputWindowProc = (WNDPROC)\r
7350       SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);\r
7351     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7352     Colorize(ColorNormal, TRUE);\r
7353     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7354     ChangedConsoleFont();\r
7355     GetClientRect(hDlg, &rect);\r
7356     sizeX = rect.right;\r
7357     sizeY = rect.bottom;\r
7358     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7359         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7360       WINDOWPLACEMENT wp;\r
7361       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7362       wp.length = sizeof(WINDOWPLACEMENT);\r
7363       wp.flags = 0;\r
7364       wp.showCmd = SW_SHOW;\r
7365       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7366       wp.rcNormalPosition.left = wpConsole.x;\r
7367       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7368       wp.rcNormalPosition.top = wpConsole.y;\r
7369       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7370       SetWindowPlacement(hDlg, &wp);\r
7371     }\r
7372 \r
7373    // [HGM] Chessknight's change 2004-07-13\r
7374    else { /* Determine Defaults */\r
7375        WINDOWPLACEMENT wp;\r
7376        wpConsole.x = wpMain.width + 1;\r
7377        wpConsole.y = wpMain.y;\r
7378        wpConsole.width = screenWidth -  wpMain.width;\r
7379        wpConsole.height = wpMain.height;\r
7380        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7381        wp.length = sizeof(WINDOWPLACEMENT);\r
7382        wp.flags = 0;\r
7383        wp.showCmd = SW_SHOW;\r
7384        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7385        wp.rcNormalPosition.left = wpConsole.x;\r
7386        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7387        wp.rcNormalPosition.top = wpConsole.y;\r
7388        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7389        SetWindowPlacement(hDlg, &wp);\r
7390     }\r
7391 \r
7392    // Allow hText to highlight URLs and send notifications on them\r
7393    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7394    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7395    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7396    SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width\r
7397 \r
7398     return FALSE;\r
7399 \r
7400   case WM_SETFOCUS:\r
7401     SetFocus(hInput);\r
7402     return 0;\r
7403 \r
7404   case WM_CLOSE:\r
7405     ExitEvent(0);\r
7406     /* not reached */\r
7407     break;\r
7408 \r
7409   case WM_SIZE:\r
7410     if (IsIconic(hDlg)) break;\r
7411     newSizeX = LOWORD(lParam);\r
7412     newSizeY = HIWORD(lParam);\r
7413     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7414       RECT rectText, rectInput;\r
7415       POINT pt;\r
7416       int newTextHeight, newTextWidth;\r
7417       GetWindowRect(hText, &rectText);\r
7418       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7419       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7420       if (newTextHeight < 0) {\r
7421         newSizeY += -newTextHeight;\r
7422         newTextHeight = 0;\r
7423       }\r
7424       SetWindowPos(hText, NULL, 0, 0,\r
7425         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7426       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7427       pt.x = rectInput.left;\r
7428       pt.y = rectInput.top + newSizeY - sizeY;\r
7429       ScreenToClient(hDlg, &pt);\r
7430       SetWindowPos(hInput, NULL, \r
7431         pt.x, pt.y, /* needs client coords */   \r
7432         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7433         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7434     }\r
7435     sizeX = newSizeX;\r
7436     sizeY = newSizeY;\r
7437     break;\r
7438 \r
7439   case WM_GETMINMAXINFO:\r
7440     /* Prevent resizing window too small */\r
7441     mmi = (MINMAXINFO *) lParam;\r
7442     mmi->ptMinTrackSize.x = 100;\r
7443     mmi->ptMinTrackSize.y = 100;\r
7444     break;\r
7445 \r
7446   /* [AS] Snapping */\r
7447   case WM_ENTERSIZEMOVE:\r
7448     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7449 \r
7450   case WM_SIZING:\r
7451     return OnSizing( &sd, hDlg, wParam, lParam );\r
7452 \r
7453   case WM_MOVING:\r
7454     return OnMoving( &sd, hDlg, wParam, lParam );\r
7455 \r
7456   case WM_EXITSIZEMOVE:\r
7457         UpdateICSWidth(hText);\r
7458     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7459   }\r
7460 \r
7461   return DefWindowProc(hDlg, message, wParam, lParam);\r
7462 }\r
7463 \r
7464 \r
7465 VOID\r
7466 ConsoleCreate()\r
7467 {\r
7468   HWND hCons;\r
7469   if (hwndConsole) return;\r
7470   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7471   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7472 }\r
7473 \r
7474 \r
7475 VOID\r
7476 ConsoleOutput(char* data, int length, int forceVisible)\r
7477 {\r
7478   HWND hText;\r
7479   int trim, exlen;\r
7480   char *p, *q;\r
7481   char buf[CO_MAX+1];\r
7482   POINT pEnd;\r
7483   RECT rect;\r
7484   static int delayLF = 0;\r
7485   CHARRANGE savesel, sel;\r
7486 \r
7487   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7488   p = data;\r
7489   q = buf;\r
7490   if (delayLF) {\r
7491     *q++ = '\r';\r
7492     *q++ = '\n';\r
7493     delayLF = 0;\r
7494   }\r
7495   while (length--) {\r
7496     if (*p == '\n') {\r
7497       if (*++p) {\r
7498         *q++ = '\r';\r
7499         *q++ = '\n';\r
7500       } else {\r
7501         delayLF = 1;\r
7502       }\r
7503     } else if (*p == '\007') {\r
7504        MyPlaySound(&sounds[(int)SoundBell]);\r
7505        p++;\r
7506     } else {\r
7507       *q++ = *p++;\r
7508     }\r
7509   }\r
7510   *q = NULLCHAR;\r
7511   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7512   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7513   /* Save current selection */\r
7514   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7515   exlen = GetWindowTextLength(hText);\r
7516   /* Find out whether current end of text is visible */\r
7517   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7518   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7519   /* Trim existing text if it's too long */\r
7520   if (exlen + (q - buf) > CO_MAX) {\r
7521     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7522     sel.cpMin = 0;\r
7523     sel.cpMax = trim;\r
7524     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7525     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7526     exlen -= trim;\r
7527     savesel.cpMin -= trim;\r
7528     savesel.cpMax -= trim;\r
7529     if (exlen < 0) exlen = 0;\r
7530     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7531     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7532   }\r
7533   /* Append the new text */\r
7534   sel.cpMin = exlen;\r
7535   sel.cpMax = exlen;\r
7536   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7537   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7538   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7539   if (forceVisible || exlen == 0 ||\r
7540       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7541        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7542     /* Scroll to make new end of text visible if old end of text\r
7543        was visible or new text is an echo of user typein */\r
7544     sel.cpMin = 9999999;\r
7545     sel.cpMax = 9999999;\r
7546     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7547     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7548     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7549     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7550   }\r
7551   if (savesel.cpMax == exlen || forceVisible) {\r
7552     /* Move insert point to new end of text if it was at the old\r
7553        end of text or if the new text is an echo of user typein */\r
7554     sel.cpMin = 9999999;\r
7555     sel.cpMax = 9999999;\r
7556     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7557   } else {\r
7558     /* Restore previous selection */\r
7559     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7560   }\r
7561   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7562 }\r
7563 \r
7564 /*---------*/\r
7565 \r
7566 \r
7567 void\r
7568 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7569 {\r
7570   char buf[100];\r
7571   char *str;\r
7572   COLORREF oldFg, oldBg;\r
7573   HFONT oldFont;\r
7574   RECT rect;\r
7575 \r
7576   if(copyNumber > 1)\r
7577     snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
7578 \r
7579   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7580   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7581   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7582 \r
7583   rect.left = x;\r
7584   rect.right = x + squareSize;\r
7585   rect.top  = y;\r
7586   rect.bottom = y + squareSize;\r
7587   str = buf;\r
7588 \r
7589   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7590                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7591              y, ETO_CLIPPED|ETO_OPAQUE,\r
7592              &rect, str, strlen(str), NULL);\r
7593 \r
7594   (void) SetTextColor(hdc, oldFg);\r
7595   (void) SetBkColor(hdc, oldBg);\r
7596   (void) SelectObject(hdc, oldFont);\r
7597 }\r
7598 \r
7599 void\r
7600 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7601               RECT *rect, char *color, char *flagFell)\r
7602 {\r
7603   char buf[100];\r
7604   char *str;\r
7605   COLORREF oldFg, oldBg;\r
7606   HFONT oldFont;\r
7607 \r
7608   if (twoBoards && partnerUp) return;\r
7609   if (appData.clockMode) {\r
7610     if (tinyLayout)\r
7611       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7612     else\r
7613       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
7614     str = buf;\r
7615   } else {\r
7616     str = color;\r
7617   }\r
7618 \r
7619   if (highlight) {\r
7620     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7621     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7622   } else {\r
7623     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7624     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7625   }\r
7626   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7627 \r
7628   JAWS_SILENCE\r
7629 \r
7630   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7631              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7632              rect, str, strlen(str), NULL);\r
7633   if(logoHeight > 0 && appData.clockMode) {\r
7634       RECT r;\r
7635       str += strlen(color)+2;\r
7636       r.top = rect->top + logoHeight/2;\r
7637       r.left = rect->left;\r
7638       r.right = rect->right;\r
7639       r.bottom = rect->bottom;\r
7640       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7641                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
7642                  &r, str, strlen(str), NULL);\r
7643   }\r
7644   (void) SetTextColor(hdc, oldFg);\r
7645   (void) SetBkColor(hdc, oldBg);\r
7646   (void) SelectObject(hdc, oldFont);\r
7647 }\r
7648 \r
7649 \r
7650 int\r
7651 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7652            OVERLAPPED *ovl)\r
7653 {\r
7654   int ok, err;\r
7655 \r
7656   /* [AS]  */\r
7657   if( count <= 0 ) {\r
7658     if (appData.debugMode) {\r
7659       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7660     }\r
7661 \r
7662     return ERROR_INVALID_USER_BUFFER;\r
7663   }\r
7664 \r
7665   ResetEvent(ovl->hEvent);\r
7666   ovl->Offset = ovl->OffsetHigh = 0;\r
7667   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7668   if (ok) {\r
7669     err = NO_ERROR;\r
7670   } else {\r
7671     err = GetLastError();\r
7672     if (err == ERROR_IO_PENDING) {\r
7673       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7674       if (ok)\r
7675         err = NO_ERROR;\r
7676       else\r
7677         err = GetLastError();\r
7678     }\r
7679   }\r
7680   return err;\r
7681 }\r
7682 \r
7683 int\r
7684 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7685             OVERLAPPED *ovl)\r
7686 {\r
7687   int ok, err;\r
7688 \r
7689   ResetEvent(ovl->hEvent);\r
7690   ovl->Offset = ovl->OffsetHigh = 0;\r
7691   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7692   if (ok) {\r
7693     err = NO_ERROR;\r
7694   } else {\r
7695     err = GetLastError();\r
7696     if (err == ERROR_IO_PENDING) {\r
7697       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7698       if (ok)\r
7699         err = NO_ERROR;\r
7700       else\r
7701         err = GetLastError();\r
7702     }\r
7703 \r
7704   }\r
7705   return err;\r
7706 }\r
7707 \r
7708 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7709 void CheckForInputBufferFull( InputSource * is )\r
7710 {\r
7711     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7712         /* Look for end of line */\r
7713         char * p = is->buf;\r
7714         \r
7715         while( p < is->next && *p != '\n' ) {\r
7716             p++;\r
7717         }\r
7718 \r
7719         if( p >= is->next ) {\r
7720             if (appData.debugMode) {\r
7721                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
7722             }\r
7723 \r
7724             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7725             is->count = (DWORD) -1;\r
7726             is->next = is->buf;\r
7727         }\r
7728     }\r
7729 }\r
7730 \r
7731 DWORD\r
7732 InputThread(LPVOID arg)\r
7733 {\r
7734   InputSource *is;\r
7735   OVERLAPPED ovl;\r
7736 \r
7737   is = (InputSource *) arg;\r
7738   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7739   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7740   while (is->hThread != NULL) {\r
7741     is->error = DoReadFile(is->hFile, is->next,\r
7742                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7743                            &is->count, &ovl);\r
7744     if (is->error == NO_ERROR) {\r
7745       is->next += is->count;\r
7746     } else {\r
7747       if (is->error == ERROR_BROKEN_PIPE) {\r
7748         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7749         is->count = 0;\r
7750       } else {\r
7751         is->count = (DWORD) -1;\r
7752         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7753         break; \r
7754       }\r
7755     }\r
7756 \r
7757     CheckForInputBufferFull( is );\r
7758 \r
7759     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7760 \r
7761     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7762 \r
7763     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7764   }\r
7765 \r
7766   CloseHandle(ovl.hEvent);\r
7767   CloseHandle(is->hFile);\r
7768 \r
7769   if (appData.debugMode) {\r
7770     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
7771   }\r
7772 \r
7773   return 0;\r
7774 }\r
7775 \r
7776 \r
7777 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7778 DWORD\r
7779 NonOvlInputThread(LPVOID arg)\r
7780 {\r
7781   InputSource *is;\r
7782   char *p, *q;\r
7783   int i;\r
7784   char prev;\r
7785 \r
7786   is = (InputSource *) arg;\r
7787   while (is->hThread != NULL) {\r
7788     is->error = ReadFile(is->hFile, is->next,\r
7789                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7790                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7791     if (is->error == NO_ERROR) {\r
7792       /* Change CRLF to LF */\r
7793       if (is->next > is->buf) {\r
7794         p = is->next - 1;\r
7795         i = is->count + 1;\r
7796       } else {\r
7797         p = is->next;\r
7798         i = is->count;\r
7799       }\r
7800       q = p;\r
7801       prev = NULLCHAR;\r
7802       while (i > 0) {\r
7803         if (prev == '\r' && *p == '\n') {\r
7804           *(q-1) = '\n';\r
7805           is->count--;\r
7806         } else { \r
7807           *q++ = *p;\r
7808         }\r
7809         prev = *p++;\r
7810         i--;\r
7811       }\r
7812       *q = NULLCHAR;\r
7813       is->next = q;\r
7814     } else {\r
7815       if (is->error == ERROR_BROKEN_PIPE) {\r
7816         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7817         is->count = 0; \r
7818       } else {\r
7819         is->count = (DWORD) -1;\r
7820       }\r
7821     }\r
7822 \r
7823     CheckForInputBufferFull( is );\r
7824 \r
7825     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7826 \r
7827     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7828 \r
7829     if (is->count < 0) break;  /* Quit on error */\r
7830   }\r
7831   CloseHandle(is->hFile);\r
7832   return 0;\r
7833 }\r
7834 \r
7835 DWORD\r
7836 SocketInputThread(LPVOID arg)\r
7837 {\r
7838   InputSource *is;\r
7839 \r
7840   is = (InputSource *) arg;\r
7841   while (is->hThread != NULL) {\r
7842     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7843     if ((int)is->count == SOCKET_ERROR) {\r
7844       is->count = (DWORD) -1;\r
7845       is->error = WSAGetLastError();\r
7846     } else {\r
7847       is->error = NO_ERROR;\r
7848       is->next += is->count;\r
7849       if (is->count == 0 && is->second == is) {\r
7850         /* End of file on stderr; quit with no message */\r
7851         break;\r
7852       }\r
7853     }\r
7854     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7855 \r
7856     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7857 \r
7858     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7859   }\r
7860   return 0;\r
7861 }\r
7862 \r
7863 VOID\r
7864 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7865 {\r
7866   InputSource *is;\r
7867 \r
7868   is = (InputSource *) lParam;\r
7869   if (is->lineByLine) {\r
7870     /* Feed in lines one by one */\r
7871     char *p = is->buf;\r
7872     char *q = p;\r
7873     while (q < is->next) {\r
7874       if (*q++ == '\n') {\r
7875         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7876         p = q;\r
7877       }\r
7878     }\r
7879     \r
7880     /* Move any partial line to the start of the buffer */\r
7881     q = is->buf;\r
7882     while (p < is->next) {\r
7883       *q++ = *p++;\r
7884     }\r
7885     is->next = q;\r
7886 \r
7887     if (is->error != NO_ERROR || is->count == 0) {\r
7888       /* Notify backend of the error.  Note: If there was a partial\r
7889          line at the end, it is not flushed through. */\r
7890       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7891     }\r
7892   } else {\r
7893     /* Feed in the whole chunk of input at once */\r
7894     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7895     is->next = is->buf;\r
7896   }\r
7897 }\r
7898 \r
7899 /*---------------------------------------------------------------------------*\\r
7900  *\r
7901  *  Menu enables. Used when setting various modes.\r
7902  *\r
7903 \*---------------------------------------------------------------------------*/\r
7904 \r
7905 typedef struct {\r
7906   int item;\r
7907   int flags;\r
7908 } Enables;\r
7909 \r
7910 VOID\r
7911 GreyRevert(Boolean grey)\r
7912 { // [HGM] vari: for retracting variations in local mode\r
7913   HMENU hmenu = GetMenu(hwndMain);\r
7914   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7915   EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
7916 }\r
7917 \r
7918 VOID\r
7919 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7920 {\r
7921   while (enab->item > 0) {\r
7922     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7923     enab++;\r
7924   }\r
7925 }\r
7926 \r
7927 Enables gnuEnables[] = {\r
7928   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7929   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7930   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7931   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7932   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7933   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7934   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7935   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7936   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7937   { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
7938   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7940   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
7941 \r
7942   // Needed to switch from ncp to GNU mode on Engine Load\r
7943   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7944   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7945   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7946   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7947   { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
7948   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7949   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },\r
7950   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7951   { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },\r
7952   { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },\r
7953   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7954   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7955   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7956   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7957   { -1, -1 }\r
7958 };\r
7959 \r
7960 Enables icsEnables[] = {\r
7961   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7962   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7963   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7964   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7965   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7966   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
7967   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
7968   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
7969   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7970   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7971   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7972   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7973   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7974   { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },\r
7975   { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },\r
7976   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7977   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
7978   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
7979   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
7980   { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },\r
7981   { -1, -1 }\r
7982 };\r
7983 \r
7984 #if ZIPPY\r
7985 Enables zippyEnables[] = {\r
7986   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7987   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7988   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7989   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
7990   { -1, -1 }\r
7991 };\r
7992 #endif\r
7993 \r
7994 Enables ncpEnables[] = {\r
7995   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7996   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7997   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7998   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7999   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8000   { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8001   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8002   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8003   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8004   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8005   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8006   { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
8007   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8008   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8009   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8010   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8011   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8012   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8013   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8014   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8015   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8016   { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },\r
8017   { -1, -1 }\r
8018 };\r
8019 \r
8020 Enables trainingOnEnables[] = {\r
8021   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8022   { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },\r
8023   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8024   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8025   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8026   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8027   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8028   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8029   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8030   { -1, -1 }\r
8031 };\r
8032 \r
8033 Enables trainingOffEnables[] = {\r
8034   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8035   { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },\r
8036   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8037   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8038   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8039   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8040   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8041   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8042   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8043   { -1, -1 }\r
8044 };\r
8045 \r
8046 /* These modify either ncpEnables or gnuEnables */\r
8047 Enables cmailEnables[] = {\r
8048   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8049   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8050   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8051   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8052   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8053   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8054   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8055   { -1, -1 }\r
8056 };\r
8057 \r
8058 Enables machineThinkingEnables[] = {\r
8059   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8060   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8061   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8062   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8063   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8064   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8065   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8066   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8067   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8068   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8069   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8070   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8071   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8072 //  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
8073   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8074   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8075   { -1, -1 }\r
8076 };\r
8077 \r
8078 Enables userThinkingEnables[] = {\r
8079   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8080   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8081   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8082   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8083   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8084   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8085   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8086   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8087   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8088   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8089   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8090   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8091   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8092 //  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
8093   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8094   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8095   { -1, -1 }\r
8096 };\r
8097 \r
8098 /*---------------------------------------------------------------------------*\\r
8099  *\r
8100  *  Front-end interface functions exported by XBoard.\r
8101  *  Functions appear in same order as prototypes in frontend.h.\r
8102  * \r
8103 \*---------------------------------------------------------------------------*/\r
8104 VOID\r
8105 CheckMark(UINT item, int state)\r
8106 {\r
8107     if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);\r
8108 }\r
8109 \r
8110 VOID\r
8111 ModeHighlight()\r
8112 {\r
8113   static UINT prevChecked = 0;\r
8114   static int prevPausing = 0;\r
8115   UINT nowChecked;\r
8116 \r
8117   if (pausing != prevPausing) {\r
8118     prevPausing = pausing;\r
8119     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8120                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8121     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8122   }\r
8123 \r
8124   switch (gameMode) {\r
8125   case BeginningOfGame:\r
8126     if (appData.icsActive)\r
8127       nowChecked = IDM_IcsClient;\r
8128     else if (appData.noChessProgram)\r
8129       nowChecked = IDM_EditGame;\r
8130     else\r
8131       nowChecked = IDM_MachineBlack;\r
8132     break;\r
8133   case MachinePlaysBlack:\r
8134     nowChecked = IDM_MachineBlack;\r
8135     break;\r
8136   case MachinePlaysWhite:\r
8137     nowChecked = IDM_MachineWhite;\r
8138     break;\r
8139   case TwoMachinesPlay:\r
8140     nowChecked = IDM_TwoMachines;\r
8141     break;\r
8142   case AnalyzeMode:\r
8143     nowChecked = IDM_AnalysisMode;\r
8144     break;\r
8145   case AnalyzeFile:\r
8146     nowChecked = IDM_AnalyzeFile;\r
8147     break;\r
8148   case EditGame:\r
8149     nowChecked = IDM_EditGame;\r
8150     break;\r
8151   case PlayFromGameFile:\r
8152     nowChecked = IDM_LoadGame;\r
8153     break;\r
8154   case EditPosition:\r
8155     nowChecked = IDM_EditPosition;\r
8156     break;\r
8157   case Training:\r
8158     nowChecked = IDM_Training;\r
8159     break;\r
8160   case IcsPlayingWhite:\r
8161   case IcsPlayingBlack:\r
8162   case IcsObserving:\r
8163   case IcsIdle:\r
8164     nowChecked = IDM_IcsClient;\r
8165     break;\r
8166   default:\r
8167   case EndOfGame:\r
8168     nowChecked = 0;\r
8169     break;\r
8170   }\r
8171   CheckMark(prevChecked, MF_UNCHECKED);\r
8172   CheckMark(nowChecked, MF_CHECKED);\r
8173   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
8174 \r
8175   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8176     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8177                           MF_BYCOMMAND|MF_ENABLED);\r
8178   } else {\r
8179     (void) EnableMenuItem(GetMenu(hwndMain), \r
8180                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8181   }\r
8182 \r
8183   prevChecked = nowChecked;\r
8184 \r
8185   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8186   if (appData.icsActive) {\r
8187        if (appData.icsEngineAnalyze) {\r
8188                CheckMark(IDM_AnalysisMode, MF_CHECKED);\r
8189        } else {\r
8190                CheckMark(IDM_AnalysisMode, MF_UNCHECKED);\r
8191        }\r
8192   }\r
8193   DisplayLogos(); // [HGM] logos: mode change could have altered logos\r
8194 }\r
8195 \r
8196 VOID\r
8197 SetICSMode()\r
8198 {\r
8199   HMENU hmenu = GetMenu(hwndMain);\r
8200   SetMenuEnables(hmenu, icsEnables);\r
8201   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,\r
8202     MF_BYCOMMAND|MF_ENABLED);\r
8203 #if ZIPPY\r
8204   if (appData.zippyPlay) {\r
8205     SetMenuEnables(hmenu, zippyEnables);\r
8206     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8207          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8208           MF_BYCOMMAND|MF_ENABLED);\r
8209   }\r
8210 #endif\r
8211 }\r
8212 \r
8213 VOID\r
8214 SetGNUMode()\r
8215 {\r
8216   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8217 }\r
8218 \r
8219 VOID\r
8220 SetNCPMode()\r
8221 {\r
8222   HMENU hmenu = GetMenu(hwndMain);\r
8223   SetMenuEnables(hmenu, ncpEnables);\r
8224     DrawMenuBar(hwndMain);\r
8225 }\r
8226 \r
8227 VOID\r
8228 SetCmailMode()\r
8229 {\r
8230   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8231 }\r
8232 \r
8233 VOID \r
8234 SetTrainingModeOn()\r
8235 {\r
8236   int i;\r
8237   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8238   for (i = 0; i < N_BUTTONS; i++) {\r
8239     if (buttonDesc[i].hwnd != NULL)\r
8240       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8241   }\r
8242   CommentPopDown();\r
8243 }\r
8244 \r
8245 VOID SetTrainingModeOff()\r
8246 {\r
8247   int i;\r
8248   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8249   for (i = 0; i < N_BUTTONS; i++) {\r
8250     if (buttonDesc[i].hwnd != NULL)\r
8251       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8252   }\r
8253 }\r
8254 \r
8255 \r
8256 VOID\r
8257 SetUserThinkingEnables()\r
8258 {\r
8259   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8260 }\r
8261 \r
8262 VOID\r
8263 SetMachineThinkingEnables()\r
8264 {\r
8265   HMENU hMenu = GetMenu(hwndMain);\r
8266   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8267 \r
8268   SetMenuEnables(hMenu, machineThinkingEnables);\r
8269 \r
8270   if (gameMode == MachinePlaysBlack) {\r
8271     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8272   } else if (gameMode == MachinePlaysWhite) {\r
8273     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8274   } else if (gameMode == TwoMachinesPlay) {\r
8275     (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
8276   }\r
8277 }\r
8278 \r
8279 \r
8280 VOID\r
8281 DisplayTitle(char *str)\r
8282 {\r
8283   char title[MSG_SIZ], *host;\r
8284   if (str[0] != NULLCHAR) {\r
8285     safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
8286   } else if (appData.icsActive) {\r
8287     if (appData.icsCommPort[0] != NULLCHAR)\r
8288       host = "ICS";\r
8289     else \r
8290       host = appData.icsHost;\r
8291       snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
8292   } else if (appData.noChessProgram) {\r
8293     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8294   } else {\r
8295     safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
8296     strcat(title, ": ");\r
8297     strcat(title, first.tidy);\r
8298   }\r
8299   SetWindowText(hwndMain, title);\r
8300 }\r
8301 \r
8302 \r
8303 VOID\r
8304 DisplayMessage(char *str1, char *str2)\r
8305 {\r
8306   HDC hdc;\r
8307   HFONT oldFont;\r
8308   int remain = MESSAGE_TEXT_MAX - 1;\r
8309   int len;\r
8310 \r
8311   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8312   messageText[0] = NULLCHAR;\r
8313   if (*str1) {\r
8314     len = strlen(str1);\r
8315     if (len > remain) len = remain;\r
8316     strncpy(messageText, str1, len);\r
8317     messageText[len] = NULLCHAR;\r
8318     remain -= len;\r
8319   }\r
8320   if (*str2 && remain >= 2) {\r
8321     if (*str1) {\r
8322       strcat(messageText, "  ");\r
8323       remain -= 2;\r
8324     }\r
8325     len = strlen(str2);\r
8326     if (len > remain) len = remain;\r
8327     strncat(messageText, str2, len);\r
8328   }\r
8329   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8330   safeStrCpy(lastMsg, messageText, MSG_SIZ);\r
8331 \r
8332   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8333 \r
8334   SAYMACHINEMOVE();\r
8335 \r
8336   hdc = GetDC(hwndMain);\r
8337   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8338   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8339              &messageRect, messageText, strlen(messageText), NULL);\r
8340   (void) SelectObject(hdc, oldFont);\r
8341   (void) ReleaseDC(hwndMain, hdc);\r
8342 }\r
8343 \r
8344 VOID\r
8345 DisplayError(char *str, int error)\r
8346 {\r
8347   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8348   int len;\r
8349 \r
8350   if (error == 0) {\r
8351     safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
8352   } else {\r
8353     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8354                         NULL, error, LANG_NEUTRAL,\r
8355                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8356     if (len > 0) {\r
8357       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8358     } else {\r
8359       ErrorMap *em = errmap;\r
8360       while (em->err != 0 && em->err != error) em++;\r
8361       if (em->err != 0) {\r
8362         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8363       } else {\r
8364         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8365       }\r
8366     }\r
8367   }\r
8368   \r
8369   ErrorPopUp(_("Error"), buf);\r
8370 }\r
8371 \r
8372 \r
8373 VOID\r
8374 DisplayMoveError(char *str)\r
8375 {\r
8376   fromX = fromY = -1;\r
8377   ClearHighlights();\r
8378   DrawPosition(FALSE, NULL);\r
8379   if (appData.popupMoveErrors) {\r
8380     ErrorPopUp(_("Error"), str);\r
8381   } else {\r
8382     DisplayMessage(str, "");\r
8383     moveErrorMessageUp = TRUE;\r
8384   }\r
8385 }\r
8386 \r
8387 VOID\r
8388 DisplayFatalError(char *str, int error, int exitStatus)\r
8389 {\r
8390   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8391   int len;\r
8392   char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
8393 \r
8394   if (error != 0) {\r
8395     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8396                         NULL, error, LANG_NEUTRAL,\r
8397                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8398     if (len > 0) {\r
8399       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
8400     } else {\r
8401       ErrorMap *em = errmap;\r
8402       while (em->err != 0 && em->err != error) em++;\r
8403       if (em->err != 0) {\r
8404         snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
8405       } else {\r
8406         snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
8407       }\r
8408     }\r
8409     str = buf;\r
8410   }\r
8411   if (appData.debugMode) {\r
8412     fprintf(debugFP, "%s: %s\n", label, str);\r
8413   }\r
8414   if (appData.popupExitMessage) {\r
8415     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8416                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8417   }\r
8418   ExitEvent(exitStatus);\r
8419 }\r
8420 \r
8421 \r
8422 VOID\r
8423 DisplayInformation(char *str)\r
8424 {\r
8425   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
8426 }\r
8427 \r
8428 \r
8429 VOID\r
8430 DisplayNote(char *str)\r
8431 {\r
8432   ErrorPopUp(_("Note"), str);\r
8433 }\r
8434 \r
8435 \r
8436 typedef struct {\r
8437   char *title, *question, *replyPrefix;\r
8438   ProcRef pr;\r
8439 } QuestionParams;\r
8440 \r
8441 LRESULT CALLBACK\r
8442 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8443 {\r
8444   static QuestionParams *qp;\r
8445   char reply[MSG_SIZ];\r
8446   int len, err;\r
8447 \r
8448   switch (message) {\r
8449   case WM_INITDIALOG:\r
8450     qp = (QuestionParams *) lParam;\r
8451     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8452     Translate(hDlg, DLG_Question);\r
8453     SetWindowText(hDlg, qp->title);\r
8454     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8455     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8456     return FALSE;\r
8457 \r
8458   case WM_COMMAND:\r
8459     switch (LOWORD(wParam)) {\r
8460     case IDOK:\r
8461       safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
8462       if (*reply) strcat(reply, " ");\r
8463       len = strlen(reply);\r
8464       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8465       strcat(reply, "\n");\r
8466       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8467       EndDialog(hDlg, TRUE);\r
8468       if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
8469       return TRUE;\r
8470     case IDCANCEL:\r
8471       EndDialog(hDlg, FALSE);\r
8472       return TRUE;\r
8473     default:\r
8474       break;\r
8475     }\r
8476     break;\r
8477   }\r
8478   return FALSE;\r
8479 }\r
8480 \r
8481 VOID\r
8482 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8483 {\r
8484     QuestionParams qp;\r
8485     FARPROC lpProc;\r
8486     \r
8487     qp.title = title;\r
8488     qp.question = question;\r
8489     qp.replyPrefix = replyPrefix;\r
8490     qp.pr = pr;\r
8491     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8492     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8493       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8494     FreeProcInstance(lpProc);\r
8495 }\r
8496 \r
8497 /* [AS] Pick FRC position */\r
8498 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8499 {\r
8500     static int * lpIndexFRC;\r
8501     BOOL index_is_ok;\r
8502     char buf[16];\r
8503 \r
8504     switch( message )\r
8505     {\r
8506     case WM_INITDIALOG:\r
8507         lpIndexFRC = (int *) lParam;\r
8508 \r
8509         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8510         Translate(hDlg, DLG_NewGameFRC);\r
8511 \r
8512         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8513         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8514         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8515         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8516 \r
8517         break;\r
8518 \r
8519     case WM_COMMAND:\r
8520         switch( LOWORD(wParam) ) {\r
8521         case IDOK:\r
8522             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8523             EndDialog( hDlg, 0 );\r
8524             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8525             return TRUE;\r
8526         case IDCANCEL:\r
8527             EndDialog( hDlg, 1 );   \r
8528             return TRUE;\r
8529         case IDC_NFG_Edit:\r
8530             if( HIWORD(wParam) == EN_CHANGE ) {\r
8531                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8532 \r
8533                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8534             }\r
8535             return TRUE;\r
8536         case IDC_NFG_Random:\r
8537           snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8538             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8539             return TRUE;\r
8540         }\r
8541 \r
8542         break;\r
8543     }\r
8544 \r
8545     return FALSE;\r
8546 }\r
8547 \r
8548 int NewGameFRC()\r
8549 {\r
8550     int result;\r
8551     int index = appData.defaultFrcPosition;\r
8552     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8553 \r
8554     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8555 \r
8556     if( result == 0 ) {\r
8557         appData.defaultFrcPosition = index;\r
8558     }\r
8559 \r
8560     return result;\r
8561 }\r
8562 \r
8563 /* [AS] Game list options. Refactored by HGM */\r
8564 \r
8565 HWND gameListOptionsDialog;\r
8566 \r
8567 // low-level front-end: clear text edit / list widget\r
8568 void\r
8569 \r
8570 GLT_ClearList()\r
8571 {\r
8572     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8573 }\r
8574 \r
8575 // low-level front-end: clear text edit / list widget\r
8576 void\r
8577 GLT_DeSelectList()\r
8578 {\r
8579     SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8580 }\r
8581 \r
8582 // low-level front-end: append line to text edit / list widget\r
8583 void\r
8584 GLT_AddToList( char *name )\r
8585 {\r
8586     if( name != 0 ) {\r
8587             SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
8588     }\r
8589 }\r
8590 \r
8591 // low-level front-end: get line from text edit / list widget\r
8592 Boolean\r
8593 GLT_GetFromList( int index, char *name )\r
8594 {\r
8595     if( name != 0 ) {\r
8596             if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
8597                 return TRUE;\r
8598     }\r
8599     return FALSE;\r
8600 }\r
8601 \r
8602 void GLT_MoveSelection( HWND hDlg, int delta )\r
8603 {\r
8604     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8605     int idx2 = idx1 + delta;\r
8606     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8607 \r
8608     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8609         char buf[128];\r
8610 \r
8611         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8612         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8613         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8614         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8615     }\r
8616 }\r
8617 \r
8618 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8619 {\r
8620     switch( message )\r
8621     {\r
8622     case WM_INITDIALOG:\r
8623         gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
8624         \r
8625         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8626         Translate(hDlg, DLG_GameListOptions);\r
8627 \r
8628         /* Initialize list */\r
8629         GLT_TagsToList( lpUserGLT );\r
8630 \r
8631         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8632 \r
8633         break;\r
8634 \r
8635     case WM_COMMAND:\r
8636         switch( LOWORD(wParam) ) {\r
8637         case IDOK:\r
8638             GLT_ParseList();\r
8639             EndDialog( hDlg, 0 );\r
8640             return TRUE;\r
8641         case IDCANCEL:\r
8642             EndDialog( hDlg, 1 );\r
8643             return TRUE;\r
8644 \r
8645         case IDC_GLT_Default:\r
8646             GLT_TagsToList( GLT_DEFAULT_TAGS );\r
8647             return TRUE;\r
8648 \r
8649         case IDC_GLT_Restore:\r
8650             GLT_TagsToList( appData.gameListTags );\r
8651             return TRUE;\r
8652 \r
8653         case IDC_GLT_Up:\r
8654             GLT_MoveSelection( hDlg, -1 );\r
8655             return TRUE;\r
8656 \r
8657         case IDC_GLT_Down:\r
8658             GLT_MoveSelection( hDlg, +1 );\r
8659             return TRUE;\r
8660         }\r
8661 \r
8662         break;\r
8663     }\r
8664 \r
8665     return FALSE;\r
8666 }\r
8667 \r
8668 int GameListOptions()\r
8669 {\r
8670     int result;\r
8671     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8672 \r
8673       safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
8674 \r
8675     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
8676 \r
8677     if( result == 0 ) {\r
8678         /* [AS] Memory leak here! */\r
8679         appData.gameListTags = strdup( lpUserGLT ); \r
8680     }\r
8681 \r
8682     return result;\r
8683 }\r
8684 \r
8685 VOID\r
8686 DisplayIcsInteractionTitle(char *str)\r
8687 {\r
8688   char consoleTitle[MSG_SIZ];\r
8689 \r
8690     snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
8691     SetWindowText(hwndConsole, consoleTitle);\r
8692 \r
8693     if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
8694       char buf[MSG_SIZ], *p = buf, *q;\r
8695         safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
8696       do {\r
8697         q = strchr(p, ';');\r
8698         if(q) *q++ = 0;\r
8699         if(*p) ChatPopUp(p);\r
8700       } while(p=q);\r
8701     }\r
8702 \r
8703     SetActiveWindow(hwndMain);\r
8704 }\r
8705 \r
8706 void\r
8707 DrawPosition(int fullRedraw, Board board)\r
8708 {\r
8709   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8710 }\r
8711 \r
8712 void NotifyFrontendLogin()\r
8713 {\r
8714         if (hwndConsole)\r
8715                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8716 }\r
8717 \r
8718 VOID\r
8719 ResetFrontEnd()\r
8720 {\r
8721   fromX = fromY = -1;\r
8722   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8723     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8724     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8725     dragInfo.lastpos = dragInfo.pos;\r
8726     dragInfo.start.x = dragInfo.start.y = -1;\r
8727     dragInfo.from = dragInfo.start;\r
8728     ReleaseCapture();\r
8729     DrawPosition(TRUE, NULL);\r
8730   }\r
8731   TagsPopDown();\r
8732 }\r
8733 \r
8734 \r
8735 VOID\r
8736 CommentPopUp(char *title, char *str)\r
8737 {\r
8738   HWND hwnd = GetActiveWindow();\r
8739   EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
8740   SAY(str);\r
8741   SetActiveWindow(hwnd);\r
8742 }\r
8743 \r
8744 VOID\r
8745 CommentPopDown(void)\r
8746 {\r
8747   CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);\r
8748   if (commentDialog) {\r
8749     ShowWindow(commentDialog, SW_HIDE);\r
8750   }\r
8751   commentUp = FALSE;\r
8752 }\r
8753 \r
8754 VOID\r
8755 EditCommentPopUp(int index, char *title, char *str)\r
8756 {\r
8757   EitherCommentPopUp(index, title, str, TRUE);\r
8758 }\r
8759 \r
8760 \r
8761 int\r
8762 Roar()\r
8763 {\r
8764   MyPlaySound(&sounds[(int)SoundRoar]);\r
8765   return 1;\r
8766 }\r
8767 \r
8768 VOID\r
8769 RingBell()\r
8770 {\r
8771   MyPlaySound(&sounds[(int)SoundMove]);\r
8772 }\r
8773 \r
8774 VOID PlayIcsWinSound()\r
8775 {\r
8776   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8777 }\r
8778 \r
8779 VOID PlayIcsLossSound()\r
8780 {\r
8781   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8782 }\r
8783 \r
8784 VOID PlayIcsDrawSound()\r
8785 {\r
8786   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8787 }\r
8788 \r
8789 VOID PlayIcsUnfinishedSound()\r
8790 {\r
8791   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8792 }\r
8793 \r
8794 VOID\r
8795 PlayAlarmSound()\r
8796 {\r
8797   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8798 }\r
8799 \r
8800 VOID\r
8801 PlayTellSound()\r
8802 {\r
8803   MyPlaySound(&textAttribs[ColorTell].sound);\r
8804 }\r
8805 \r
8806 \r
8807 VOID\r
8808 EchoOn()\r
8809 {\r
8810   HWND hInput;\r
8811   consoleEcho = TRUE;\r
8812   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8813   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8814   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8815 }\r
8816 \r
8817 \r
8818 VOID\r
8819 EchoOff()\r
8820 {\r
8821   CHARFORMAT cf;\r
8822   HWND hInput;\r
8823   consoleEcho = FALSE;\r
8824   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8825   /* This works OK: set text and background both to the same color */\r
8826   cf = consoleCF;\r
8827   cf.crTextColor = COLOR_ECHOOFF;\r
8828   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8829   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8830 }\r
8831 \r
8832 /* No Raw()...? */\r
8833 \r
8834 void Colorize(ColorClass cc, int continuation)\r
8835 {\r
8836   currentColorClass = cc;\r
8837   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8838   consoleCF.crTextColor = textAttribs[cc].color;\r
8839   consoleCF.dwEffects = textAttribs[cc].effects;\r
8840   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8841 }\r
8842 \r
8843 char *\r
8844 UserName()\r
8845 {\r
8846   static char buf[MSG_SIZ];\r
8847   DWORD bufsiz = MSG_SIZ;\r
8848 \r
8849   if(appData.userName != NULL && appData.userName[0] != 0) { \r
8850         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
8851   }\r
8852   if (!GetUserName(buf, &bufsiz)) {\r
8853     /*DisplayError("Error getting user name", GetLastError());*/\r
8854     safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
8855   }\r
8856   return buf;\r
8857 }\r
8858 \r
8859 char *\r
8860 HostName()\r
8861 {\r
8862   static char buf[MSG_SIZ];\r
8863   DWORD bufsiz = MSG_SIZ;\r
8864 \r
8865   if (!GetComputerName(buf, &bufsiz)) {\r
8866     /*DisplayError("Error getting host name", GetLastError());*/\r
8867     safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
8868   }\r
8869   return buf;\r
8870 }\r
8871 \r
8872 \r
8873 int\r
8874 ClockTimerRunning()\r
8875 {\r
8876   return clockTimerEvent != 0;\r
8877 }\r
8878 \r
8879 int\r
8880 StopClockTimer()\r
8881 {\r
8882   if (clockTimerEvent == 0) return FALSE;\r
8883   KillTimer(hwndMain, clockTimerEvent);\r
8884   clockTimerEvent = 0;\r
8885   return TRUE;\r
8886 }\r
8887 \r
8888 void\r
8889 StartClockTimer(long millisec)\r
8890 {\r
8891   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8892                              (UINT) millisec, NULL);\r
8893 }\r
8894 \r
8895 void\r
8896 DisplayWhiteClock(long timeRemaining, int highlight)\r
8897 {\r
8898   HDC hdc;\r
8899   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8900 \r
8901   if(appData.noGUI) return;\r
8902   hdc = GetDC(hwndMain);\r
8903   if (!IsIconic(hwndMain)) {\r
8904     DisplayAClock(hdc, timeRemaining, highlight, \r
8905                         flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
8906   }\r
8907   if (highlight && iconCurrent == iconBlack) {\r
8908     iconCurrent = iconWhite;\r
8909     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8910     if (IsIconic(hwndMain)) {\r
8911       DrawIcon(hdc, 2, 2, iconCurrent);\r
8912     }\r
8913   }\r
8914   (void) ReleaseDC(hwndMain, hdc);\r
8915   if (hwndConsole)\r
8916     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8917 }\r
8918 \r
8919 void\r
8920 DisplayBlackClock(long timeRemaining, int highlight)\r
8921 {\r
8922   HDC hdc;\r
8923   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8924 \r
8925 \r
8926   if(appData.noGUI) return;\r
8927   hdc = GetDC(hwndMain);\r
8928   if (!IsIconic(hwndMain)) {\r
8929     DisplayAClock(hdc, timeRemaining, highlight, \r
8930                         flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
8931   }\r
8932   if (highlight && iconCurrent == iconWhite) {\r
8933     iconCurrent = iconBlack;\r
8934     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8935     if (IsIconic(hwndMain)) {\r
8936       DrawIcon(hdc, 2, 2, iconCurrent);\r
8937     }\r
8938   }\r
8939   (void) ReleaseDC(hwndMain, hdc);\r
8940   if (hwndConsole)\r
8941     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8942 }\r
8943 \r
8944 \r
8945 int\r
8946 LoadGameTimerRunning()\r
8947 {\r
8948   return loadGameTimerEvent != 0;\r
8949 }\r
8950 \r
8951 int\r
8952 StopLoadGameTimer()\r
8953 {\r
8954   if (loadGameTimerEvent == 0) return FALSE;\r
8955   KillTimer(hwndMain, loadGameTimerEvent);\r
8956   loadGameTimerEvent = 0;\r
8957   return TRUE;\r
8958 }\r
8959 \r
8960 void\r
8961 StartLoadGameTimer(long millisec)\r
8962 {\r
8963   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8964                                 (UINT) millisec, NULL);\r
8965 }\r
8966 \r
8967 void\r
8968 AutoSaveGame()\r
8969 {\r
8970   char *defName;\r
8971   FILE *f;\r
8972   char fileTitle[MSG_SIZ];\r
8973 \r
8974   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8975   f = OpenFileDialog(hwndMain, "a", defName,\r
8976                      appData.oldSaveStyle ? "gam" : "pgn",\r
8977                      GAME_FILT, \r
8978                      _("Save Game to File"), NULL, fileTitle, NULL);\r
8979   if (f != NULL) {\r
8980     SaveGame(f, 0, "");\r
8981     fclose(f);\r
8982   }\r
8983 }\r
8984 \r
8985 \r
8986 void\r
8987 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8988 {\r
8989   if (delayedTimerEvent != 0) {\r
8990     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
8991       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8992     }\r
8993     KillTimer(hwndMain, delayedTimerEvent);\r
8994     delayedTimerEvent = 0;\r
8995     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
8996     delayedTimerCallback();\r
8997   }\r
8998   delayedTimerCallback = cb;\r
8999   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9000                                 (UINT) millisec, NULL);\r
9001 }\r
9002 \r
9003 DelayedEventCallback\r
9004 GetDelayedEvent()\r
9005 {\r
9006   if (delayedTimerEvent) {\r
9007     return delayedTimerCallback;\r
9008   } else {\r
9009     return NULL;\r
9010   }\r
9011 }\r
9012 \r
9013 void\r
9014 CancelDelayedEvent()\r
9015 {\r
9016   if (delayedTimerEvent) {\r
9017     KillTimer(hwndMain, delayedTimerEvent);\r
9018     delayedTimerEvent = 0;\r
9019   }\r
9020 }\r
9021 \r
9022 DWORD GetWin32Priority(int nice)\r
9023 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9024 /*\r
9025 REALTIME_PRIORITY_CLASS     0x00000100\r
9026 HIGH_PRIORITY_CLASS         0x00000080\r
9027 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9028 NORMAL_PRIORITY_CLASS       0x00000020\r
9029 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9030 IDLE_PRIORITY_CLASS         0x00000040\r
9031 */\r
9032         if (nice < -15) return 0x00000080;\r
9033         if (nice < 0)   return 0x00008000;\r
9034         if (nice == 0)  return 0x00000020;\r
9035         if (nice < 15)  return 0x00004000;\r
9036         return 0x00000040;\r
9037 }\r
9038 \r
9039 void RunCommand(char *cmdLine)\r
9040 {\r
9041   /* Now create the child process. */\r
9042   STARTUPINFO siStartInfo;\r
9043   PROCESS_INFORMATION piProcInfo;\r
9044 \r
9045   siStartInfo.cb = sizeof(STARTUPINFO);\r
9046   siStartInfo.lpReserved = NULL;\r
9047   siStartInfo.lpDesktop = NULL;\r
9048   siStartInfo.lpTitle = NULL;\r
9049   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9050   siStartInfo.cbReserved2 = 0;\r
9051   siStartInfo.lpReserved2 = NULL;\r
9052   siStartInfo.hStdInput = NULL;\r
9053   siStartInfo.hStdOutput = NULL;\r
9054   siStartInfo.hStdError = NULL;\r
9055 \r
9056   CreateProcess(NULL,\r
9057                 cmdLine,           /* command line */\r
9058                 NULL,      /* process security attributes */\r
9059                 NULL,      /* primary thread security attrs */\r
9060                 TRUE,      /* handles are inherited */\r
9061                 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9062                 NULL,      /* use parent's environment */\r
9063                 NULL,\r
9064                 &siStartInfo, /* STARTUPINFO pointer */\r
9065                 &piProcInfo); /* receives PROCESS_INFORMATION */\r
9066 \r
9067   CloseHandle(piProcInfo.hThread);\r
9068 }\r
9069 \r
9070 /* Start a child process running the given program.\r
9071    The process's standard output can be read from "from", and its\r
9072    standard input can be written to "to".\r
9073    Exit with fatal error if anything goes wrong.\r
9074    Returns an opaque pointer that can be used to destroy the process\r
9075    later.\r
9076 */\r
9077 int\r
9078 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9079 {\r
9080 #define BUFSIZE 4096\r
9081 \r
9082   HANDLE hChildStdinRd, hChildStdinWr,\r
9083     hChildStdoutRd, hChildStdoutWr;\r
9084   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9085   SECURITY_ATTRIBUTES saAttr;\r
9086   BOOL fSuccess;\r
9087   PROCESS_INFORMATION piProcInfo;\r
9088   STARTUPINFO siStartInfo;\r
9089   ChildProc *cp;\r
9090   char buf[MSG_SIZ];\r
9091   DWORD err;\r
9092 \r
9093   if (appData.debugMode) {\r
9094     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9095   }\r
9096 \r
9097   *pr = NoProc;\r
9098 \r
9099   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9100   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9101   saAttr.bInheritHandle = TRUE;\r
9102   saAttr.lpSecurityDescriptor = NULL;\r
9103 \r
9104   /*\r
9105    * The steps for redirecting child's STDOUT:\r
9106    *     1. Create anonymous pipe to be STDOUT for child.\r
9107    *     2. Create a noninheritable duplicate of read handle,\r
9108    *         and close the inheritable read handle.\r
9109    */\r
9110 \r
9111   /* Create a pipe for the child's STDOUT. */\r
9112   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9113     return GetLastError();\r
9114   }\r
9115 \r
9116   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9117   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9118                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9119                              FALSE,     /* not inherited */\r
9120                              DUPLICATE_SAME_ACCESS);\r
9121   if (! fSuccess) {\r
9122     return GetLastError();\r
9123   }\r
9124   CloseHandle(hChildStdoutRd);\r
9125 \r
9126   /*\r
9127    * The steps for redirecting child's STDIN:\r
9128    *     1. Create anonymous pipe to be STDIN for child.\r
9129    *     2. Create a noninheritable duplicate of write handle,\r
9130    *         and close the inheritable write handle.\r
9131    */\r
9132 \r
9133   /* Create a pipe for the child's STDIN. */\r
9134   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9135     return GetLastError();\r
9136   }\r
9137 \r
9138   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9139   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9140                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9141                              FALSE,     /* not inherited */\r
9142                              DUPLICATE_SAME_ACCESS);\r
9143   if (! fSuccess) {\r
9144     return GetLastError();\r
9145   }\r
9146   CloseHandle(hChildStdinWr);\r
9147 \r
9148   /* Arrange to (1) look in dir for the child .exe file, and\r
9149    * (2) have dir be the child's working directory.  Interpret\r
9150    * dir relative to the directory WinBoard loaded from. */\r
9151   GetCurrentDirectory(MSG_SIZ, buf);\r
9152   SetCurrentDirectory(installDir);\r
9153   SetCurrentDirectory(dir);\r
9154 \r
9155   /* Now create the child process. */\r
9156 \r
9157   siStartInfo.cb = sizeof(STARTUPINFO);\r
9158   siStartInfo.lpReserved = NULL;\r
9159   siStartInfo.lpDesktop = NULL;\r
9160   siStartInfo.lpTitle = NULL;\r
9161   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9162   siStartInfo.cbReserved2 = 0;\r
9163   siStartInfo.lpReserved2 = NULL;\r
9164   siStartInfo.hStdInput = hChildStdinRd;\r
9165   siStartInfo.hStdOutput = hChildStdoutWr;\r
9166   siStartInfo.hStdError = hChildStdoutWr;\r
9167 \r
9168   fSuccess = CreateProcess(NULL,\r
9169                            cmdLine,        /* command line */\r
9170                            NULL,           /* process security attributes */\r
9171                            NULL,           /* primary thread security attrs */\r
9172                            TRUE,           /* handles are inherited */\r
9173                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9174                            NULL,           /* use parent's environment */\r
9175                            NULL,\r
9176                            &siStartInfo, /* STARTUPINFO pointer */\r
9177                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9178 \r
9179   err = GetLastError();\r
9180   SetCurrentDirectory(buf); /* return to prev directory */\r
9181   if (! fSuccess) {\r
9182     return err;\r
9183   }\r
9184 \r
9185   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9186     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9187     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9188   }\r
9189 \r
9190   /* Close the handles we don't need in the parent */\r
9191   CloseHandle(piProcInfo.hThread);\r
9192   CloseHandle(hChildStdinRd);\r
9193   CloseHandle(hChildStdoutWr);\r
9194 \r
9195   /* Prepare return value */\r
9196   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9197   cp->kind = CPReal;\r
9198   cp->hProcess = piProcInfo.hProcess;\r
9199   cp->pid = piProcInfo.dwProcessId;\r
9200   cp->hFrom = hChildStdoutRdDup;\r
9201   cp->hTo = hChildStdinWrDup;\r
9202 \r
9203   *pr = (void *) cp;\r
9204 \r
9205   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9206      2000 where engines sometimes don't see the initial command(s)\r
9207      from WinBoard and hang.  I don't understand how that can happen,\r
9208      but the Sleep is harmless, so I've put it in.  Others have also\r
9209      reported what may be the same problem, so hopefully this will fix\r
9210      it for them too.  */\r
9211   Sleep(500);\r
9212 \r
9213   return NO_ERROR;\r
9214 }\r
9215 \r
9216 \r
9217 void\r
9218 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9219 {\r
9220   ChildProc *cp; int result;\r
9221 \r
9222   cp = (ChildProc *) pr;\r
9223   if (cp == NULL) return;\r
9224 \r
9225   switch (cp->kind) {\r
9226   case CPReal:\r
9227     /* TerminateProcess is considered harmful, so... */\r
9228     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9229     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9230     /* The following doesn't work because the chess program\r
9231        doesn't "have the same console" as WinBoard.  Maybe\r
9232        we could arrange for this even though neither WinBoard\r
9233        nor the chess program uses a console for stdio? */\r
9234     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9235 \r
9236     /* [AS] Special termination modes for misbehaving programs... */\r
9237     if( signal == 9 ) { \r
9238         result = TerminateProcess( cp->hProcess, 0 );\r
9239 \r
9240         if ( appData.debugMode) {\r
9241             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9242         }\r
9243     }\r
9244     else if( signal == 10 ) {\r
9245         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9246 \r
9247         if( dw != WAIT_OBJECT_0 ) {\r
9248             result = TerminateProcess( cp->hProcess, 0 );\r
9249 \r
9250             if ( appData.debugMode) {\r
9251                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9252             }\r
9253 \r
9254         }\r
9255     }\r
9256 \r
9257     CloseHandle(cp->hProcess);\r
9258     break;\r
9259 \r
9260   case CPComm:\r
9261     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9262     break;\r
9263 \r
9264   case CPSock:\r
9265     closesocket(cp->sock);\r
9266     WSACleanup();\r
9267     break;\r
9268 \r
9269   case CPRcmd:\r
9270     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9271     closesocket(cp->sock);\r
9272     closesocket(cp->sock2);\r
9273     WSACleanup();\r
9274     break;\r
9275   }\r
9276   free(cp);\r
9277 }\r
9278 \r
9279 void\r
9280 InterruptChildProcess(ProcRef pr)\r
9281 {\r
9282   ChildProc *cp;\r
9283 \r
9284   cp = (ChildProc *) pr;\r
9285   if (cp == NULL) return;\r
9286   switch (cp->kind) {\r
9287   case CPReal:\r
9288     /* The following doesn't work because the chess program\r
9289        doesn't "have the same console" as WinBoard.  Maybe\r
9290        we could arrange for this even though neither WinBoard\r
9291        nor the chess program uses a console for stdio */\r
9292     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9293     break;\r
9294 \r
9295   case CPComm:\r
9296   case CPSock:\r
9297     /* Can't interrupt */\r
9298     break;\r
9299 \r
9300   case CPRcmd:\r
9301     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9302     break;\r
9303   }\r
9304 }\r
9305 \r
9306 \r
9307 int\r
9308 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9309 {\r
9310   char cmdLine[MSG_SIZ];\r
9311 \r
9312   if (port[0] == NULLCHAR) {\r
9313     snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
9314   } else {\r
9315     snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
9316   }\r
9317   return StartChildProcess(cmdLine, "", pr);\r
9318 }\r
9319 \r
9320 \r
9321 /* Code to open TCP sockets */\r
9322 \r
9323 int\r
9324 OpenTCP(char *host, char *port, ProcRef *pr)\r
9325 {\r
9326   ChildProc *cp;\r
9327   int err;\r
9328   SOCKET s;\r
9329 \r
9330   struct sockaddr_in sa, mysa;\r
9331   struct hostent FAR *hp;\r
9332   unsigned short uport;\r
9333   WORD wVersionRequested;\r
9334   WSADATA wsaData;\r
9335 \r
9336   /* Initialize socket DLL */\r
9337   wVersionRequested = MAKEWORD(1, 1);\r
9338   err = WSAStartup(wVersionRequested, &wsaData);\r
9339   if (err != 0) return err;\r
9340 \r
9341   /* Make socket */\r
9342   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9343     err = WSAGetLastError();\r
9344     WSACleanup();\r
9345     return err;\r
9346   }\r
9347 \r
9348   /* Bind local address using (mostly) don't-care values.\r
9349    */\r
9350   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9351   mysa.sin_family = AF_INET;\r
9352   mysa.sin_addr.s_addr = INADDR_ANY;\r
9353   uport = (unsigned short) 0;\r
9354   mysa.sin_port = htons(uport);\r
9355   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9356       == SOCKET_ERROR) {\r
9357     err = WSAGetLastError();\r
9358     WSACleanup();\r
9359     return err;\r
9360   }\r
9361 \r
9362   /* Resolve remote host name */\r
9363   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9364   if (!(hp = gethostbyname(host))) {\r
9365     unsigned int b0, b1, b2, b3;\r
9366 \r
9367     err = WSAGetLastError();\r
9368 \r
9369     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9370       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9371       hp->h_addrtype = AF_INET;\r
9372       hp->h_length = 4;\r
9373       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9374       hp->h_addr_list[0] = (char *) malloc(4);\r
9375       hp->h_addr_list[0][0] = (char) b0;\r
9376       hp->h_addr_list[0][1] = (char) b1;\r
9377       hp->h_addr_list[0][2] = (char) b2;\r
9378       hp->h_addr_list[0][3] = (char) b3;\r
9379     } else {\r
9380       WSACleanup();\r
9381       return err;\r
9382     }\r
9383   }\r
9384   sa.sin_family = hp->h_addrtype;\r
9385   uport = (unsigned short) atoi(port);\r
9386   sa.sin_port = htons(uport);\r
9387   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9388 \r
9389   /* Make connection */\r
9390   if (connect(s, (struct sockaddr *) &sa,\r
9391               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9392     err = WSAGetLastError();\r
9393     WSACleanup();\r
9394     return err;\r
9395   }\r
9396 \r
9397   /* Prepare return value */\r
9398   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9399   cp->kind = CPSock;\r
9400   cp->sock = s;\r
9401   *pr = (ProcRef *) cp;\r
9402 \r
9403   return NO_ERROR;\r
9404 }\r
9405 \r
9406 int\r
9407 OpenCommPort(char *name, ProcRef *pr)\r
9408 {\r
9409   HANDLE h;\r
9410   COMMTIMEOUTS ct;\r
9411   ChildProc *cp;\r
9412   char fullname[MSG_SIZ];\r
9413 \r
9414   if (*name != '\\')\r
9415     snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
9416   else\r
9417     safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
9418 \r
9419   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9420                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9421   if (h == (HANDLE) -1) {\r
9422     return GetLastError();\r
9423   }\r
9424   hCommPort = h;\r
9425 \r
9426   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9427 \r
9428   /* Accumulate characters until a 100ms pause, then parse */\r
9429   ct.ReadIntervalTimeout = 100;\r
9430   ct.ReadTotalTimeoutMultiplier = 0;\r
9431   ct.ReadTotalTimeoutConstant = 0;\r
9432   ct.WriteTotalTimeoutMultiplier = 0;\r
9433   ct.WriteTotalTimeoutConstant = 0;\r
9434   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9435 \r
9436   /* Prepare return value */\r
9437   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9438   cp->kind = CPComm;\r
9439   cp->hFrom = h;\r
9440   cp->hTo = h;\r
9441   *pr = (ProcRef *) cp;\r
9442 \r
9443   return NO_ERROR;\r
9444 }\r
9445 \r
9446 int\r
9447 OpenLoopback(ProcRef *pr)\r
9448 {\r
9449   DisplayFatalError(_("Not implemented"), 0, 1);\r
9450   return NO_ERROR;\r
9451 }\r
9452 \r
9453 \r
9454 int\r
9455 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9456 {\r
9457   ChildProc *cp;\r
9458   int err;\r
9459   SOCKET s, s2, s3;\r
9460   struct sockaddr_in sa, mysa;\r
9461   struct hostent FAR *hp;\r
9462   unsigned short uport;\r
9463   WORD wVersionRequested;\r
9464   WSADATA wsaData;\r
9465   int fromPort;\r
9466   char stderrPortStr[MSG_SIZ];\r
9467 \r
9468   /* Initialize socket DLL */\r
9469   wVersionRequested = MAKEWORD(1, 1);\r
9470   err = WSAStartup(wVersionRequested, &wsaData);\r
9471   if (err != 0) return err;\r
9472 \r
9473   /* Resolve remote host name */\r
9474   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9475   if (!(hp = gethostbyname(host))) {\r
9476     unsigned int b0, b1, b2, b3;\r
9477 \r
9478     err = WSAGetLastError();\r
9479 \r
9480     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9481       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9482       hp->h_addrtype = AF_INET;\r
9483       hp->h_length = 4;\r
9484       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9485       hp->h_addr_list[0] = (char *) malloc(4);\r
9486       hp->h_addr_list[0][0] = (char) b0;\r
9487       hp->h_addr_list[0][1] = (char) b1;\r
9488       hp->h_addr_list[0][2] = (char) b2;\r
9489       hp->h_addr_list[0][3] = (char) b3;\r
9490     } else {\r
9491       WSACleanup();\r
9492       return err;\r
9493     }\r
9494   }\r
9495   sa.sin_family = hp->h_addrtype;\r
9496   uport = (unsigned short) 514;\r
9497   sa.sin_port = htons(uport);\r
9498   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9499 \r
9500   /* Bind local socket to unused "privileged" port address\r
9501    */\r
9502   s = INVALID_SOCKET;\r
9503   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9504   mysa.sin_family = AF_INET;\r
9505   mysa.sin_addr.s_addr = INADDR_ANY;\r
9506   for (fromPort = 1023;; fromPort--) {\r
9507     if (fromPort < 0) {\r
9508       WSACleanup();\r
9509       return WSAEADDRINUSE;\r
9510     }\r
9511     if (s == INVALID_SOCKET) {\r
9512       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9513         err = WSAGetLastError();\r
9514         WSACleanup();\r
9515         return err;\r
9516       }\r
9517     }\r
9518     uport = (unsigned short) fromPort;\r
9519     mysa.sin_port = htons(uport);\r
9520     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9521         == SOCKET_ERROR) {\r
9522       err = WSAGetLastError();\r
9523       if (err == WSAEADDRINUSE) continue;\r
9524       WSACleanup();\r
9525       return err;\r
9526     }\r
9527     if (connect(s, (struct sockaddr *) &sa,\r
9528       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9529       err = WSAGetLastError();\r
9530       if (err == WSAEADDRINUSE) {\r
9531         closesocket(s);\r
9532         s = -1;\r
9533         continue;\r
9534       }\r
9535       WSACleanup();\r
9536       return err;\r
9537     }\r
9538     break;\r
9539   }\r
9540 \r
9541   /* Bind stderr local socket to unused "privileged" port address\r
9542    */\r
9543   s2 = INVALID_SOCKET;\r
9544   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9545   mysa.sin_family = AF_INET;\r
9546   mysa.sin_addr.s_addr = INADDR_ANY;\r
9547   for (fromPort = 1023;; fromPort--) {\r
9548     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9549     if (fromPort < 0) {\r
9550       (void) closesocket(s);\r
9551       WSACleanup();\r
9552       return WSAEADDRINUSE;\r
9553     }\r
9554     if (s2 == INVALID_SOCKET) {\r
9555       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9556         err = WSAGetLastError();\r
9557         closesocket(s);\r
9558         WSACleanup();\r
9559         return err;\r
9560       }\r
9561     }\r
9562     uport = (unsigned short) fromPort;\r
9563     mysa.sin_port = htons(uport);\r
9564     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9565         == SOCKET_ERROR) {\r
9566       err = WSAGetLastError();\r
9567       if (err == WSAEADDRINUSE) continue;\r
9568       (void) closesocket(s);\r
9569       WSACleanup();\r
9570       return err;\r
9571     }\r
9572     if (listen(s2, 1) == SOCKET_ERROR) {\r
9573       err = WSAGetLastError();\r
9574       if (err == WSAEADDRINUSE) {\r
9575         closesocket(s2);\r
9576         s2 = INVALID_SOCKET;\r
9577         continue;\r
9578       }\r
9579       (void) closesocket(s);\r
9580       (void) closesocket(s2);\r
9581       WSACleanup();\r
9582       return err;\r
9583     }\r
9584     break;\r
9585   }\r
9586   prevStderrPort = fromPort; // remember port used\r
9587   snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
9588 \r
9589   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9590     err = WSAGetLastError();\r
9591     (void) closesocket(s);\r
9592     (void) closesocket(s2);\r
9593     WSACleanup();\r
9594     return err;\r
9595   }\r
9596 \r
9597   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9598     err = WSAGetLastError();\r
9599     (void) closesocket(s);\r
9600     (void) closesocket(s2);\r
9601     WSACleanup();\r
9602     return err;\r
9603   }\r
9604   if (*user == NULLCHAR) user = UserName();\r
9605   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9606     err = WSAGetLastError();\r
9607     (void) closesocket(s);\r
9608     (void) closesocket(s2);\r
9609     WSACleanup();\r
9610     return err;\r
9611   }\r
9612   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9613     err = WSAGetLastError();\r
9614     (void) closesocket(s);\r
9615     (void) closesocket(s2);\r
9616     WSACleanup();\r
9617     return err;\r
9618   }\r
9619 \r
9620   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9621     err = WSAGetLastError();\r
9622     (void) closesocket(s);\r
9623     (void) closesocket(s2);\r
9624     WSACleanup();\r
9625     return err;\r
9626   }\r
9627   (void) closesocket(s2);  /* Stop listening */\r
9628 \r
9629   /* Prepare return value */\r
9630   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9631   cp->kind = CPRcmd;\r
9632   cp->sock = s;\r
9633   cp->sock2 = s3;\r
9634   *pr = (ProcRef *) cp;\r
9635 \r
9636   return NO_ERROR;\r
9637 }\r
9638 \r
9639 \r
9640 InputSourceRef\r
9641 AddInputSource(ProcRef pr, int lineByLine,\r
9642                InputCallback func, VOIDSTAR closure)\r
9643 {\r
9644   InputSource *is, *is2 = NULL;\r
9645   ChildProc *cp = (ChildProc *) pr;\r
9646 \r
9647   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9648   is->lineByLine = lineByLine;\r
9649   is->func = func;\r
9650   is->closure = closure;\r
9651   is->second = NULL;\r
9652   is->next = is->buf;\r
9653   if (pr == NoProc) {\r
9654     is->kind = CPReal;\r
9655     consoleInputSource = is;\r
9656   } else {\r
9657     is->kind = cp->kind;\r
9658     /* \r
9659         [AS] Try to avoid a race condition if the thread is given control too early:\r
9660         we create all threads suspended so that the is->hThread variable can be\r
9661         safely assigned, then let the threads start with ResumeThread.\r
9662     */\r
9663     switch (cp->kind) {\r
9664     case CPReal:\r
9665       is->hFile = cp->hFrom;\r
9666       cp->hFrom = NULL; /* now owned by InputThread */\r
9667       is->hThread =\r
9668         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9669                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9670       break;\r
9671 \r
9672     case CPComm:\r
9673       is->hFile = cp->hFrom;\r
9674       cp->hFrom = NULL; /* now owned by InputThread */\r
9675       is->hThread =\r
9676         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9677                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9678       break;\r
9679 \r
9680     case CPSock:\r
9681       is->sock = cp->sock;\r
9682       is->hThread =\r
9683         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9684                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9685       break;\r
9686 \r
9687     case CPRcmd:\r
9688       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9689       *is2 = *is;\r
9690       is->sock = cp->sock;\r
9691       is->second = is2;\r
9692       is2->sock = cp->sock2;\r
9693       is2->second = is2;\r
9694       is->hThread =\r
9695         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9696                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9697       is2->hThread =\r
9698         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9699                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9700       break;\r
9701     }\r
9702 \r
9703     if( is->hThread != NULL ) {\r
9704         ResumeThread( is->hThread );\r
9705     }\r
9706 \r
9707     if( is2 != NULL && is2->hThread != NULL ) {\r
9708         ResumeThread( is2->hThread );\r
9709     }\r
9710   }\r
9711 \r
9712   return (InputSourceRef) is;\r
9713 }\r
9714 \r
9715 void\r
9716 RemoveInputSource(InputSourceRef isr)\r
9717 {\r
9718   InputSource *is;\r
9719 \r
9720   is = (InputSource *) isr;\r
9721   is->hThread = NULL;  /* tell thread to stop */\r
9722   CloseHandle(is->hThread);\r
9723   if (is->second != NULL) {\r
9724     is->second->hThread = NULL;\r
9725     CloseHandle(is->second->hThread);\r
9726   }\r
9727 }\r
9728 \r
9729 int no_wrap(char *message, int count)\r
9730 {\r
9731     ConsoleOutput(message, count, FALSE);\r
9732     return count;\r
9733 }\r
9734 \r
9735 int\r
9736 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9737 {\r
9738   DWORD dOutCount;\r
9739   int outCount = SOCKET_ERROR;\r
9740   ChildProc *cp = (ChildProc *) pr;\r
9741   static OVERLAPPED ovl;\r
9742   static int line = 0;\r
9743 \r
9744   if (pr == NoProc)\r
9745   {\r
9746     if (appData.noJoin || !appData.useInternalWrap)\r
9747       return no_wrap(message, count);\r
9748     else\r
9749     {\r
9750       int width = get_term_width();\r
9751       int len = wrap(NULL, message, count, width, &line);\r
9752       char *msg = malloc(len);\r
9753       int dbgchk;\r
9754 \r
9755       if (!msg)\r
9756         return no_wrap(message, count);\r
9757       else\r
9758       {\r
9759         dbgchk = wrap(msg, message, count, width, &line);\r
9760         if (dbgchk != len && appData.debugMode)\r
9761             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
9762         ConsoleOutput(msg, len, FALSE);\r
9763         free(msg);\r
9764         return len;\r
9765       }\r
9766     }\r
9767   }\r
9768 \r
9769   if (ovl.hEvent == NULL) {\r
9770     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9771   }\r
9772   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9773 \r
9774   switch (cp->kind) {\r
9775   case CPSock:\r
9776   case CPRcmd:\r
9777     outCount = send(cp->sock, message, count, 0);\r
9778     if (outCount == SOCKET_ERROR) {\r
9779       *outError = WSAGetLastError();\r
9780     } else {\r
9781       *outError = NO_ERROR;\r
9782     }\r
9783     break;\r
9784 \r
9785   case CPReal:\r
9786     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9787                   &dOutCount, NULL)) {\r
9788       *outError = NO_ERROR;\r
9789       outCount = (int) dOutCount;\r
9790     } else {\r
9791       *outError = GetLastError();\r
9792     }\r
9793     break;\r
9794 \r
9795   case CPComm:\r
9796     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9797                             &dOutCount, &ovl);\r
9798     if (*outError == NO_ERROR) {\r
9799       outCount = (int) dOutCount;\r
9800     }\r
9801     break;\r
9802   }\r
9803   return outCount;\r
9804 }\r
9805 \r
9806 void\r
9807 DoSleep(int n)\r
9808 {\r
9809     if(n != 0) Sleep(n);\r
9810 }\r
9811 \r
9812 int\r
9813 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9814                        long msdelay)\r
9815 {\r
9816   /* Ignore delay, not implemented for WinBoard */\r
9817   return OutputToProcess(pr, message, count, outError);\r
9818 }\r
9819 \r
9820 \r
9821 void\r
9822 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9823                         char *buf, int count, int error)\r
9824 {\r
9825   DisplayFatalError(_("Not implemented"), 0, 1);\r
9826 }\r
9827 \r
9828 /* see wgamelist.c for Game List functions */\r
9829 /* see wedittags.c for Edit Tags functions */\r
9830 \r
9831 \r
9832 int\r
9833 ICSInitScript()\r
9834 {\r
9835   FILE *f;\r
9836   char buf[MSG_SIZ];\r
9837   char *dummy;\r
9838 \r
9839   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9840     f = fopen(buf, "r");\r
9841     if (f != NULL) {\r
9842       ProcessICSInitScript(f);\r
9843       fclose(f);\r
9844       return TRUE;\r
9845     }\r
9846   }\r
9847   return FALSE;\r
9848 }\r
9849 \r
9850 \r
9851 VOID\r
9852 StartAnalysisClock()\r
9853 {\r
9854   if (analysisTimerEvent) return;\r
9855   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9856                                         (UINT) 2000, NULL);\r
9857 }\r
9858 \r
9859 VOID\r
9860 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9861 {\r
9862   highlightInfo.sq[0].x = fromX;\r
9863   highlightInfo.sq[0].y = fromY;\r
9864   highlightInfo.sq[1].x = toX;\r
9865   highlightInfo.sq[1].y = toY;\r
9866 }\r
9867 \r
9868 VOID\r
9869 ClearHighlights()\r
9870 {\r
9871   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9872     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9873 }\r
9874 \r
9875 VOID\r
9876 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9877 {\r
9878   premoveHighlightInfo.sq[0].x = fromX;\r
9879   premoveHighlightInfo.sq[0].y = fromY;\r
9880   premoveHighlightInfo.sq[1].x = toX;\r
9881   premoveHighlightInfo.sq[1].y = toY;\r
9882 }\r
9883 \r
9884 VOID\r
9885 ClearPremoveHighlights()\r
9886 {\r
9887   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9888     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9889 }\r
9890 \r
9891 VOID\r
9892 ShutDownFrontEnd()\r
9893 {\r
9894   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9895   DeleteClipboardTempFiles();\r
9896 }\r
9897 \r
9898 void\r
9899 BoardToTop()\r
9900 {\r
9901     if (IsIconic(hwndMain))\r
9902       ShowWindow(hwndMain, SW_RESTORE);\r
9903 \r
9904     SetActiveWindow(hwndMain);\r
9905 }\r
9906 \r
9907 /*\r
9908  * Prototypes for animation support routines\r
9909  */\r
9910 static void ScreenSquare(int column, int row, POINT * pt);\r
9911 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9912      POINT frames[], int * nFrames);\r
9913 \r
9914 \r
9915 #define kFactor 4\r
9916 \r
9917 void\r
9918 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)\r
9919 {       // [HGM] atomic: animate blast wave\r
9920         int i;\r
9921 \r
9922         explodeInfo.fromX = fromX;\r
9923         explodeInfo.fromY = fromY;\r
9924         explodeInfo.toX = toX;\r
9925         explodeInfo.toY = toY;\r
9926         for(i=1; i<4*kFactor; i++) {\r
9927             explodeInfo.radius = (i*180)/(4*kFactor-1);\r
9928             DrawPosition(FALSE, board);\r
9929             Sleep(appData.animSpeed);\r
9930         }\r
9931         explodeInfo.radius = 0;\r
9932         DrawPosition(TRUE, board);\r
9933 }\r
9934 \r
9935 void\r
9936 AnimateMove(board, fromX, fromY, toX, toY)\r
9937      Board board;\r
9938      int fromX;\r
9939      int fromY;\r
9940      int toX;\r
9941      int toY;\r
9942 {\r
9943   ChessSquare piece;\r
9944   int x = toX, y = toY;\r
9945   POINT start, finish, mid;\r
9946   POINT frames[kFactor * 2 + 1];\r
9947   int nFrames, n;\r
9948 \r
9949   if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();\r
9950 \r
9951   if (!appData.animate) return;\r
9952   if (doingSizing) return;\r
9953   if (fromY < 0 || fromX < 0) return;\r
9954   piece = board[fromY][fromX];\r
9955   if (piece >= EmptySquare) return;\r
9956 \r
9957   if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
9958 \r
9959 again:\r
9960 \r
9961   ScreenSquare(fromX, fromY, &start);\r
9962   ScreenSquare(toX, toY, &finish);\r
9963 \r
9964   /* All moves except knight jumps move in straight line */\r
9965   if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {\r
9966     mid.x = start.x + (finish.x - start.x) / 2;\r
9967     mid.y = start.y + (finish.y - start.y) / 2;\r
9968   } else {\r
9969     /* Knight: make straight movement then diagonal */\r
9970     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9971        mid.x = start.x + (finish.x - start.x) / 2;\r
9972        mid.y = start.y;\r
9973      } else {\r
9974        mid.x = start.x;\r
9975        mid.y = start.y + (finish.y - start.y) / 2;\r
9976      }\r
9977   }\r
9978   \r
9979   /* Don't use as many frames for very short moves */\r
9980   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9981     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9982   else\r
9983     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9984 \r
9985   animInfo.from.x = fromX;\r
9986   animInfo.from.y = fromY;\r
9987   animInfo.to.x = toX;\r
9988   animInfo.to.y = toY;\r
9989   animInfo.lastpos = start;\r
9990   animInfo.piece = piece;\r
9991   for (n = 0; n < nFrames; n++) {\r
9992     animInfo.pos = frames[n];\r
9993     DrawPosition(FALSE, NULL);\r
9994     animInfo.lastpos = animInfo.pos;\r
9995     Sleep(appData.animSpeed);\r
9996   }\r
9997   animInfo.pos = finish;\r
9998   DrawPosition(FALSE, NULL);\r
9999 \r
10000   if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
10001 \r
10002   animInfo.piece = EmptySquare;\r
10003   Explode(board, fromX, fromY, toX, toY);\r
10004 }\r
10005 \r
10006 /*      Convert board position to corner of screen rect and color       */\r
10007 \r
10008 static void\r
10009 ScreenSquare(column, row, pt)\r
10010      int column; int row; POINT * pt;\r
10011 {\r
10012   if (flipView) {\r
10013     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;\r
10014     pt->y = lineGap + row * (squareSize + lineGap) + border;\r
10015   } else {\r
10016     pt->x = lineGap + column * (squareSize + lineGap) + border;\r
10017     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;\r
10018   }\r
10019 }\r
10020 \r
10021 /*      Generate a series of frame coords from start->mid->finish.\r
10022         The movement rate doubles until the half way point is\r
10023         reached, then halves back down to the final destination,\r
10024         which gives a nice slow in/out effect. The algorithmn\r
10025         may seem to generate too many intermediates for short\r
10026         moves, but remember that the purpose is to attract the\r
10027         viewers attention to the piece about to be moved and\r
10028         then to where it ends up. Too few frames would be less\r
10029         noticeable.                                             */\r
10030 \r
10031 static void\r
10032 Tween(start, mid, finish, factor, frames, nFrames)\r
10033      POINT * start; POINT * mid;\r
10034      POINT * finish; int factor;\r
10035      POINT frames[]; int * nFrames;\r
10036 {\r
10037   int n, fraction = 1, count = 0;\r
10038 \r
10039   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10040   for (n = 0; n < factor; n++)\r
10041     fraction *= 2;\r
10042   for (n = 0; n < factor; n++) {\r
10043     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10044     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10045     count ++;\r
10046     fraction = fraction / 2;\r
10047   }\r
10048   \r
10049   /* Midpoint */\r
10050   frames[count] = *mid;\r
10051   count ++;\r
10052   \r
10053   /* Slow out, stepping 1/2, then 1/4, ... */\r
10054   fraction = 2;\r
10055   for (n = 0; n < factor; n++) {\r
10056     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10057     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10058     count ++;\r
10059     fraction = fraction * 2;\r
10060   }\r
10061   *nFrames = count;\r
10062 }\r
10063 \r
10064 void\r
10065 SettingsPopUp(ChessProgramState *cps)\r
10066 {     // [HGM] wrapper needed because handles must not be passed through back-end\r
10067       EngineOptionsPopup(savedHwnd, cps);\r
10068 }\r
10069 \r
10070 int flock(int fid, int code)\r
10071 {\r
10072     HANDLE hFile = (HANDLE) _get_osfhandle(fid);\r
10073     OVERLAPPED ov;\r
10074     ov.hEvent = NULL;\r
10075     ov.Offset = 0;\r
10076     ov.OffsetHigh = 0;\r
10077     switch(code) {\r
10078       case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_SH\r
10079       case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break;   // LOCK_EX\r
10080       case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN\r
10081       default: return -1;\r
10082     }\r
10083     return 0;\r
10084 }\r
10085 \r
10086 char *\r
10087 Col2Text (int n)\r
10088 {\r
10089     static int i=0;\r
10090     static char col[8][20];\r
10091     COLORREF color = *(COLORREF *) colorVariable[n];\r
10092     i = i+1 & 7;\r
10093     snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
10094     return col[i];\r
10095 }\r
10096 \r
10097 void\r
10098 ActivateTheme (int new)\r
10099 {   // Redo initialization of features depending on options that can occur in themes\r
10100    InitTextures();\r
10101    if(new) InitDrawingColors();\r
10102    fontBitmapSquareSize = 0; // request creation of new font pieces\r
10103    InitDrawingSizes(boardSize, 0);\r
10104    InvalidateRect(hwndMain, NULL, TRUE);\r
10105 }\r